# Changelog

All notable changes to **imPRESS Studio** are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
and the project adheres to [Semantic Versioning](https://semver.org/).
Installers for released versions are published on the project's GitHub Releases page.

## [Unreleased]

Development is currently paused. The planned items below are retained as ideas for a possible future return.

### Planned
- Hot folders feeding the Workflow server: a file dropped into a hot folder becomes a Workflow job, with its history, notifications and place on the production board.
- JDF/JMF integration (job-ticket parsing for hot folders).
- macOS and Linux builds (Avalonia is cross-platform).
- OpenGL preview engine for faster large-sheet rendering.
- Additional UI languages (German, Czech), more gang-run presets, per-hot-folder multi-template routing by filename pattern.

## [1.7.3] - 2026-10-12

### Changed
- imPRESS Studio is free with all features and no activation, licence expiry, export or workstation restrictions. The shared licensing gate permits desktop exports, CLI, hot folders, Workflow and Windows services when licensing is disabled.
- Added installation-wide `config/licensing.ini`: `license = no` ships with this release; `license = yes` restores the existing signed-licence checks and online activation/renewals after restarting the application and services. Missing, unreadable or invalid configuration defaults to the free mode in this release.
- Preserved the licensing implementation, public key, signature/machine/expiry validation and existing user licences. Free mode does not modify licence files, request a trial or contact the licence refresh API.
- The desktop badge and licence dialog describe the unrestricted free edition in Polish and English without an expiry countdown or activation/renewal controls.
- Updated www2 PL/EN landing pages, help, FAQ, release history and metadata for the free 1.7.3 release, including summaries of 1.7.1 and 1.7.2. Removed paid offers and public currency conversion while retaining the licence administration backend for future use.

### Verified
- Optional anonymous telemetry remains enabled according to the user's existing preference and reports the free edition. Delivery stays in the background; HTTP errors, connection/DNS failures and timeouts do not propagate to application operations. Shutdown is bounded and delivery recovers when the server returns.

## [1.7.2] - 2026-10-10

### Changed
- Refreshed the desktop light and dark themes with a slate palette, consistent blue accents, clearer typography, softer cards and more readable controls. Main-window step navigation, sidebar headers, inspector tabs, floating tools, template editing and Workflow share the same visual language.
- Toned down the light theme with soft grey surfaces and a muted blue accent to reduce glare during long working sessions.
- Start-screen instructions and the PDF drop area occupy separate columns at the minimum window size; template previews use a compact card with their description immediately below it.
- Preserved existing commands, shortcuts, input ranges and workflows; sidebar sections still support mouse and keyboard expansion.

### Added
- Paper grain settings with explicit sheet/product axes, automatic spine/fold requirements, constrained rotation, preflight checks and a preview indicator. Grid optimization and sheet advice respect enforced grain.
- Press profiles with independent edge margins, gripper exclusions, imageable-area limits and allowed stock sizes; reusable JSON/user library, actual front/back and bleed validation, and rejection of incompatible production alternatives.
- CSV personalization with mapped text, images, UTF-8 QR and Code 128 fields on either product side, exact record quantities, registered duplex tails and rotated previews/PDF output. Supports quoted UTF-8 CSV and relative image paths; data is snapshotted per planned job.
- Custom signature folding/cutting schemes, derived pagination, an interactive paper dummy with layer stages, scheme JSON exchange and corresponding fold marks. Half-block operations support either folding direction, cuts and ordered gathering for saddle/perfect binding.
- DXF/CF2 die exchange with separate cut, crease and perforation geometry, arc tessellation, CAD previews, nesting outline transfer, product/current-sheet CAD export and optional overprinting manufacturing spot separations in PDF. Supported exchange subsets and limits are documented in `docs/advanced-production-1.7.2.md`.
- Whole-run production cost comparison across approved sheet formats, orientations and grid alternatives, including exact product quantities, setup forms and waste, printing sides and guillotine stack operations. The desktop shows a ranked cost breakdown and runs comparisons in the background; Workflow uses the job quantity.
- Guillotine-compatible rectangular nesting with vertical, horizontal and automatic packing. Automatic production comparison evaluates both packing directions by cost. Final layouts are checked for full-block separability; optional PDF cutting instructions list each cut, block dimensions and finished product blocks.
- Cut & Stack imposition for simplex and paired duplex products, with an optional maximum number of sheets per cut stack.
- C-fold leaflets with a narrower tuck panel, separate panel/flat-product dimensions, registered verso and mirrored fold marks.
- 16- and 32-page folded forms for saddle and perfect binding, derived from physical fold sequences; horizontal and vertical fold marks appear in preview and PDF output.
- Work-and-tumble: a single form containing rectos and rotated versos, for a second press pass after tumbling the sheet top-to-bottom. Supports repeat, consecutive and Cut & Stack page modes.
- Mixed-size gang runs with per-product dimensions, quantities, optional verso and quarter-turn rotation. An empty item list automatically imports dimensions from source PDF TrimBoxes.
- Shape nesting using actual simple closed cutting contours, polygon collision and spacing checks, and clipping of artwork plus bleed. Contours can be entered manually or imported from a named Separation color (default CutContour), including curves and transformed Form XObjects.
- Desktop controls, source import, PL/EN mode names and seven metric starting presets and seven imperial counterparts for the new imposition modes. New options survive template save/load and work in the shared desktop/CLI/Workflow planning and export engine.

### Fixed
- The inspector's four tabs fit without clipping the Validation label after loading a PDF. Compact spacing preserves the font size, and the tab strip can wrap when needed.
- Expected switches to approximate ICC preview are logged as a short informational message without an exception stack. Cancellation and unreadable PDFs remain distinguishable from this normal fallback.
- Ordinary PDF export and Save PDF now apply a selected CMYK ICC profile, convert supported RGB artwork and embed the profile; existing CMYK values are preserved. Preview converts source CMYK through the printing profile before rasterisation, avoiding PDFium's generic CMYK conversion and a second bitmap proof. Unsupported preview constructs are labelled approximate. Native and Ghostscript PDF/X share the same default-profile resolver and never silently substitute a missing selected ICC.
- RGB images with embedded source ICC / CalRGB keep their calibration when mixed with untagged images. Indexed DeviceRGB images are now detected for ICC conversion. PDF/X-1a routes calibrated source colours to Ghostscript instead of discarding their profiles or claiming false native conformance.
- Main-window sheet counters display the planned quantity from job statistics, including production runs backed by an array of layouts.
- Windows output targets in the desktop application have an explicit dropdown for all installed printers, including when a printer is already selected; typing filters names without case sensitivity.
- Changing the output path after planning is respected by export without discarding manual slot edits or source products.
- Preview fits cropped PDF bitmaps against the visible CropBox/MediaBox intersection, including off-centre TrimBoxes and rotated source pages.
- Ticket and page numbers stay inside their own nesting contour in preview and PDF output. Numbers anchored outside a shape are moved inside; export reports when a complete number cannot fit.
- Dimension inputs convert their minimum, maximum, step and precision with the display units, including nesting item rows and the placement inspector. Live unit changes preserve millimetre values.
- Leaflet preflight checks source panel dimensions in panel and flat-product modes, accepting narrowed C-fold and gatefold flaps on the appropriate pages.
- Nesting preview uses cutting outlines for selection, tooltips, eyedropper sampling and file drops, including overlapping bounding rectangles, cached scenes, spread views and view rotation.
- Automatic nesting exports read only the selected source products, so excluded pages with missing contours or incompatible verso dimensions do not block export. Original front/back pairs are still required, and temporary subsets are cleaned up on extraction failure.
- PDF placement and preview align non-centred source TrimBoxes with product slots.
- Shape utilization and cutting guides use actual outlines. Source-page export selection remaps nesting products and requires complete front/back pairs.

## [1.7.1] - 2026-10-04

### Changed
- Removed the decorative brand block from the main toolbar; the File menu now starts at the left edge.
- The expanded Workflow drawer covers both the template/source-file step rail and the editor.
- Reorganised Workflow filters into two aligned rows, with consistent field heights and a segmented Table / Board switch.
- Redesigned the Workflow footer with separate server controls and printer status chips, connection/queue counters and horizontal scrolling for long printer lists.
- The main window opens before online licensing; startup timing stops at its Opened event and excludes the activation dialog and network waits.

### Added
- Anonymous telemetry v2: session IDs, idempotent delivery batches, periodic activity heartbeats, session duration, startup phase timings and Workflow operation counters/durations/failures, including console and Windows service modes. Opt-out and the fixed diagnostic whitelist are preserved.
- Hardware diagnostics: bounded CPU/GPU models, core/thread counts, installed RAM and physical disk types; collected once per process in the background when diagnostics are enabled. The installation table and CSV show the readings; serial numbers and computer names are excluded.
- The website server now owns its backend package and requirements under www2/server. It starts without the legacy sibling server directory and preserves the configured database and signing key.
- PL/EN administration panels can extend existing trials or issue full licenses assigned to them. The desktop and background services fetch validated, signed updates automatically; existing offline activation remains available.
- Administration diagnostics show startup median/P95, startup by version, operation timings/failures, daily activity and execution modes. Historical startup measurements are excluded from the new startup statistics.

## [1.7.0] - 2026-09-30

**Print Workflow**: imPRESS Studio becomes the central job system of a print shop. An HTTP server (ASP.NET Core / Kestrel) runs inside the desktop application itself — nothing separate to install — takes jobs and PDFs from a web portal and a REST API, and moves them through preflight, repair, imposition, export and delivery in the background, with the same engines as the desktop export. The shop runs its work from the application's Workflow panel or from any browser on the network. **The server is off by default**: until it is switched on, nothing is created on disk and the application behaves exactly as 1.6.5.

### Added

#### Jobs and workflows
- **Jobs with a lifecycle**: New → Uploading → Preflight → Waiting → Processing → Ready → Completed, with Hold, Error and Rejected. A job carries customer, quantity, pages, page size, paper and grammage, duplex, template, finishing, priority, due date, notes, preflight verdict and approval. Every move is checked against one transition table, applied atomically and recorded with who made it and why. Completed is terminal — a reprint is a new job.
- **A job folder per job**: `Jobs/2026/0001842/` with `original/`, `working/`, `output/`, `reports/`, `thumbnails/` and a `job.json` snapshot. The location is configurable.
- **Workflows**: named production recipes (template, preflight profile, repairs, PDF/X target, engine, ICC profile, cutting guide, auto-preflight, output target, notifications) with rules that pick the workflow for a new job.
- **Workflow modes**: Manual (preflight, then the operator decides), Assisted (preflight, a suggested rule, operator approval, production) and Automatic (preflight, rule matching, production, Ready — with no matching rule the job waits for the operator).
- **Approval**: production can require an explicit approval; producing is refused until then. New artwork or a new preflight clears it. Uploads either start preflight at once or return the job to New for on-demand preflight, per workflow.
- **Processing rules**: conditions (page width/height/size with a 1 mm tolerance, paper format, pages, pages modulo 4, quantity, paper, paper grammage, finishing, duplex, customer, job name, priority, preflight result) and actions (preset, sheet, imposition mode, crop marks, PDF/X export, creep, booklet, repairs, output), with priority and an enable switch. Rules only adjust the inputs of the existing imposition and export engines.
- **The pipeline**: every workflow is a pipeline of ten steps — Input → Preflight → Condition → Repair → Approval → Imposition → Export → Output → Notification → Completion. Condition, Repair and Output can be switched off; the last two are opt-in:
  - **Notification**: a JSON webhook (status, number, name, customer, quantity) when a job becomes Ready, On hold, Error or any chosen status — for an order system, Slack/Teams or a script. One retry; the outcome is logged in the job's history.
  - **Completion**: a job whose files were delivered to a target moves straight to Completed.
- **Background processing**: a queue and 1–8 workers on the thread pool, never on the UI thread. Preflight uses the workflow's profile and template geometry, files the report, reads page count and size from the artwork, applies the workflow's repairs to a copy and renders PNG thumbnails; production imposes the newest repaired copy and exports it. Live progress per job, transient I/O failures retried, anything else ends in Error with the reason. A job is never processed by two workers at once, and work interrupted by a shutdown is recovered on the next start.

#### Output and printers
- **Output connectors**: finished files are delivered to a folder — a RIP's or another hot folder's input — written under a temporary name and renamed when complete, never overwriting anything. Targets are configured in Settings → Workflow Server → Output targets, with `workflow output add`, or by an administrator in the portal's Administration page; everyone else on the web only ever sees their names and printer states.
- **Printers**: a printer is an output target like a folder, so workflows, rules and the Output step print without any other change. Three ways to reach one:
  - **IPP / IPPS** (`ipp://printer/ipp/print`) — the PDF is sent as is, with copies, duplex and optionally the media as job attributes; the printer reports its state, its model and whether it accepts PDF.
  - **Raw port 9100** — the data streamed to the printer, wrapped in PJL for copies and duplex (can be switched off); state from `@PJL INFO STATUS` when the printer answers it.
  - **Windows printer** — any installed or shared queue, chosen from a list; the data is spooled as RAW, status from the spooler.

  **Printers without a PDF interpreter get PostScript**: an office MFP that only knows PostScript and PCL can reset when it is sent a PDF. The data format is *automatic* by default: before sending, imPRESS asks the printer what it understands — IPP on the same host (read-only), else PJL `INFO CONFIG` — and sends PDF when it can, otherwise PostScript Level 3 with every page rendered by PDFium (the preview's renderer; no Ghostscript), in greyscale for a mono printer, 300 dpi by default. A printer that takes neither PDF nor PostScript is refused with a clear message instead of being sent data it would choke on; the last answer is remembered, so a printer whose web service stalls is not suddenly sent PDF. Per target the format can be forced (PDF / PostScript), with resolution and greyscale.
  **Duplex "automatic"** prints two-sided when the job asks for it *or* when the imposition produced fronts and backs (a sheetwise duplex template, such as the business-card presets). The turn follows the imposition (backs are mirrored left-to-right, i.e. head-to-head): long edge of the paper for a portrait sheet, short edge for a landscape one.
  Nothing is rendered by a Windows driver: a Windows queue cannot say what the device understands, so there "automatic" means PDF — choose PostScript for a printer without PDF. Copies (1–999) and duplex (as in the job, one-sided, long or short edge) are set per target, and **Test** shows the printer's state.
- **Send / print by hand**: the Workflow panel (after a confirmation that names the printer and the number of copies) and the portal send a job's newest finished PDF (the PDF/X one if made) to any enabled target — "print this on the Konica now", or a reprint. The job's status does not change; the delivery is logged in its history with the operator's name.

#### Web portal
- **Web portal** at `http://server:port/` — no client to install, Polish and English, light and dark mode, phone layout. A sign-in page, a job list with search and status filters (with counts), job details with a step bar (received → preflight → approval → production → ready → completed), preflight results as a summary plus a checklist ("✓ Fonts embedded", "⚠ RGB images", "✗ Missing bleed"), page previews, output files to download and a history timeline. A New Job form in three sections with drag-and-drop upload, upload progress and an immediate "Job #… received · File uploaded · Preflight: processing" receipt. The portal is compiled into the application — one stylesheet and one script, nothing loaded from the Internet.
- **Live status in the browser**: status changes and progress arrive as they happen (Server-Sent Events); if a proxy blocks the stream the portal falls back to refreshing every 10 seconds.
- **Template, paper and grammage on the New Job form**:
  - **Template**: the print shop's saved templates (including ones created in the template manager) and the application's built-in presets, in two groups, plus "chosen by the print shop". Production uses the job's template unless a rule picks a preset; with no choice the workflow's template applies. The choice is validated against the catalogue (names only — a path is refused), shown in the job details on the web and the desktop, and available over the API (`templateFileName` on jobs, `GET /api/templates`). Built-in presets are addressed by their stable id (`preset-a4-saddle.json`); a saved file of that name takes a preset over. Free-text finishing is still accepted over the API and shown when set.
  - **Paper type and grammage**: the print shop's papers plus stocks already added, and the grammage (60–400 g/m² or any other value). "Add your own type" creates a stock on the spot (name + kind of paper) in the shop's paper catalogue (`papers.json` next to the templates), which the application reads too — it appears in Settings → Workflow Server → Papers at once, marked "web portal" with who added it and when, and the template editor offers the same stocks for creep compensation. A stock may carry the grammages it comes in (one, several or none); the portal lists a paper's own grammages first and preselects the only one, and production and the template editor take a single grammage from the paper itself. For saddle-stitched work with creep compensation the job's grammage and paper kind set the paper caliper.
- **Production board** (`#/board`, "Production" in the menu for staff; a List | Board switch on All Jobs): the shop's work from any computer on the network, one column per stage — Received → Preflight → To approve → Production → Ready → Completed, plus **Needs attention** (on hold, failed, rejected). Headline numbers on top (in progress, to approve, in production, ready, needs attention, overdue, due today — each a filter), the stages as a production line with their counts (a click shows only that stage), search and filters by workflow and priority. Each card shows number, name, customer, quantity, paper, priority (urgent and high are marked), due date (overdue and today stand out), status, preflight verdict, workflow, the hold or error reason and a live progress bar while it is produced; columns are in working order — urgent first, then the nearest due date, then the oldest job.
  - **Actions on the card** are exactly the ones this person may use on this job right now — the server decides, with the same rules the services enforce — so an operator approves and produces, a production manager also releases, a DTP account never sees Approve.
  - **Bulk actions**: select jobs (or a whole column) and approve, produce, complete, retry, release, re-run preflight, hold or reject them at once, or set their priority; every job goes through the service on its own and the result lists any that failed, with the reason. Everything updates live.
- **Managing a job from the web**: staff see the job's way through its own workflow (Input, Preflight, Rules, Repairs, Approval, Imposition, Export, Output, Notify, Complete — done, running, on hold, failed, with details such as "1 error / 3 warnings" or the matched rule) and the actions the server allows: approve, produce, complete, retry, release, preflight again, hold, reject. Accounts that manage jobs change the **priority** in place and **edit the order** — name, customer, quantity, pages, priority, due date, template, workflow, paper, grammage, duplex, notes — without losing what the form does not show. A priority change is recorded in the job's history ("Priority changed", staff only). On a finished job **Send / print** sends it to any enabled target (with the printer's state next to the choice); on a waiting, held or failed job **Auto-repair** lists the repairs that would change something and the problems only the customer can fix, and runs the chosen ones. The shop's job list shows every printer's state in a strip above the table.
- **Administration**: an Administration page, so the shop can be configured from any computer on the network instead of from the machine with the application. Each tab appears only for accounts allowed to use it:
  - **Users** (administrators): create accounts (the generated password is shown once, with a copy button), edit name, e-mail, role and customer, disable or re-enable, reset a password, issue and revoke API keys (the key is shown once).
  - **Outputs & printers** (administrators): add, edit, disable, test and remove folder targets and printers (IPP/IPPS, raw port 9100, a Windows queue picked from the printers installed on the server), with copies, duplex, data format, raster resolution and greyscale; printers report their state as soon as the page opens, and a target still used by a workflow or rule cannot be removed.
  - **Papers** (production managers): add stocks, change their kind and grammages, delete them; built-in papers are read-only.
  - **Workflow** (production managers): the whole recipe — mode, template, output target, description, preflight profile, hold on errors, strict mode, automatic repairs, PDF/X standard, export engine, cutting guide, webhook address and the statuses that trigger it; workflows can be added and deleted. The pipeline is drawn as ten colour-coded boxes like the desktop editor — each saying what it will do with the settings below ("Preflight starts when the file arrives", "PDF/X4 · Automatic", the target's name…), with switches for the optional steps, side exits ("errors → Hold"), the number of this workflow's jobs at each box right now, and the validator's notes (✗ / ⚠ / ℹ on the box and a clickable list under it), re-checked as you edit, before saving. Clicking a box takes you to its settings.
  - **Rules** (production managers): the processing-rule forms.
  - **Server** (administrators): status tiles (state, how the server runs — application, Windows service or console — address, uptime, workers, queue, jobs in progress, connections) and jobs by status, plus the settings: address, port, LAN only, sign-in, session lifetime, workers, stage timeout, upload limit, retention and job folder. Changes that need a restart say so. Switching the server off, HTTPS and access from outside the LAN stay in the application — a browser could lock itself out.

#### In the application
- **Workflow panel**: the Workflow button at the right end of the toolbar (or Ctrl+Shift+W) rolls the job panel down over the editor and back up on a second click; opening a job in the editor rolls it up by itself. Fully in Polish and English.
  - **The queue**: a table of every job with search and filters (status, priority, user, customer, creation date, archive), coloured status labels, a mark on high and urgent jobs and overdue dates in red — or, with the **Table | Board** switch, a board with a column per stage and job cards (priority edge, due date, status, preflight verdict, hold or error reason) in working order.
  - **Production flow**: a strip above the queue with the seven stages and their counts; a click shows only that stage. An **overdue / due today** switch narrows the queue to urgent due dates. The stages follow the same rules as the portal's board, so both show the same.
  - **Job details**: the job's pipeline as a vertical list of steps with their state and details, parameters, the preflight checklist and full report, the timeline, the matched rule and, for a failed job, the failed stage, the reason, the time and a Retry button. A **priority** box (audited, reorders every board, the portal's too).
  - **Actions** grouped into Production (Open in imPRESS, Preflight, Auto-repair, Produce), Decision (Approve, Release, Hold, Reject, Complete, Archive) and Send / print. **Open in imPRESS** opens a working copy of the artwork in the editor; saving or exporting registers the result with the job. **Auto-repair** checks the job's file, lists the repairs that would change something and the problems no repair can fix (such as missing bleed), and runs the chosen ones on a working copy with a fresh preflight.
  - A status bar shows the server and every printer with a coloured dot (printers are checked every 30 seconds while the panel is open), with the server's Start, Stop and Restart buttons.
- **Settings → Workflow Server…** — one window, five tabs:
  - **Server**: enable, address, port, LAN only, job folder, upload limit, start with imPRESS Studio, sign-in, retention, workers and stage timeout — grouped into Server and access, Jobs and files, Processing and Security, with explanations under the fields — plus the Windows service.
  - **Users**: create accounts, edit name, e-mail, role and customer, enable or disable, reset the password, issue and revoke API keys. Passwords and keys are generated and shown once. The last active Administrator can be neither disabled nor demoted.
  - **Output targets**: folders (with a folder picker) and printers (IPP, raw 9100, a Windows queue from the installed printers), with the fields each kind needs and copies and duplex for printers; change, disable, **test** (a folder is written to, a printer reports its state, model and reasons), and see which workflows and rules use a target — one in use cannot be removed.
  - **Pipeline**: the visual editor of workflows. The ten steps are drawn as colour-coded cards joined by arrows, wrapping over rows, with side exits ("errors → Hold", "waits for the operator"); each describes what it will do with the current settings, optional steps switch on and off on the card, and clicking a card opens its settings (the workflow's mode lives on the Approval card). **Checks as you edit** flag what will not work as it looks (Output switched on with a disabled target, Automatic mode with no rules, no template, a Notification without an address…) on the card and in a clickable list of notes. **Live counts** show how many of the workflow's jobs sit at each step, and a card with jobs lists them, most urgent first. **Dry run**: pick a PDF and see what each step would do with it — preflight result, matched rule, repairs that would apply, the approval route, template and sheet, export, whether the output target is reachable — with the pipeline as it stands in the editor, unsaved edits included; no job is created, nothing is written or sent. Processing rules can be switched on and off from the Condition card. Saved through the same validation as the portal and the command line.
  - **Papers**: the paper catalogue shared with the portal — add and remove stocks and set the grammages each comes in.
- **Windows service**: the Workflow server can run as the Windows service "imPRESS Studio Workflow" — the portal, the API and background processing stay up with nobody logged in and come back after a reboot (automatic start, restart on failure). Install, start, stop, restart and remove it from Settings → Workflow Server → Server → Windows service (one UAC prompt per action) or from the command line. The service works on the same database, job folders, templates and paper catalogue as the application, even under another account: both data locations are written into the service's command line (`--data-dir`, `--local-dir`). While the service is installed the application does not start a server of its own (the port belongs to the service); its Workflow panel shows the service's state and its Start/Stop buttons drive the service, and work started on the desktop (preflight after an upload, production) is handed to the service through the database (picked up within about two seconds). Removing the service brings the application's own server back.

#### Integration
- **REST API** under `/api`: `auth/login`, `auth/logout`, `users/me`, `server/status`, `health`, an event stream (`events`), and for jobs: list, create, edit, `status`, `history`, `events`, `timeline`, `pipeline`, `upload` (raw PDF or multipart form), file download, `preflight`, `process`, `approve`, `hold`, `reject`, `release`, `complete`, `retry`, `priority`, `send`, `repairs`/`repair` and `bulk` (one command on many jobs). Everything the portal does goes through the same API, including the production board and administration; API keys (`imp_…`) serve integrations. Full list in `docs/workflow-1.7.0.md`.
- **Accounts and roles**: Administrator, ProductionManager, Operator, DTP and SubmitUser, each an authorization policy. A submitter sees only their own jobs and files work under their own customer name; someone else's job answers "not found".
- **Audit log**: JobCreated, FileUploaded, PreflightStarted, PreflightCompleted, ProcessingStarted, ProcessingCompleted, Approved, Rejected, Hold, Error and Completed, plus repairs, thumbnails, releases, deliveries, notifications and priority changes — each with time, user, job and details.
- **Retention**: Completed and Rejected jobs can be deleted automatically after a number of days (off by default).
- **Command line**: `workflow status | enable | disable | serve`, `workflow user add | password` (a generated password printed once, optionally an API key), `workflow output add | printer | list | test | printers | remove`, `workflow service install [--start] [--account DOMAIN\user --password …] [--manual] | uninstall | start | stop | restart | status`.

### Security
- Submitters never see internal detail: rule names, failure stages and technical error text, staff names and raw event logs stay with the print shop; a submitter sees a neutral message and a restricted timeline, and every staff-only endpoint (board, job pipeline, bulk actions, priorities, pipeline counters) answers 403. The portal runs under a strict Content Security Policy and never inserts markup built from data.
- Designed for a LAN: when enabled the server listens on all interfaces but, in the default LAN-only mode, closes connections from public addresses before any HTTP is exchanged. Serving beyond the LAN needs `--allow-public` together with HTTPS (certificate from the Windows store) or explicit consent to plain HTTP. Login may be switched off only on a 127.0.0.1 listener.
- Passwords are PBKDF2-SHA256 with 600 000 iterations; tokens carry 256 bits of randomness and only their SHA-256 is stored. Login is rate-limited per address and an account is locked for 15 minutes after five failures. Changing a password ends every session.
- Every endpoint declares the permission it needs and the services check it again. Output target settings (folders, printer addresses) are shown on the web only to accounts with the ManageServer permission (administrators); operators see names and printer states. A bulk request is refused as a whole (403) when the caller may not use its action at all, holds at most 200 jobs, and each job is then checked again by the service. The administration API goes through the same services, validation and audit as the application and the command line.
- Uploads: allowed types only, size limit enforced while streaming, `%PDF-` signature and a full parse by the existing analyser. The client's file name is display-only — files are stored under generated names, never overwrite anything, and every path is checked to stay inside its job folder. No static files, no directory listing, downloads always as attachments, no stack traces in responses. Production re-checks the licence for every job.

### Changed
- **Never stuck, never silent**: every failure moves a job to Error with its stage (upload, preflight, imposition, export, output), a short reason and the time; Retry resumes from that stage. A stage running longer than the configured limit (30 minutes by default) is stopped and reported, and an upload interrupted by a restart returns the job to New.
- One definition of the template file format (`TemplateSerializer`) and of the per-job preflight profile (`PreflightProfileFactory`) replaces identical copies in the CLI, hot folders, main window, template editor, preset materialiser and numbering migration. Files and verdicts are unchanged.
- The command-line `impose` verb runs through the new shared production service, the same path the workflow uses.
- PDFium rasterisation moved out of the preview renderer into a UI-independent `PdfRasterizer`, shared by the preview and the server's thumbnails. The preview renders exactly as before.

### Migration
- **Nothing changes for an existing installation until the Workflow server is switched on.** Templates, hot folders, the CLI and exports work as in 1.6.5. The workflow database (`workflow.db`, schema v6) and the job folders are created when the server is first enabled; a database written by a pre-release 1.7.0 build is migrated automatically.

### Developer notes
- The application references the ASP.NET Core shared framework. Installers are unaffected (self-contained), but running a Debug build or the tests framework-dependent needs the **ASP.NET Core 8 runtime**; otherwise run the tests with `dotnet test -r win-x64 -p:SelfContained=true`. The published size grows by about 25 MB.
- Browser end-to-end scenarios (Edge headless) run with `IMPRESS_BROWSER_E2E=1`; `IMPRESS_E2E_SHOTS` and `IMPRESS_UI_SHOTS` save portal and desktop screenshots.
- Architecture, data model, roles, lifecycle, API and security notes: `docs/workflow-1.7.0.md`.

## [1.6.5] - 2026-09-22

### Changed
- **Sheet numbering moved out of the export options and into the template**, under Templates → Extras, next to the logo overlay and ticket numbering. It is a property of the product, not of the person exporting it: the same job now carries the same numbering whether it is exported from the main window, a hot folder or the CLI, and a template can be handed to a colleague with its numbering intact. The export dialog's checkbox, its position dropdown and its format field are gone.
- **The number's format uses the same named tokens as the slug line** — `{sheet}`, `{sheets}`, `{side}`, `{filename}`, `{template}`, `{jobid}`, `{date[:format]}` — instead of the positional `{0}` / `{1}`. One convention for one concept, and the number can now carry the file name or the job id alongside the count.
- **The preview draws the number as permanent chrome rather than a badge that fades after two seconds.** It is part of the sheet's design now, so it is shown the way the slug line is, in the place the export will stamp it, on both sides. The template editor's sheet thumbnail draws it too, planned by the same helper the composer uses, so the thumbnail cannot promise a placement the export will not honour.

### Added
- **Page numbering — the source PDF's page number stamped inside each item**, under Templates → Extras. The folio of the finished product: on an imposed notebook or calendar every item carries the page it actually is, so a proof can be checked against the original document and a mis-ordered signature shows up on the sheet. Fronts and backs, blank slots skipped, format built from `{page}` and `{pages}`, with a start number for documents whose page 1 is not the product's page 1. The preview stamps it in the same place the export does.
- The Extras tab now holds three numberings, which are easy to confuse, so each says what it counts: **page numbering** repeats the source page of the item, **ticket numbering** runs a counter of its own across the products and ignores what is printed on them, and **sheet numbering** counts press sheets and shares one number between a sheet's front and back.
- **Start number and step for sheet numbering.** A reprint can continue an earlier run ("this batch starts at 240") and a countdown is a negative step. Overflow is clamped rather than wrapped, because a silently negative sheet number on press is worse than a stuck one.
- **Per-side selection**: number both sides, fronts only or backs only — front and back of one sheet still share a number, since the number identifies the sheet and not the side. Use `{side}` to tell them apart.
- **Mirror on back**, which places the number on the same physical corner of the sheet once the stack is turned, instead of the same corner of the page image.
- **Anchoring to the usable area** instead of the physical sheet edge. On a large sheet a 5 mm inset lands in the gripper and never prints; anchored to the usable area the number follows the sheet margin. A margin wide enough to collapse the usable area falls back to the whole sheet rather than placing the number off-sheet.
- **Nine anchor positions** (the same set the logo overlay and ticket numbering use) instead of six, separate X and Y offsets instead of one shared inset, and a registration-black option for offset forms.
- **A non-blocking advisory in the template editor** when the marks profile's slug line also prints `{sheet}`, which would stamp the number twice. It is flagged rather than rejected — a slug along an edge and a number in a corner is a legitimate layout — and it never stops a save.
- Help gained a "Template extras" section covering the logo overlay, ticket numbering and sheet numbering, and explaining where sheet numbering went.

### Fixed
- **Ticking a section's “enabled” box in the template editor now ungreys its fields immediately.** Contour-cutter marks, creep compensation and ticket numbering gated their inputs on a binding that read the template object directly; the template is a plain data object with no change notification, so the box was ticked, the value was stored and the fields stayed greyed out until the dialog was closed and reopened. All the optional sections now go through the same notifying wrapper the logo overlay already used, and a headless-UI test ticks every one of them through the real dialog.
- **Switching tabs in the template editor scrolls back to the top.** All four tabs share one scroll area and only swap which panel is visible, so the offset survived the switch: arriving from a long tab landed the operator in the middle of the next one, with its first card above the viewport. With Extras now holding four cards, that was enough to make a section look absent.
- **The sheet number landed in the wrong place at any zoom other than 100%.** The preview measured the sheet in screen pixels and handed it to the placement helper, which converts its millimetre insets to PDF points — at 1:1 the two coincide and it looked correct, everywhere else the number drifted, and with usable-area anchoring and a wide sheet margin it could be pushed far enough to read as missing. Placement now goes through one scaled entry point that cannot mix the two units, and the same mistake in the template editor's thumbnail is gone with it.
- **A spread's back pane no longer shows a sheet number when the plan carries no verso.** The composer writes a back page only when there are back placements; the preview stamped one regardless, promising a numbered side that never reaches the file.
- **The preview expands `{jobid}` instead of leaving it empty.** It was hardcoded blank, so the slug line, the tracking barcode and the sheet number all rendered shorter on screen than in the exported file — and a format built from nothing else rendered as nothing at all.

- **A reprint no longer reuses ticket numbers that are already out in the world.** Exporting a sheet range cut the plan down to the selection before composing, so everything counted across the run counted the reprint instead: the ticket sequence restarted at 1, and a reprint of sheets 3–5 out of 39 handed out the same numbers as the original press run. A sheet range is now an output selection, not a smaller job — the whole plan reaches the composer, unwritten sheets still advance the counter, and a reprinted sheet comes out identical to its page in the full run.
- **The sheet total in the marks is the planned total again, not the size of the reprint.** The same truncation made the slug line, the sheet-number stamp and the tracking barcode read "sheet 3 of 3" on a three-sheet reprint of a thirty-nine-sheet job. The cutting guide, which describes the file rather than the run, still covers only the sheets actually written.
- **Blocking an export on an invalid licence says why.** The status bar showed the literal text `St.ExportBlockedNoLicense` — the key had no entry in either language, and the indexer returns the key it cannot find — while the actual reason was passed to a format string with nowhere to put it and silently dropped. The message now exists in both languages and carries the validator's explanation. Three new tests pin the whole surface: both dictionaries defining the same keys, every key the application asks for existing, and every formatted string having somewhere to put its argument.
- **Contour-cutter (OPOS) markers have their own preview toggle.** They were drawn behind the crop-marks filter, so unticking crop marks to inspect a sheet hid markers the exported file was still going to carry — the preview asserting the opposite of what shipped. They now sit beside the other per-type filters, greyed out when the marks profile defines no markers, and the crop toggle again governs crop marks alone.

- **The logo overlay is now visible in the preview and in the template thumbnail.** It had always reached the exported PDF, but nothing ever drew it on screen, so switching it on changed nothing anywhere in the application and the feature looked broken. Both previews now place it with the same helper the composer uses, so what is lined up on screen is what gets stamped. Raster logos are drawn; a PDF logo rasterises through the same background renderer as page content and shows its footprint until it lands. A missing or undecodable file draws the footprint outline instead of taking the editor down.
- **A template edit now discards the cached preview bitmap instead of re-blitting it.** The preview composes a whole sheet into one cached image; changing the template asked for a repaint but not for the cache to be dropped, so marks, overlays and numbering could keep showing the previous template's state until something unrelated forced a rebuild.
- **The contour-cutter box and the marks-profile dropdown follow a newly loaded template.** Applying a preset or loading a template from disk replaces the whole template at once; these two controls were not told, so they kept showing the previous template's state over the new one's data — the profile dropdown in particular named a profile the template no longer referenced.

### Migration
- **The old export-side setting is written into the saved templates on first start**, into every template that does not already define its own sheet numbering, and the status bar reports how many were touched. Before the move the checkbox applied to every export regardless of template, so leaving the setting behind would have silently dropped the numbers from work that has always carried them. Positional formats are converted to named tokens; a numeric specifier such as `{0:000}` becomes a plain `{sheet}`, as the named tokens are straight substitutions.
- **Hot folders keep working.** The per-folder `pageNumbering` flag is still read from `hotfolders.json` and fills in for a folder whose template defines no numbering, so an existing folder produces exactly the sheets it did before. The checkbox is gone from the folder editor — the folder's template decides now.
- **`--numbering` still exists in the CLI**, as a force-enable for a template that defines no numbering of its own. A template with its own sheet numbering is used as-is and the flag changes nothing about it.
- Sheet-range exports are unaffected: a reprint of sheets 3–5 still stamps 3, 4 and 5, because the numbers come from the plan rather than from the position in the written file.

## [1.6.4] - 2026-09-22

### Added
- File information now separates the source PDF from the current imposition. After planning it opens the layout summary, showing actual sheets/roll segments, dimensions, front/back sides, filled/blank slots and source-page coverage, including manual preview edits.
- The imposition view links to the last successful export of that plan, including its PDF/X output. This opens a separate analysis of the saved PDF's own page geometry, metadata, resources and preflight findings.
- Template suggestions now show validation diagnostics and disable application of invalid layouts. Signature/fold padding is shown explicitly; page and panel yield is distinguished from flat-item yield and roll segment output.
- Shared, non-mutating template validation for the editor and suggestion cards, including non-finite numbers, missing sections, unknown active modes, gatefold door width, perfect-bound spreads and roll segment length.
- Explicit preflight errors for malformed or empty page boxes, conflicting PDF/X metadata and damaged embedded OutputIntent profiles. ICC checks cover the signature, declared size, tag-table bounds and channel count; they do not certify the complete profile.
- An incomplete analysis now produces an error with the read diagnostics in the report. This cannot be hidden by a profile severity override. Independent analysis stages continue after another stage fails.
- Regression PDFs covering indirect objects, inherited page geometry, all four page rotations, font programs and CID descendants, nested form resources, compressed XMP, ICC corruption, soft masks, layer visibility, vector-only pages and cancellation.

### Fixed
- Ink coverage scanning no longer sends RGB export profiles (such as eciRGB_v2) to the CMYK bitmap transform, which failed with WCS error 87. The scan preserves selected CMYK profiles, discloses a scan-only CMYK fallback for incompatible or missing selections, and names the actual profile with the results. Export settings are unchanged. The transform rejects non-CMYK profiles before calling WCS; scan help and progress text now describe the native scanner and its estimation limits.
- File information follows the document behind the preview, including edited and merged sources, rather than an unrelated selected list entry. Source preflight verdicts are kept separate from planned-output statistics. Output preflight retains press policy without applying individual-item trim, bleed and safe-zone requirements to the whole press sheet.
- Context suggestions are ranked across the full candidate pool before applying the result limit. Applicable layouts precede invalid candidates; grades and explanations reflect the adapted layout and page-count padding.
- Suggested grids fit the source's actual orientation without being truncated by a rotated optimizer result. Folding and binding yields follow their production strategies; larger-sheet recommendations no longer promise unsupported N-up gains for folded or bound work.
- Explicit PDF TrimBox dimensions are preserved without inferred bleed subtraction. Large-format dimensional scores use the same scaled tolerance as candidate selection, and invalid numeric input cannot poison ranking.
- Cancelling the suggestion dialog after selecting a card no longer applies the selection. Applying a suggestion revalidates the layout; context adaptation retains the selected marks profile.
- Page-box parsing now resolves indirect arrays and coordinates, inherited MediaBox/CropBox and non-zero page origins. TrimBox, BleedBox and ArtBox retain their specified CropBox defaults without inheriting unrelated parent values. Basic analysis, preview/export trim reading and deep preflight use the same parser.
- Font embedding is determined from FontDescriptor/FontFile streams, including CID descendants and Type 3 CharProcs. A subset-looking name no longer proves embedding; fully embedded fonts are recognized, and identically named fonts with different embedding states remain separate findings.
- Font, spot-colour and transparency inventories now follow inherited resources and nested forms/tiling patterns. Case-sensitive ink names are preserved; registration colour detection remains available.
- OutputIntent arrays and dictionaries, embedded ICC streams, document Info and compressed XMP are dereferenced correctly. XMP identifiers are read by XML namespace, including attribute notation and arbitrary prefixes.
- Vector-only pages are no longer reported as blank. Painted paths contribute to content/bleed bounds; rotated images and text use all four corners for their extent. Image DPI continues to use the transformed sample-axis lengths.
- Graphics-state soft masks are detected. Stencil masks and the Compatible blend mode no longer cause false live-transparency findings. Layer visibility respects BaseState and ON/OFF overrides.
- Saved reports preserve different fonts, colourants and details sharing an issue code, while merging identical findings and their page lists.

### Changed
- Deep-analysis cache is bounded to 32 files, replaces obsolete entries and retries incomplete scans. Cancellation is checked on cache hits and between pages in the main walkers.
- Non-default UserUnit is explicitly reported as requiring physical-size/DPI verification. PDF/X and PDF/A metadata remain declarations, not a complete standards certificate. Scope and remaining limitations are documented in `docs/preflight-1.6.4.md`.
- Application version is **1.6.4**, file version **1.6.4.0**. Existing correction actions and export workflows are retained.

## [1.6.3] - 2026-09-09

### Added
- **The preview shows the job in the colours the press can actually reproduce.** When an ICC output profile is selected in the export settings, pressing Plan now renders the sheet through that profile — the same one export embeds as the output intent — instead of in screen RGB.

  It is a real ICC transform through the Windows colour system, not an approximation: sRGB into the chosen CMYK space and back out to sRGB, relative colorimetric on both legs. What that leaves on screen is exactly the difference the press will introduce. Saturated greens and blues that no ink set can hold collapse toward what it can; everything already inside the gamut is left where it was. Until now the operator laid out a sheet in bright RGB and first met that difference after exporting, or when the proof came back.

  The strip along the bottom of the preview gains a **PROOF** badge naming the profile in force, because an operator who cannot see why the greens went dull will read it as a rendering fault. Clearing the profile in the export options turns it off. The channel view and the overprint simulation now sit on top of the proofed colours rather than beside them — they are ways of looking at the sheet that will be printed.

  A profile the colour system cannot open leaves the preview working in plain screen colour rather than failing; the export path still reports it properly when it comes to use it. The proof is Windows-only, like the rest of the colour path, and where it is unavailable the preview behaves as it did before.

### Fixed
- **Installing over a machine running the hot folder service failed with "DeleteFile; code 5. Access denied".** The service runs the installed executable, so while it is running it holds every DLL in the program directory open, and Setup stopped at the first one it tried to replace — naming `Avalonia.Base.dll` and saying nothing about why.

  `CloseApplications` did not cover it. The Restart Manager closes applications; a service running as LocalSystem is neither closed by it nor stoppable by a Setup that runs, correctly, without elevation. So the installer never learned about the service the application had been able to install since 1.6.0, and every update on a shop that uses service mode would have hit this — including the silent in-app update, where it would have failed with no visible reason at all.

  Setup now stops the service before copying files and starts it again afterwards, through elevated `sc.exe` calls, which is where the UAC prompts during an update come from; they appear only on machines that actually run the service. If the stop does not go through, the installation aborts with a sentence saying what to do instead of a file name and an error number.

- **Uninstalling left the hot folder service registered**, pointing at an executable that no longer existed — it failed at every boot and could only be removed with `sc.exe`. The uninstaller now stops and removes it.

### Changed
- **The application is precompiled to native code at publish time, and starts about twice as fast.** Prompted by reports of slow startup on older machines, and by telemetry showing 45 seconds on one of them against under two on a current desktop.

  The cause was that everything which is not Microsoft's own framework shipped as IL: our own assembly, the whole of Avalonia and the PDF stack — 166 files and 28 MB — recompiled by the JIT on every single cold start. The framework assemblies were already precompiled, because that is how Microsoft ships them, which is exactly why the cost was close to invisible in profiling here and brutal on an old machine: JIT work is CPU-bound and scales directly with single-thread performance. Running with `DOTNET_TieredCompilation=0` put a number on it — pure compilation accounted for roughly 800 ms of a 950 ms start on a modern desktop.

  Measured on the same machine, comparing published builds of identical code:

  | | first run (cold) | subsequent (median) |
  |---|---|---|
  | before | 2730 ms | 952 ms |
  | after | 1421 ms | 570 ms |

  Machines that are slower per core stand to gain proportionally more, because the part that disappears is precisely the part that scaled with them.

  The cost is size: the installer grows from 41.5 MB to 50.7 MB, and a release build takes about twenty seconds longer. Composite mode was measured too and rejected — it is faster once warm (488 ms) but takes 5406 ms on a cold start, because one large image has to be paged in before anything runs, and a cold start on a slow disk is the case this was meant to fix.

  Nothing about the application changed; this is how it is compiled. `dotnet build` and the test run are untouched, and the setting is conditioned on a runtime identifier so a plain `dotnet publish` still works.

## [1.6.2] - 2026-09-08

### Added
- **Help → "Report a problem": one click produces the whole diagnostic package.** Every support conversation used to open the same way — asking a print shop, mid-production, to find `%LocalAppData%`, work out which of fourteen log files matters, and quote a version number they have no reason to know. What usually came back was a screenshot. Everything needed to diagnose a fault is already on the customer's machine; this collects it.

  The package is a single zip: the application logs (newest first, up to a budget that keeps the attachment inside every mail server's limit), the settings file, the hot folder configuration, saved templates and marks profiles, and a generated `diagnostics.txt` naming the version, the operating system, the runtime, the licence edition and state, the machine fingerprint and where everything is installed. There is also a box for the one thing a package cannot reconstruct: what the operator was trying to do. Their words go in verbatim — a translated bug report is a rewritten bug report.

  **The licence file is never included.** It is a signed bearer credential, and mail is copied, forwarded and archived by systems nobody in the conversation controls. The summary names the edition and the fingerprint hash instead, which is what a licensing question actually needs.

  The dialog says what will be collected before anything is written, including that logs carry the names and paths of the files the shop imposed — which are often their own customers' names. Nothing leaves the machine on its own: the package is written to the desktop, put on the clipboard as a file, and the mail is opened with the address and subject filled in, so attaching it is Ctrl+V. A `mailto:` link cannot carry an attachment, and uploading the package instead would mean sending a shop's job names to a server without them watching — a different promise from the one this dialog makes.

  Today's log — the one describing the fault the customer just hit — is the one the logger still holds open, and it is packaged along with the rest. A package that is nonetheless incomplete says so, with a count, rather than being discovered short a file by whoever opens it.

### Fixed
- **The application started in Polish whatever language was chosen in the installer.** Reported by a customer who installed the English build on an English Windows and got a Polish program. The stored language defaulted to Polish unconditionally, and nothing carried the setup wizard's choice across — the installer offered English and Polish, and then threw the answer away.

  What made it worse than a wrong default is that it was self-sealing: every menu that could have corrected it was also in Polish, including the one an English speaker needs, which is spelled "Ustawienia". Their first contact with the product was a screen they could not read and no obvious way out. The message ended "Is there an option that I am missing?" — there was, and finding it required already knowing Polish.

  The starting language is now decided in this order: an explicit choice made in Settings; a stored value of English, which the old default could never have produced and so can only be a decision somebody made; the language selected in the installer; the Windows UI language; and English. Polish is the answer when something says Polish, not the answer when nothing says anything.

  The fix reaches existing installations, not just new ones. Telemetry writes the settings file on first run, so anyone affected already has Polish recorded with nothing to say whether they chose it — and that ambiguous case is exactly the one that gets reconsidered against the installer and the system. A Polish install on a Polish machine resolves to Polish either way, so the installations this can reach are the ones that were wrong.

### Changed
- The installer records the language chosen in its wizard (`impress-setup.ini` in the program directory, removed on uninstall). It is written declaratively rather than from an install script, so there is no setup code that can fail quietly and leave the marker missing.
- Settings now record whether the interface language was picked deliberately. Without that distinction a chosen Polish is indistinguishable from the Polish this field used to default to, and the two cannot be treated differently.

## [1.6.1] - 2026-09-07

### Fixed
- **The preflight report showed empty boxes where the findings should have been.** Every finding was there — counted in the banner, grouped under the right heading, laid out in its own card — and none of it could be read: the card and the text on it were painted the same grey. The dialog resolves its colours from the application palette, and that palette is declared inside `ResourceDictionary.ThemeDictionaries`, once per theme. Looked up from code without naming a theme variant, the search runs against `ThemeVariant.Default`, where none of those keys exist; the lookup reported failure and the dialog substituted grey for both the card background and the text drawn on top of it. Since the substitution was applied uniformly, the result was consistent, plausible-looking, and blank.

  The failure mode is worth stating plainly, because it is what made it expensive: nothing was missing, nothing threw, and nothing was logged. To the operator it looked like preflight had stopped producing results, which is the one thing the window exists to do — and it appeared at the moment of deciding whether to send a job to press.

  Three things changed. The lookup now names the theme variant, so the palette resolves. A key that genuinely does not resolve no longer produces a substitute colour: the property is left alone and the control keeps whatever the theme gave it, because a fallback that can match the surface it is drawn on is worse than no fallback at all. And the lookup itself was hoisted into one place — this exact mistake had already been found and fixed once, in the template suggestion dialog, where it made the suggestion cards invisible, and it came back because the fix lived only in that file.

### Changed
- **Switching between the light and dark theme now takes effect immediately.** Choosing a theme in Settings used to persist the choice and nothing else — the window kept the theme the application had started in, and a hint under the picker said so. The reason was the sheet preview: it read its palette once at type initialisation, so switching live would have left the canvas around the paper in the old theme while everything else changed, which reads as a rendering fault rather than a setting. The canvas now takes its two theme-dependent colours at draw time and drops its cached scene bitmap when the variant changes, so there is nothing left to wait for. The Tools menu toggle and the Settings picker are the same switch.
- Preflight findings, count chips and group headings are drawn through the shared palette resolver, so all three follow the selected theme instead of one window staying its own shade.

## [1.6.0] - 2026-09-05

Preflight grows the checks a prepress department expects to find and misses when they are absent, gains two repairs for the failures that stop a file at someone else's gate, and both preflight windows are rebuilt around the decision they exist to support. Along the way, two things an operator simply could not reach: the report writer that shipped in 1.5.12 with nothing calling it, and a template manager that never showed the template the job was running.

### Added
- **Ten checks, every one of them reading something the analyzer had been extracting all along.** Page rotation, encryption, PDF version against the target standard, the PDF/X title and XMP requirements, line-art resolution and the separation rules were all measurable from data already in the report — nothing was looking at it. That is a particular kind of gap: no failure, no log line, just a rule that was never written.
  - **Page rotation** (`PAGE_ROTATION_SET`, `PAGE_ROTATION_MIXED`). Rotation in PDF is a viewing instruction, not geometry: the boxes keep the numbers they were written with and the reader turns the result. Consumers all honour it and disagree about what it means for the boxes, so a 90° page whose TrimBox reads 210×297 imposes as a portrait item and prints on its side — and the operator has no reason to suspect it, because the screen shows the rotated page. Mixed rotations are reported separately and as an error: one rotation throughout can at least be compensated, a document where some pages turn and others do not cannot be imposed correctly under any single reading. The angle is normalised first, because `/Rotate` is defined modulo 360 and a file may legally say 450 where it means 90.
  - **Encryption and empty documents** (`PDF_ENCRYPTED`, `PDF_NO_PAGES`). A file carrying permission flags opens perfectly in a viewer and then refuses to be placed, imposed or ripped. Reporting it at preflight turns a late, cryptic export failure into a fact the operator can act on while the customer is still reachable. An empty document stops the rest of the checks rather than producing a page of findings derived from nothing.
  - **PDF version against the target** (`PDF_VERSION_ABOVE_TARGET`). Each PDF/X flavour is defined on a specific version — X-1a and X-3 on 1.4, X-4 on 1.6 — and a file above it carries constructs the standard cannot describe.
  - **The two PDF/X metadata requirements** (`DOC_TITLE_MISSING`, `XMP_MISSING`). The cheapest possible conformance failure: nothing about the artwork is wrong, the file simply does not say what it is, and a validator downstream rejects it while the pages look perfect. Reported only when a PDF/X target is actually selected — outside that it is tidiness, and preflight that reports tidiness as loudly as defects teaches operators to stop reading it.
  - **Line-art resolution** (`IMAGE_LINEART_DPI_LOW`), with a threshold of its own. A 1-bit image has two values and nothing in between, so every edge is built from whole pixels: 300 dpi hides its sampling in a photograph and puts visible stair-stepping on every diagonal of a scanned signature or a die-line. The industry figure is around four times the contone one, which is why measuring both against a single threshold reports the wrong thing twice. `ImageDpiValidator` now skips 1-bit images entirely, so one low-resolution bitmap produces one finding with the right number in it rather than two that disagree.
  - **The `All` colorant** (`SPOT_ALL_COLORANT`). A Separation named `All` is defined by PDF 1.7 §8.6.6.4 to paint on every plate at once. It exists for registration marks and arrives in customer artwork by accident — a mark copied off a template, a swatch picked from the wrong end of the panel. Nothing about it looks unusual on screen, and on press it inks the object into all four process plates plus every spot. Matched exactly: a spot genuinely called "Allura Red" is a colour, and an error on a correct file costs more trust than the check earns.
  - **Plate count against the press** (`SPOT_COUNT_OVER_LIMIT`). How many spot colours a job may carry is a property of the machine, not of the file: nine are perfectly legal PDF and simply unrunnable on a six-unit press. Counted on distinct colorants, because the same ink named on three pages is still one unit, and the `All` colorant is excluded — it is a defect to remove, not an ink to plan for.
- **Two new repairs.** Both change document-level declarations rather than marks on the page, which is what makes them safe to offer and worth offering: a file can be perfect artwork and still be rejected because of what it fails to say.
  - **Document metadata** writes the title (from the file name) and the `/Trapped` declaration ISO 15930 requires — `/Unknown` and its absence are both non-conformant. **The trapped key is asserted, not guessed**: `/False` is a statement a RIP acts on, so the repair declines the moment the report shows a trap network in the file. Telling a trapped file it is untrapped is how work gets trapped twice, and double-trapping is a press problem rather than a validation one.
  - **Layer print state** stops layers hidden on screen from printing. Optional content carries two independent states — visible, and printing — and design files routinely end up with them disagreeing, because hiding a layer does not touch a print setting that defaults to on. The operator sees clean artwork and the plate comes back with the die-line on it. Nothing is deleted: the content stays and the layer can be turned back on, which is what separates this from flattening. Visible layers are never touched — a layer you can see is a layer you have decided about.
- **Hot folders accept CIP4 job tickets — JDF 1.x and XJDF 2.x.** A shop's MIS already describes every job it schedules: finished size, press sheet, binding, sidedness, and which file to print. Until now that description stopped at the hot folder and an operator retyped it into a template, or accepted whatever the folder's fixed template happened to say. A folder with tickets enabled reads the ticket instead, and imposes the PDF the ticket names.
  - **The ticket describes the work; it is not the work.** Dropping a `.jdf` in does not impose the ticket — it resolves the artwork the ticket points at, imposes that, and files the two together afterwards. Artwork first, ticket last is the convention every MIS follows, and the folder waits for the artwork anyway when they arrive the other way round.
  - **The folder's template is the floor, the ticket is the override.** A hot folder carries a shop's settled decisions about marks, bleed, gutters, sheet margins and creep. A ticket states a handful of things about one product, so those are what it changes — finished size, press sheet, binding, one- or two-sided — and everything else is left alone. Building a template from the ticket instead would throw away every decision the operator made. Each override is written to the job log with the value before and after, because a sheet that came out 700 mm wide has to be traceable to the ticket that asked for it.
  - **Both generations, and both ways 1.x states a value.** JDF 1.x wraps intent values in span sub-elements carrying `Actual` and `Preferred` — the negotiated value and the requested one, with `Actual` winning — while plenty of writers put the value straight on the intent element instead. Files a print shop actually receives contain both, so both are read. XJDF 2.0's flattened form is read the same way.
  - **Points, not millimetres.** CIP4 states every measurement in points of 1/72 inch. A ticket saying 595.276 means A4 width, and the conversion happens in the reader so nothing downstream can forget it. Numbers are parsed invariantly, which is the difference between a feature that works and one that reads 595.276 as 595276 on a machine set to Polish.
  - **A binding this application cannot impose is named, not approximated.** CIP4 has a dozen bindings — ring, tape, sewn, hard cover — and mapping them to the nearest neighbour would impose ring binding as saddle stitch. Unmapped bindings leave the template's own binding in place and put the ticket's word in the log.
  - **The page count is never taken from the ticket.** The count that governs imposition is the one the PDF has; a ticket that disagrees is reported and not believed.
- **Job tickets are treated as the hostile input they are.** A hot folder accepts files from whoever can write to a watched directory, and an XML job ticket is the one format in this application that an outsider both authors and expects to be parsed. So DTDs are prohibited outright and no resolver is supplied, which closes external-entity reads (`file:///C:/…`, or a connection opened from inside the print shop's network) and entity-expansion denial of service in one move; document size and length are capped as well. Artwork references are resolved, normalised and only then checked to fall inside a directory the operator allowed — in that order, because normalising after checking is how traversal defences are defeated. Absolute paths are allowed inside a root, so the normal MIS installation works, and `http`, `https` and `ftp` references are refused outright.
- **Hot folders can run as a Windows service.** A hot folder is production infrastructure — the shop expects files dropped at 04:00 to be imposed at 04:00 — and run from the desktop application it stopped the moment the operator logged off, with a workstation that rebooted overnight coming back watching nothing until somebody noticed. `hotfolder service install / start / stop / uninstall / status` registers and drives it, and the hot folder manager offers the same four actions with a status line, elevating through UAC because the application otherwise runs without administrator rights and should keep doing so.
  - **The service is told where the operator's data lives.** Configuration, the licence and saved templates sit in the roaming profile of whoever set the folders up, and a service does not run as that person: as LocalSystem its `%APPDATA%` is under `C:\Windows\System32\config`, where it would find an empty configuration, no licence, and quietly watch nothing — the failure with no symptom. The installer captures the path and passes it as `--data-dir`, which is parsed before anything else so that the whole process resolves one set of roots rather than two.
  - **A named service account is offered**, because LocalSystem authenticates as the machine and a file server routinely refuses it — which is most shops, whose hot folders sit on a share.
  - **Restart on failure is configured at install time**: three attempts a minute apart, reset daily. The point of a service is that a folder which dies at 02:00 on a network hiccup does not stay dead until 08:00.
  - **The service control handshake is treated as the real constraint it is.** The SCM allows a few seconds to acknowledge a start before declaring the service hung, and starting the host is I/O — configuration, the ledger database, staging directories, a scan of input folders that may be on a slow share. So the start is acknowledged immediately and performed on a background thread, and a start that fails stops the service with a non-zero exit code rather than sitting in "running" while watching nothing.
- **The hot folder template list offers the built-in starting points, not only saved files.** On a fresh installation the list was empty, so configuring the first hot folder meant opening the template manager, building a booklet and saving it before coming back. Picking a preset now writes it into the ordinary templates directory and points the folder at it — from that moment it is a template like any other, visible in the template manager and editable, because a preset the operator cannot adjust is worse than no preset. An existing file of the same name is left alone: replacing a template somebody has since tuned, because they picked the same preset twice, would be silent and destructive.
- **Two more thresholds in the press profile**, following the same rule as the rest: line-art resolution and the spot-colour limit describe the machine, so they belong to the profile rather than the job. Offset carries a 1200 dpi line-art floor and a two-plate spot limit, newsprint 800 dpi and one, digital and large format no plate limit at all — they separate to process or run from a roll, where the count is not the constraint.

### Changed
- **The export preflight window was rebuilt around the decision it exists to support.** It stated its findings in a sentence — the one thing an operator with a customer waiting does not read — and rendered them as a flat list in colours hard-coded in hex, which made it the one window in the application that stayed the same shade whatever theme was chosen. Now the verdict is the headline, the counts are chips beside it (and a severity with nothing in it gets no chip, because "0 errors" next to "3 warnings" makes you read two numbers to learn one thing), the findings are grouped severest-first under headings that say what each severity means for the export, and every colour comes from the theme's semantic brushes. A severity filter appears once there is enough to narrow — a forty-finding report is read by asking "what is blocking me" first — and a severity nothing was found for cannot be selected, so no click lands on an empty list. The findings can also be copied to the clipboard: relaying one to whoever made the file previously meant retyping it out of a modal dialog.
- **The validation tab in File info gained the same filter**, plus a count on every category heading, so the shape of a long report is visible without counting rows. The "what to fix first" panel is deliberately still computed from the whole report and never from the filtered subset — it answers a question about the job, and recomputing it inside a filter would quietly answer a different one.

### Fixed
- **Hot folders demanded Ghostscript for work that no longer needs it.** Since 1.5.12 the imPRESS Export Engine packages PDF/X-1a, X-3 and X-4 itself, but the hot folder validator still refused to save any configuration that asked for PDF/X without a path to `gswin64c.exe` — an executable this application does not ship and most machines do not have, because Ghostscript is separately licensed AGPL software. A configured path that points at nothing is still an error; choosing the Ghostscript engine with no path is now a warning, since it may be found on `PATH` at run time. Three user-facing texts that described Ghostscript as bundled with the application were corrected as well, in both languages.
- **Native exports queued behind a limit meant for Ghostscript.** The concurrency throttle exists to stop N simultaneous Ghostscript processes from taking the machine down; it was taken for every PDF/X job regardless of engine, so a folder pinned to the native engine serialised its work behind a limit protecting it from a tool it never invokes. Auto still takes a slot — it decides per file, inside the pipeline, and may yet fall back.
- **Installing the service from the manager did nothing, and the UAC prompt said ".NET Host".** Both the elevation and the service registration took `Environment.ProcessPath` to be the application, which it only is when the application was started through its own apphost. Started as `dotnet "imPRESS Studio.dll"` — what `dotnet run` does, and a plausible way to script a server install — the process is `dotnet.exe`, whose file description is literally ".NET Host". So the operator was asked to elevate the .NET host, and the host was then handed `hotfolder service install`, which it does not understand: it exited immediately, without a window, and the manager reported nothing at all. Registered as a service the same mistake is worse — the service starts, cannot parse its own command line, and stops with nothing anywhere saying why. The launch command is now resolved rather than assumed, naming the managed assembly when the host needs it, and the manager checks the service state afterwards so an operation that achieves nothing says so with its exit code instead of leaving the window unchanged.
- **The service is named for the product rather than for a schema.** The SCM key is `ImpressStudioHotFolders` — plain ASCII, no spaces, capitalised like a type name, because a service key gets typed into consoles and pasted into scripts by people who have never seen the product's stylised lower-case spelling. Services.msc lists it as **imPRESS Studio Hot Folders**: product first so it sorts with anything else this application ever registers, and without a trailing "Service", which the list it appears in already implies.
- **A failed service operation reported nothing at all.** Installing a service needs administrator rights, so the work happens in a second process started through UAC — a separate, console-less instance whose output goes nowhere and whose exit code is a number. An operation refused for a perfectly ordinary reason (the account name is wrong, the service is marked for deletion, group policy forbids it) therefore reached the operator as a window that did not change, which is the least actionable failure a program can produce. The elevated process now writes its result into the operator's own data directory and the manager reads it back and shows it — the file travels downward across the integrity boundary, which is the direction that works, and is deleted as soon as it is read so a stale result can never be reported as this attempt's outcome. When no result was written at all, that is reported too: it means the elevated process never reached the command.
- **Every CLI command reported success even when it failed.** `Environment.ExitCode` set inside a `System.CommandLine` handler is discarded when `Main` returns an int, so `hotfolder service install` exited 0 after refusing to do anything — and any script or caller checking the code was told it had worked. The parser's result and the handler's exit code are now combined, worst wins.
- **Nothing the hot folder manager asked of the service actually ran.** Install, start and remove all produced a UAC prompt and then no change: the service stayed exactly as it was. `--data-dir` decides where every persistent file is read from, so it is parsed by hand in `Program.Main` before the command parser or the composition root exist — and it had never also been *declared* in the command tree. Every command carrying it was therefore rejected as a parse error, and the elevated process printed usage to a console it did not have and exited. From the desktop the failure was invisible; from a console the same command worked, because a console invocation does not pass the option. It is now a global option on the root command, accepted (and ignored) everywhere, while an option the parser genuinely does not know is still rejected. `status` also gained the `--result-file` its four siblings already took: a subcommand that refuses an option the others accept is a button that silently does nothing, which is how this was found.
- **The CLI ran correctly and printed nothing.** Every command invoked from a console produced no output at all — not an error, not a result, nothing — and the operator was left to conclude the program had not run. It had: the log shows each invocation starting and doing its work. A Windows-subsystem executable is given no standard handles by `cmd.exe`, and .NET binds `Console.Out` to whatever `GetStdHandle` returns the first time it is used; once that is an invalid handle, every write goes nowhere for the life of the process. `AttachConsole` gave the process a console to draw on but did not repair those handles, so the half of the fix that had been there since the CLI was written was the half that does not matter on its own. The handles are now opened explicitly against `CONOUT$` / `CONIN$` and installed before the streams are rebound — and a handle the caller already supplied is left alone, so redirection to a file or a pipe still goes where the caller is reading.
- **A service command leaves an audit trail.** Registering a service is an administrative act, and it is also the one operation whose output an operator most often cannot see: an elevated process has no console at all. Every outcome is now written to the application log as well as to the console and the result file.
- **The service layer speaks the operator's language.** Twenty-three messages from the Windows-service commands — the ones that now reach the manager's status row and the CLI — were English regardless of the interface language, in an application that is otherwise bilingual throughout. They are resolved through the same localisation service as everything else. The log line that carries them keeps its English structure, so a support reader still recognises the event even when the message inside it is Polish.
- **The documentation PDFs were four releases out of date.** They were built on 31 August, described 1.5.12, and contained neither the CIP4 job tickets, nor service mode, nor the rewritten preflight chapter — while the HTML beside them, which the site links first, had all three. All four are regenerated from the current sources, and the page count quoted on the download cards now matches what is actually in the file rather than what used to be.
- **The hot folder screenshots showed the wrong window, from the wrong version.** The image captioned "hot folder manager" was in fact the *editor*, captured before 1.5.12 — complete with the "Ghostscript path" field the release removed, on a page that tells the reader PDF/X needs no Ghostscript. The manager now illustrates the manager (including the Windows-service row), the editor has its own figure showing the built-in starting points, and both exist in Polish and English.
- **The template manager did not show the template in use.** `TemplateEditorViewModel` is a singleton, and nothing ever handed it the active template — so every way of choosing one that does not run through that editor (a suggestion applied from the suggestion dialog, the default template auto-loaded at startup, a restored session) left the manager displaying whatever it happened to hold last. An operator who applied a suggested business card and opened the manager to check it was shown a different template, with nothing to say that what they were looking at was not in use. The manager now opens on the active template, seeded at the one place all four routes pass through. It adopts a copy, so editing there still changes nothing until *Use template*, and it leaves the editor alone when it is already showing that setup — otherwise reopening the manager would forget which file the template came from and turn the next *Save* into a *Save as*. The comparison deliberately ignores the template's `Id`: `Validate()` mints one whenever it is missing, so the same setup routinely carries two different identifiers, while a template adapted from another one carries its source's — the identifier answers neither question.
- **The saved preflight report could not be saved.** `PreflightReportWriter` shipped complete in 1.5.12 — eleven passing tests of its own, an entry in the changelog saying a report could be saved — and nothing in the application ever called it. There was no button. Every test supplied its own caller, which is precisely why none of them could see that the product had none: a class with no caller is invisible to a suite that always provides one. *File info → Validation* now carries **Save report**, next to the repairs that act on the same findings, and writes the report as it stands — same findings, same order, same press profile — so what reaches the customer is what the operator was looking at. A test now asks the opposite question of the usual one: does the UI call this at all.
- **Repairs were judged against default thresholds rather than the shop's.** The File info dialog asked the fixup engine what applied using a freshly constructed profile instead of the one the report was produced with, and ran the chosen repairs the same way. A shop whose profile raises RGB to an error, or lowers a resolution floor, got its findings from its own profile and its repairs from someone else's.

### Tests
- 188 new tests. The ones worth naming are about boundaries rather than happy paths: that mixed page rotations suppress the plain rotation finding instead of reporting one problem twice, that `/Rotate 450` and `-270` are recognised as the same 90° before anything is judged, that a spot called "Allura Red" is not the reserved `All` colorant, that the `All` colorant does not consume a press unit, that the metadata repair refuses to declare a trapped file untrapped, and that the layer repair deletes nothing and leaves visible layers alone. Eight cover the manager adopting the active template, two of them on the trap that made the obvious implementation wrong: the same setup routinely carries two different `Id`s (`Validate()` mints one whenever it is missing), while a template adapted from another one carries its source's, so the identifier had to be left out of the comparison entirely. Two ask questions no ordinary unit test can: one pins the call site between two view models that were each correct while the call between them was missing, and one greps the UI sources for references to the report writer and the fixup engine, because the failure it guards against is the ABSENCE of a caller rather than a wrong one. One test was written wrong first and caught by its own teeth check: it asserted that the contone check skips line art using a 300 dpi image, which sits inside the acceptable band anyway, so it passed with the skip removed — the resolution is now below both floors, where the assertion means something. The job-ticket suite is weighted the same way: most of it covers what the reader and the resolver REFUSE, because that is where the value of both components sits. One of those tests was written wrong too — it put the external entity in an attribute value, which XML forbids outright, so it passed with every defence removed and proved nothing. Referenced from element content, where it is well-formed and would resolve, it fails the moment either guard is taken away. The hot folder additions are pinned where they can be: that PDF/X validates without Ghostscript for all three flavours, that `--data-dir` is parsed before the application exists and refuses a trailing option or the next flag as a directory, and that picking a preset twice does not overwrite a template the operator has since edited. Every built-in preset is also fed through template validation, because a preset a hot folder runs unattended should fail here rather than at 04:00. The launch-command resolution is pinned on both branches, which took two attempts: the first version read the process path itself, so under a test runner it only ever exercised the apphost branch and passed with the .NET-host branch deleted. Taking both inputs as parameters is what makes the branch that actually broke reachable.

## [1.5.12] - 2026-08-31

Two bodies of work. Ghostscript stops being a dependency — the imPRESS Export Engine now packages all three PDF/X flavours, so nothing in the product requires an external, separately-licensed tool. And preflight is brought up to professional standard: the report's advice panel had drifted away from the rules it advises on, strict mode refused ordinary booklets, and a shop had no way to say a check does not apply to it.

### Added
- **PDF/X-1a:2001 and PDF/X-3:2003 are packaged natively.** They were the last two features that forced the Ghostscript path. Both are defined on PDF 1.4 and differ from X-4 in exactly three ways, all now handled in one place: the PDF version stamped on the file, the conformance string claimed in the info dictionary and the XMP packet, and the ban on live transparency. Ghostscript remains selectable, but it is an option rather than a requirement — and it is not distributed with the application, being separately licensed AGPL software.
  - **Transparency is handled honestly, not by pretending to flatten.** Turning genuinely non-opaque artwork into opaque marks means compositing the page. That is a rasteriser's job, and doing it would silently convert vector text into pixels — the one thing a prepress operator must never discover after the fact. Instead the engine removes the transparency constructs that are provably *inert*, and refuses — naming what it found — when the file carries transparency that actually paints.
  - **That covers the case which actually occurs.** Most print-ready artwork contains no live transparency at all, yet still fails PDF/X-1a validation, because anything that has passed through Illustrator or InDesign inherits a page-level `/Group << /S /Transparency >>` plus a handful of `/ca 1`, `/BM /Normal`, `/SMask /None` graphics-state entries. Every one of those is a no-op: a transparency group whose members are all fully opaque and composited in Normal mode renders identically to ungrouped content. Dropping them changes no pixel while making the file conformant.
  - **Conservative by construction.** Inert entries are stripped only when the whole document is free of real transparency. A single `/ca 0.5` anywhere means nothing is stripped and the export is refused: an isolated or knockout group becomes observable once real transparency is in play, and quietly dropping it there would change the printed result.
  - **X-1a colour is verified, not assumed.** The standard permits DeviceCMYK, DeviceGray and spot only. Rather than trusting the caller's conversion flags, the engine re-scans the file it has just written and fails the export if any DeviceRGB survived. A wrong flag, or a construct the converter skipped, can no longer yield a file that claims CMYK-only conformance while carrying RGB.
- **A shop can decide what each check is worth — including switching it off.** A profile now carries per-code severity policy (`Off`, `Info`, `Warning`, `Error`) keyed by the stable issue code. A printer who traps in the RIP can silence the trapping advisory; one running a CMYK-only press can raise RGB from warning to error; one who deliberately pads booklets can drop the note entirely. Until now the only per-check control was three hard-coded booleans, so every other rule fired at whatever severity its author chose, for every shop, forever.
  - **Applied in one place.** The policy is enforced in `PrepressValidationContext.Add`, which every validator funnels through, so all twenty rules — and any added later — inherit it without knowing the mechanism exists.
  - **`Off` means gone, not demoted.** A suppressed finding is neither listed nor counted, so it cannot gate a strict export from behind a filter.
  - **A profile from a newer build still loads.** Unknown codes and unparsable policy names are skipped rather than rejected, so a settings file that mentions a check this version does not have degrades to default behaviour instead of failing to open.
- **The report can be saved.** `PreflightReportWriter` renders a result as plain text: header (file, profile, timestamp, verdict), then findings severest-first, repeats of one code collapsed, and consecutive pages folded into ranges — `1-3, 7, 11-13` rather than a line of forty numbers. A prepress finding is an argument with a customer about whose file is wrong, and it wants to travel with evidence; until now the only way to communicate one was to describe it from memory. Plain text on purpose: it opens everywhere, pastes into a mail without becoming an attachment nobody opens, and stays greppable in an archive years later. A message whose arguments do not match its template degrades to the bare code rather than taking the save down.

- **The left setup panel collapses too.** The inspector has had a collapse handle on its seam with the canvas for a while; the setup panel on the other side had none, so on a laptop screen the canvas was stuck with whatever was left of 312 px it could not reclaim. It now carries the mirror-image handle, and the glyph points the way the panel travels (`‹` to collapse, `›` to bring it back) rather than both tabs showing the same arrow. Both side columns are `Auto` with the width on the panel itself, which is what makes hiding one actually hand the space to the canvas instead of leaving a band of empty background where it used to be.
### Fixed
- **Every sheet the composer wrote carried an RGB transparency group.** PdfSharpCore stamps `/Group << /CS /DeviceRGB /S /Transparency >>` onto every page it serialises — measured on a blank page, with no content and no transparency, the saved file still contains it. On a PDF/X-1a file that is wrong twice over: a transparency feature on a PDF 1.4 file, and a DeviceRGB colour space in a CMYK-only standard. Removing the key through the object model achieves nothing, because the writer puts it back on the next save, so the groups are now taken out of the finished file instead. The replacement is byte-for-byte the same length (spaces are legal whitespace between PDF tokens), which keeps every cross-reference offset valid rather than forcing a full xref rebuild.
- **A library change can no longer produce a file that lies about its conformance.** Stripping those groups depends on how PdfSharpCore serialises pages, so the packaging path re-reads the file it has just written and fails the export if any transparency survived. A future library update that breaks the technique now surfaces as a refused export rather than as a PDF/X-1a file with a transparency group inside it.
- **The "what to fix first" panel gave generic advice for 47 of 60 findings.** Actions are resolved by issue code, and the advice vocabulary had drifted from the codes the validators actually emit — eight templates carried names nothing could produce (`MIXED_COLOR_SPACE` against the real `MIXED_COLOR_SPACES`, `SPOT_COLOR_FORBIDDEN` against `SPOT_FORBIDDEN`, `IMAGE_DPI_VERY_LOW` and `IMAGE_DPI_HIGH` against `IMAGE_DPI_LOW` and `IMAGE_DPI_OVER_MAX`, plus `RGB_IN_CMYK_TARGET`, `TRANSPARENCY_FLATTEN`, `PDFX_NONCONFORMANT` and `FONT_SUBSET_INCOMPLETE`). Both sides were internally consistent, so nothing failed and nothing logged: the panel an operator reads first simply stopped advising, and every finding added since — the whole 1.5.8 prepress set, including every error-severity one — fell through to "N issue(s) detected, see the list below". Every code now has its own imperative in both languages, the dead templates are gone, and a test compares the two vocabularies so they cannot drift apart again.
- **Strict preflight refused ordinary booklets.** In headless and CLI runs the switch aborted on `Issues.Count > 0`, which counted the informational notes — and two of those describe entirely normal production. `SIGNATURE_PADDING` fires whenever a booklet's page count is not a multiple of the signature, which is precisely what the saddle strategy exists to pad; `DUPLEX_ODD_PAGES` fires on any odd-page duplex job. A six-page A5 booklet on an eight-page signature therefore failed a strict export, making the switch unusable for the automation it was built for. Errors already stop an export unconditionally, so what strict adds — and all it now adds — is warnings; the abort message also names the codes rather than saying "warnings present".
- **A trim mismatch reported once per page.** Every other rule aggregates, but this one emitted a row for each offending page — and a document whose trim does not match the template usually mismatches on all of them, so a forty-page job buried the rest of the report under forty identical lines. It is now one finding: the first offending page carries the measured numbers, the page list carries the extent.
- **The template-suggestion dialog cut its cards off, on both axes.** Horizontally, nine of the card's text fields had no wrapping: a product name carries its dimensions ("Notes / kalendarz A5 — spirala (148×210 mm)") and shares its row with the confidence badge, so at the dialog's width it ran off the card instead of breaking onto a second line. Vertically — and this is what an operator noticed — the list put its inset on the ScrollViewer as `Padding="16,12"`. A ScrollViewer subtracts its own padding from the content it measures without adding it back into the scrollable extent, so the extent came out 24 px short of the list: scrolled all the way to the bottom, the last card still hung 36 px below the viewport with no way to bring it into view. The inset now sits on the content as a margin, where it is measured as part of what scrolls. The same attribute was doing the same thing to the marks-profile editor and the preflight report, whose last finding was the one being cut.

### Changed
- **The ink-coverage scan is native, and the code now says so.** The scan itself has run on PDFium plus a real ICC transform since 1.5.0, but it still lived in a class called `GhostscriptInkScanner`, which also carried an unused Ghostscript `inkcov` implementation. The dead code is gone; the coverage model and the warning threshold moved to `NativeInkScanner`, where they belong, and Ghostscript discovery moved into a `GhostscriptLocator` that does only that.
- **Ghostscript discovery no longer looks for a bundled copy.** It searched two paths inside the installation directory for a copy that is not shipped — Ghostscript is AGPL software and cannot be redistributed inside a commercial closed-source product. It now looks where a user-installed copy actually lives: the configured path, then `PATH` (which the previous implementation never checked, despite the in-app help telling operators to add it there), then the standard `%ProgramFiles%\gs` layout.
- The export dialog no longer describes the native engine as "X-4 only", and the preflight advice for X-1a / X-3 transparency now distinguishes the inert constructs the engine removes by itself from transparency that genuinely paints.

### Tests
- 32 new tests. The two that matter most compare vocabularies rather than behaviour: every emitted code must have advice, and every piece of advice must belong to a code something can emit. Each side of that pairing was self-consistent while being wrong about the other, which is exactly the shape of defect no single-sided test can see. Also pinned: action templates use only the two arguments the generator supplies (a template reaching for a third throws while the operator is reading the report), the policy enum's values line up with the severities they stand for (the mapping is a cast), and informational findings never gate a strict export. The scrolling pair is behavioural plus structural: one scrolls the suggestion list to its end and asserts the last card is inside the viewport, the other forbids vertical padding on any ScrollViewer in the UI, since the mistake is one attribute away in every dialog and fails silently until the content outgrows the window. The headless test app also loads FluentTheme now — without it a ScrollViewer has no ScrollContentPresenter and quietly clips instead of scrolling, which is why this suite could not see the defect. The panel handles are pinned structurally, because what makes a collapse work is where the fixed width is written — on the panel, never on the grid column — and markup that gets that wrong looks correct while giving the canvas nothing back.

## [1.5.11] - 2026-08-30

Folded leaflets stop being sized by the press sheet. The panel width now comes from the product the operator entered, the way it does for every other binding, and both the item size and the sheet size can be picked from the standard-size list.

### Fixed
- **Fold panels were spaced by dividing the sheet, not by the product size.** Z-fold, accordion and gatefold derived their panel width as `(sheet − margins − gutters) ÷ panels`, and the trim size was only a rectangle the artwork was fitted into, centred inside whatever that division produced. The fold lines therefore moved whenever the stock changed: a DL leaflet (panels 99 mm) imposed on SRA3 came out with its panels 143.33 mm apart, so a folder creasing at 99 mm creased through the artwork. There was no way to say "the product is A4 folded to DL" — only the sheet was an input, which is exactly backwards for a product whose geometry is fixed by the finishing machine. Panels are now laid out at the entered size, touching at the fold lines, with the whole block centred on the sheet and the leftover paper left blank. `FoldGeometry` is the one place that computes this, shared by the strategy, validation, the preview and the marks renderer, so all four agree on where the sheet gets creased.
- **A fold whose panels overran the sheet validated clean.** Only gatefold had a fit check; Z-fold and accordion fell through to the generic N-up test, which — with the `NUpX = 1` every fold preset ships — asked whether ONE panel fits. Three panels of 210 mm on a 297 mm sheet passed validation and reached the strategy, which produced panels overlapping by 114 mm, the first starting 52 mm off the left edge of the paper and the last overhanging the right. All three folding bindings now share one check (`panels × panel width + gutters + margins` against the sheet, plus the panel height), and it names what it measured: *"3 panele po 210,0mm"* or *"wyrób rozłożony 297×210mm = 3×99,0mm"*.
- **The built-in DL Z-fold preset was internally inconsistent** — an A4 landscape sheet with a 210 × 99 mm trim, i.e. the case above — and shipped that way. It is now an A4 landscape product (entered as the unfolded leaflet) on SRA3, giving the 99 × 210 mm panels the name promises. The DL accordion moves to a 10 mm sheet margin so its four panels sit inside the stock, and the two US fold presets are marked as what their code comments already said they were: flat Letter products, so the tri-fold's panels resolve to 93.1 mm instead of being read as 279.4 mm panels.
- **The size dropdowns did not follow the template they describe.** The paper list and its landscape tick were only re-pointed at the template by *Load*, *New* and the preset buttons; on startup, and after any other swap, the tick stayed at "portrait" while the template held a landscape sheet. The next size picked from the list therefore came out rotated against what the operator was looking at. Both dropdowns (sheet and item) now re-sync on every template change, including the one the editor opens with.
- **Fold marks were only ever drawn for saddle stitch.** Every fold preset ships with fold marks enabled, and neither the preview nor the exported PDF showed a single one for a folded leaflet — the one binding whose name is about folding. Dashed ticks are now drawn at the top and bottom edge of each crease, at the positions the panels actually meet, in the preview and in the PDF alike. Saddle stitch keeps its spine mark.
- **A pitch-spaced Step & Repeat grid that overran the sheet validated clean.** In centre-to-centre mode the strategy centres a block of `(N−1) × pitch + item` on the sheet and ignores gutter and sheet margin — but the only sheet-fit check that ran was the generic margin/gutter formula, which describes a different layout. A 4-column grid of 50 mm stickers at a 60 mm pitch on A4 (230 mm needed, 210 available) passed validation and printed with the outer columns cropped at the sheet edge; conversely, a valid pitch template with a non-zero sheet margin could be rejected for space the strategy never uses. Validation now checks the pitch grid against the sheet exactly the way the strategy lays it out, and the generic formula no longer applies to that mode.
- **The US accordion preset produced 2.13 × 11″ strips.** It folded a portrait Letter across its 8.5″ width into four tall, narrow panels — a shape no folder rack expects. It now matches the tri-fold preset's orientation: flat Letter landscape on 11×17, four standard 2.75 × 8.5″ panels.
- **Leaving Ganging could strand a template in work-and-turn.** The duplex-mode combo is only meaningful for Ganging, but the selected mode survived a binding switch, and OK then failed validation on a value the operator had no visible control for. The mode now resets to sheetwise when the binding stops being Ganging, and the combo is shown only where it applies (duplex Ganging). Flipping the duplex checkbox also updates that row live — it previously refreshed only when the whole template was swapped.
- **Five suggestion-library products shipped on a sheet their own bleed could not fit.** The B2, Tabloid, ANSI C and ANSI D poster presets paired the poster with a press sheet of the very same size, and the Half-Letter flyer (as wide as a Letter sheet) tried to gang on Letter — every one of them built a template that failed validation the moment the operator applied it. Posters now run on a sheet one class up (B2→B1, Tabloid→ANSI C, ANSI C→D, D→E), the Half-Letter flyer gangs 2-up on portrait Tabloid, and a new test feeds every library entry its own canonical size and asserts the resulting template validates — so this class of bug cannot ship again.

### Added
- **The item size can be picked from the standard-size list, like the sheet already could.** The trim card gets the same dropdown and landscape tick as the sheet card, over the same registry — so custom sizes added there appear here too, and persist across restarts as before. Typing dimensions by hand re-selects the matching standard size or clears the selection rather than leaving a name that lies about the item. This applies to every binding, not only the folded ones.
- **Folding templates can be described either way round.** A new *Entered size is* selector on the fold card reads the trim as **one panel** (the page size of the incoming PDF) or as the **unfolded product** — "A4 folded to DL" being the way most jobs arrive — in which case the panel is the width divided by the panel count. Templates written before this release deserialize as *panel*, which is what their trim already meant, so existing sheets are unchanged.
- **The quick-start presets were renamed around the product and grouped by kind.** Two dozen entries in one flat column, named after the mechanism ("A4 Ganging 2-up", "DL Ulotka Z-fold"), asked the operator to translate binding jargon into the job on their desk. Every preset now leads with the product and the finished size — *"Ulotka DL — falc Z, 3 panele"*, *"Wizytówki 85×55 mm — 12 na arkuszu"*, *"Broszura A4 — zeszytowa"* — and the list is split under four headings: booklets & books, folded leaflets, multiple items per sheet, labels & stickers. The sub-line under each name states what actually gets produced, verified against the preset's own parameters rather than inherited from an older revision: *"SRA3 poziomo → A4 poziomo złożone na 3 panele 99×210 mm"*.
  - Three descriptions had gone stale and were saying the wrong thing: the DL Z-fold still advertised an A4 sheet after this release moved it to SRA3, and the A4 sticker preset claimed a 55 mm pitch where the template carries 52 mm.
  - **The list says out loud that its numbers are a starting point.** A sub-line reading "SRA3 landscape → A4, 8-page signature" describes the preset the way a product spec describes a finished job, which invites the reading that clicking it locks those sizes in. The section is now headed *Starting points* with one line under it — the sheets and formats listed are starting values, changeable before the template is saved — every preset's hover text repeats it, and the status line after a click says the parameters are yours to adjust. The descriptions keep their exact numbers: what the preset actually sets is worth knowing before clicking it.
  - A preset whose category key matches no heading would silently vanish from the picker, so the catalogue is pinned by tests: every preset reaches exactly one group, and every name, description and heading exists in both languages.

- **The fold card shows what the numbers resolve to**: panel size, unfolded size, and the panel block against the sheet width, with a warning when the block does not fit. That is the readout an operator needs before sending 500 leaflets to a folder set to fixed positions.

- **The template manager gained a live sheet preview.** A third column draws a schematic of the first press sheet — item slots with their page numbers, blank slots, fold lines where the folder will crease, the usable-area margin — refreshed as the operator types, with a per-sheet yield line (items × coverage) underneath. It is planned by the very same binding strategies that plan production, so the thumbnail cannot drift from what exports; roll previews clamp to a readable handful of rows, and an invalid template degrades to a plain "fix the errors above" note instead of a stale drawing.
- **Validation runs live, not only on OK.** The error banner now appears while the operator is still editing — and clears the moment the field is fixed — instead of waiting for *Use template* to be clicked. The refresh is content-guarded, so the banner never flickers on a quiet tick.
- **Every binding type now has a starting point.** Wiro and gatefold were the two bindings with no preset — invisible to an operator browsing the quick-start list. Four new entries: *Notes A5 — spirala (wiro)* (4 × A5 on portrait SRA3, cut → punch → bind) and *Ulotka altarka — 4 panele 100×210 mm* (400 × 210 mm unfolded, doors narrowed 2 mm) in the metric set; *Half-Letter notebook — wire-o* and *Gatefold leaflet 17×11″* as their US twins. A test now pins the rule: each `BindingType` must be reachable from at least one preset in both unit sets.
- **The preset catalogue is proven valid by tests, not by comments.** Every factory's hand-written fit proof is now executable: each preset must pass `Template.Validate()`, carry a unique id, and survive a JSON save → load round trip unchanged.
- **DL and the business-card sizes joined the standard-size registry** (DL 99 × 210, BC 85×55, BC 90×50, BC US 3.5×2″), so the item-size dropdown names what the presets set instead of clearing.
- **The signature spinner hides for bindings that ignore it.** *Pages per signature* only drives saddle stitch and perfect binding; for ganging, wiro, folds, roll and step & repeat it was a spinner that did nothing.
- **About lists the project's addresses.** The website and the GitHub repository are now clickable in the About box, with the address itself as the caption so it stays readable (and typeable) on a machine with no browser configured. The website URL is a localisation key rather than a constant, so the Polish interface opens the Polish site and the English one opens `/en/` — a test pins both languages, checks each address really parses as an https URL (a "translated" or typo'd URL would leave the button silently doing nothing), and asserts the two languages still point at one repository.
- **Template suggestions got materially smarter.**
  - **Bleed detection.** A PDF that hands over its MediaBox instead of a TrimBox arrives as trim + bleed — an 85×55 card as 91×61 — and used to be answered with a shrug of near-misses. When the raw size snaps to nothing but "size − 2×bleed" snaps *exactly* (tried at 3, 2 and 5 mm, within a deliberately tight 1 mm band so a legitimate custom size is never re-read as someone else's product), the engine corrects the input, builds the template on the true trim with the detected bleed, and says so on the card: *"Wymiary pliku zawierają spad ~3 mm — jako format użytku przyjęto 85×55 mm."*
  - **Distance thresholds now scale with the format.** The fixed 25 mm search radius located business cards perfectly and made posters unfindable — a B1 file 30 mm off nominal got "no matches". Both thresholds keep their old values as floors and grow with the input's diagonal (5% for the search radius, 0.3% for the exact-snap band), so card-scale behaviour is untouched while poster-scale files finally resolve.
  - **New products**: wire-o notebooks/calendars A5 and A4 (offered alongside the saddle-stitch booklet from 5 pages up — the operator knows which finishing line the job goes to), the 4-panel DL accordion and the 4-panel gatefold *altarka* (gated to 7-8-page documents so they never crowd the 6-page Z-fold), posters A1 and B1, and the 105×297 rack leaflet. Context adaptation now preserves gatefold style and fold geometry instead of silently reducing a 4-panel product to the 3-panel default.

### Changed
- *Optimise N-up* now declines to touch bindings that have no N-up grid (folds, saddle stitch, roll-fed) instead of writing grid numbers their strategies never read.
- Help and the marks-profile editor no longer describe fold marks as saddle-stitch-only, and the template help lists the item-size picker and the two ways a folded product can be described.

## [1.5.10] - 2026-08-08

Ganging stops being a copier. The N-up grid can now carry the document's consecutive pages instead of the same page over and over — the difference between imposing a 5-page job and imposing page 1 of it five times.

### Added
- **Source pages can be reordered by dragging their thumbnails.** Drag a page in the preview strip to a new position and the loaded document is rewritten in that order, so it is what gets planned, imposed, exported and saved — there is no separate "apply" step to forget. A banner appears once the order has been touched, with a *Restore order* button back to the natural sequence.
  - **The drag says where the page will land.** The tile follows the pointer as a ghost, the tile it came from fades so its origin stays visible, and an insertion caret marks the exact gap the page will drop into — a wrapped grid of look-alike tiles otherwise gives the operator nothing to aim at. The caret overlays the tile rather than occupying columns of its own: reserving space for it widened every tile by 6 px, which at the inspector's width was enough to push a tile onto the next row and break up the strip. An indicator that only appears mid-drag must not change the layout it is pointing at. The caret sits on one side of a tile while the move removes the page before re-inserting it, so the index arithmetic that keeps the caret's promise is pinned by tests: an off-by-one there lands the page one slot from where it was aimed and reads as the feature simply being inaccurate. A page dropped outside the strip cancels the drag instead of being answered with "only PDF files are accepted".
  - **The order survives the page edits that rebuild the document.** Rotating or converting a page re-assembles the loaded document from the source files, in file order, so the arrangement is held as a permutation and re-applied after every assembly rather than by rewriting whatever file happens to be loaded. Otherwise an operator would reorder, rotate one page, and get their arrangement silently reverted — noticed on the printed sheet.
  - **Saving honours the order too.** *Save PDF* reads a source file rather than the loaded document, and *Merge and save* re-merges from the file list — both would have handed back the original sequence. A manual order belongs to the loaded document, so when that document is the file being saved, the save reads it; the redirect is scoped to exactly that case, and a per-file save otherwise stays a per-file save.
  - Page edits address physical pages while the strip names positions, and after a move the two numberings differ. Rotating "the first thumbnail" now rotates the page that is actually first, not the one that used to be. A permutation whose length no longer matches the document is dropped rather than applied: the set of pages changed underneath it, and a stale permutation would scramble the job instead of preserving an intent.

- **The in-app help was rewritten against what the app actually does, in both languages.** It still described a three-binding, session-only-custom-sizes version of the program. Ten sections are new — the step bar and what each numbered step really opens, source files and per-page edits, ganging slot modes, layout optimisation and the sheet advisor, template suggestions, hand-editing slots, marks profiles, preflight (both levels, the five press profiles, and which findings have repairs and which deliberately do not), hot folders, and application settings — and the existing ones were brought up to date: all nine bindings instead of three, the current export dialog, preview tools including separations, eyedropper and the registration overlay, and a glossary that now covers ganging, work-and-turn, TAC, trapping, output intents and the PDF/X flavours. Two stale claims are gone: custom paper sizes persist across restarts, and the shortcut table lists the undo/redo and F1 keys the app has had for a while. A test pins every help key to both dictionaries, because a section translated in one language reaches the operator as a raw key in the middle of the manual.

- **Ganging can now fill its slots with consecutive document pages, not only with copies of one.** Ganging had exactly one behaviour: every slot on a sheet carries the same source page, with duplex pairing pages 1-2 onto the front/back of sheet 1, 3-4 onto sheet 2, and so on. That is right for a business-card run — one design, twelve copies, verso on the back — and wrong for the thing the presets are named after: a 5-page document ganged 2-up came out as three sheets of one page repeated, with half the document on versos and the operator convinced pages 2 and 4 had been dropped. **ConsecutivePages** fills the slots in reading order instead: 1+2, 3+4, 5. With duplex each slot becomes its own leaf (odd page recto, next page verso, X-mirrored so it backs up after a long-edge flip), and work-and-turn pairs each product across the fold rather than repeating one.
  - **Nothing changes for templates that already exist.** The mode defaults to RepeatItem, which is what every saved template and every hot folder has been doing; a template written before this release deserializes into exactly the press sheets it produced yesterday. The multi-page gang presets — A4 2-up, A5 4-up and their Letter / Half-Letter twins — ship with ConsecutivePages, because "2× A4 on the sheet" has always meant two different A4s. The business-card, DL-leaflet and rack-card presets stay on RepeatItem: those are single-design runs.
  - The template manager grows a *Ganging* card explaining, in one sentence per mode, what the current setting does to a multi-page document — the question the old behaviour raised and never answered.

### Changed
- **Switching language or units now changes what is on screen immediately.** The setting was already pushed into the running app; what did not work was the binding almost every caption in the program uses.
  - **`{loc:Loc}` had never followed a language change at all.** The markup extension bound to the localisation service's INDEXER and relied on a WPF-style `PropertyChanged("Item[]")` notification, which Avalonia does not treat as invalidating an indexer path. Every menu, button, label, tab, dialog and tooltip in the application rendered correctly on first paint and then froze in the language the app had started in — for the life of the process. Only the handful of view-model properties that notify by name ever updated, which is exactly why this read as "some of the interface changes" instead of "the mechanism is dead". The extension now binds to a plain property that really does raise PropertyChanged and maps it to text through a converter, so one change fixes every caption in the app; `{loc:UnitLabel}` had the same broken leg and gets the same fix. A headless test drives a real bound control through a language switch, because the defect was invisible at every level above the binding engine.
  - Text that no XAML binding covers had to be pushed by hand, and now is: the sheet canvas repaints and drops its composed scene (the old wording was baked into the bitmap), the preview's front/back labels re-emit, and source-page captions are retranslated in place rather than by rebuilding the strip, which would have thrown away every rendered miniature to change a caption.
  - Readouts that resolve through a **converter** — the "no template" placeholders on the step bar and the template card, the Yes/No duplex line — only re-run when their binding source changes, so they are nudged explicitly. The "Load checked (3)" caption and the licence badge follow too: the badge is now composed from the licence *facts* held in the view-model instead of a sentence built once at startup.
  - The idle "Ready" greeting follows the language; a status message about something that happened does not. "Loaded 12 pages" was true in the language it was written in — retranslating a record would be a small lie, and the settings hint now says so instead of promising a restart nothing needs.
- **Step (4) is now called "Export settings", not "Export".** It opens the options dialog; the green button on the right of the same bar is what writes the file. Two buttons a hand's width apart, both labelled export and only one of them exporting, is a trap the step bar laid every time. The numbered step also loses its ▶ glyph, which read as "run this" right next to the button that actually does.

### Fixed
- **Editing a slot moved the outlines but not the page.** Position, size, rotation and source-page changes in the inspector reached the plan correctly and then appeared to do nothing: the canvas caches its whole composed scene into one bitmap and re-blits it while nothing in its snapshot key changes, and that key carried the sheet NUMBER but nothing about the sheet's content. An edited sheet therefore looked identical to the cached one. The guides and the selection outline are recomputed every frame and did move, which is exactly why this read as "only lines show, physically nothing happens to the image". The cache now compares the sheet instance itself, which every edit path — including undo and redo — replaces rather than mutates.
- **Rotating a page right and then left left it permanently landscape.** The rotation is applied to a working copy by changing the page's `/Rotate`, and writing that through PdfSharp's page wrapper makes the library resolve the page's VISUAL size and bake it into the MediaBox on save. So the round trip came back to `/Rotate 0` on an A4 whose box had become 842×595: the content was upright again, the sheet was not. Measured on a portrait A4 — right then left used to yield `media=842×595`, and now yields `595×842`, identical to the original. The rotation is written straight into the page dictionary instead, which leaves the geometry exactly as the file declared it; the page-tree walk that finds the page also handles nested `/Pages` nodes and inherited `/Rotate`, both of which a flat lookup got wrong on documents assembled from several sources.
- **A freshly loaded document stayed soft until the "Str. N" badge faded.** Two independent defects, and the badge really was implicated in one of them.
  - **The canvas kept blitting a stale composed scene.** The canvas caches its whole composed scene into one bitmap and re-blits it while nothing relevant changes. It flagged that cache as provisional when a page had drawn a *loading placeholder*, but not when the page had drawn a **stand-in bitmap from another scale** — so a scene painted with a 150 px thumbnail blown up to full size counted as final. The completed full-resolution render then had nothing to invalidate, and the soft scene stayed on screen until some unrelated part of the cache key changed. On a freshly loaded document the only thing that changed on its own was the page badge's fade phase, two seconds later — which is exactly when operators saw the preview snap into focus. Stand-ins are now flagged like placeholders, so the real render replaces them the moment it lands.
  - **The thumbnail strip cancelled the canvas's render.** The canvas and the strip share one renderer and ask it for the same page at wildly different sizes — display resolution against 150 px — but it kept a single "newest scale wanted" slot per page. Every thumbnail request marked the canvas's full-resolution render stale; that render bailed out at the concurrency gate. The strip re-requests its visible window on every completed render, so this repeated until the whole strip was cached. Requests now declare which lane they belong to and the two no longer cancel each other. Measured on a 595×842 page: the shared slot left a 446 px bitmap where the canvas had asked for 2677 px.
- **"Optimize N-up" left the N-up fields showing the old grid.** The optimiser wrote the new layout straight into the template, which is a plain object with no change notification, so the status bar announced "4×5" while the spinners next to it still read "4×3" — and the operator had to retype the numbers the button had just computed. The fields now follow the template. Their upper bound moves from 16 to 100 at the same time: a small item on a large sheet genuinely gangs past sixteen rows, and a spinner capped at 16 would have quietly written that cap back over the computed layout the moment it started refreshing.
- **Clicking a source-page thumbnail threw you out of the source-page list.** The canvas jumping to the sheet that page landed on is the point of the click; switching the navigator to the *sheets* list as well meant the operator lost their place after every single click and had to switch tabs back to look at the next page. The canvas still jumps, the sheet tile is still highlighted, and the list you are browsing stays put.

## [1.5.9] - 2026-08-07

The sheet you pick is the sheet you get. Three defects in the template manager conspired to impose jobs on a sheet nobody chose, and the N-up button only ever knew how to make a layout bigger.

### Fixed
- **The sheet picked in the template manager could be ignored, and the job planned on the preset's sheet instead.** Clicking a preset replaced the template — including its sheet — but never told the paper dropdown, so the dropdown kept pointing at the previous pick. Selecting that same entry again raises no change notification, which left the operator no way to get their sheet back: "Business cards 12-up, then B1 landscape" imposed on SRA3. The dropdown and the landscape tick now follow whatever sheet the template actually carries — after a preset click, a template load and *New*.
- **The paper dropdown went blank after applying any landscape preset.** The sync looked the sheet's name up verbatim, and a landscape sheet is named "SRA3 (L)", which is not a paper-preset name — so every ISO preset in the quick-start list (all of which are landscape) left the field empty while describing itself as "SRA3(L) → 12× business card". The name is now read with its orientation suffix, and matched on dimensions when the name says nothing usable. A sheet no preset describes clears the dropdown rather than showing a name that lies about what will print.
- **"Optimize N-up" could not make a layout smaller.** It compared the current N-up against a *denser* packing and stopped there, so a template moved to a smaller sheet — 12-up business cards from SRA3 to A4, 374 mm of layout against 210 mm of paper — was told it was "already optimal", and the mismatch surfaced much later as a validation error at export. The button now fits the layout to the sheet in both directions, using the same arithmetic (and the same tolerance) validation uses, so it can never call a layout correct that export will refuse. When not even one item fits, it says so and leaves the operator's numbers alone instead of writing an N-up of zero.
  - **It no longer applies a grid that assumes a rotated item.** The optimiser considers both item orientations, and the denser one often needs the item turned 90° — but the N-up fields and the trim describe one layout together, so writing the rotated grid while leaving the trim as authored produced a layout that fit *worse* than the one it replaced. Rotation stays what it always claimed to be in the status bar: a suggestion.
- **The sheet card now shows the sheet it holds** — name, width × height in the active unit. The dropdown was the only feedback in that card, so a sheet it could not name showed as nothing at all.

## [1.5.8] - 2026-08-03

Template suggestions stop being a distance ranking and start being a recommendation: six weighted signals, an explanation for every entry, and a memory of what this particular shop actually prints.

### Added
- **Template suggestions are ranked on six weighted factors instead of dimensional distance alone.** Distance still dominates by design — an operator picked the product their PDF *is*, not whatever packs densest — but proportion agreement, page count, sheet fit, workflow fit and how product-specific a preset is now all contribute. Every weight lives in one place as a named constant, and the six sum to exactly 1.0, which a test enforces so a future edit cannot quietly move every quality band.
  - **Sheet efficiency is deliberately barred from reordering products.** A 90×50 business card and a 90×50 roll label are the same size and gang completely differently; letting yield decide would swap the product under the operator. It contributes to the score shown on the card and to nothing else. Yield comparisons are only meaningful across sheets for *one* product — which is what the alternative-sheet panel is for.
- **Every suggestion now explains itself.** Each card carries a short checklist — "dimensions match to the millimetre", "page count suits this product", "fits your current sheet and binding", "matches your measurement system" — built only from reasons that actually hold. A checklist padded with weak entries teaches operators to stop reading it, so a preset whose proportions merely happen to agree does not claim that as a reason when the dimensions already matched exactly.
- **Alternative press sheets are suggested when they would yield materially more items.** A card whose layout leaves half the sheet empty now says which other sheet would gang it denser, with the item count and the improvement factor. **It never changes the sheet the suggestion applies** — which sheet a shop runs depends on its press, its stock and its finishing, none of which the engine can see. It makes the trade-off visible and leaves the decision where it belongs. Roll-fed products are excluded: segment length is derived, not chosen, so "a bigger sheet" is not something the operator can act on.
- **Products are categorised and the library list is grouped by category.** Business cards, leaflets, booklets, posters, labels, stickers, tickets, hang tags and the rest each get a heading, ordered by their best-ranked member so the most likely product still comes first and nothing is reordered within a group. "Show me the other labels" is a real question and it used to require reading a flat list of forty entries.
- **Four match-quality grades instead of two.** Exact / Close is still what the engine reports to any existing caller — that contract is unchanged — but the card now shows Perfect, Excellent, Good or Worth checking, so a near-tie and a stretch no longer look identical. A dimensional snap can never be graded below Excellent: being the same size as the product is the strongest evidence available, and no combination of weaker signals should talk us out of it.
- **The engine learns which preset this shop picks for a given size.** Open an 85×55 file, choose "Business card", and the next 85×55 file ranks that preset higher. Three deliberate picks for the same size is already a habit rather than a coincidence, so the bonus reaches full strength quickly — and it is capped low enough that learned habit can promote a near-tie and never override a clear dimensional mismatch. A shop that has printed a hundred business cards must still be told when the file in front of them is an A4 leaflet. Sizes are bucketed, so an 85.04×54.98 export of the same card feeds the same history entry rather than building a second half-strength one, and a rotated PDF matches the same entry.
  - **It persists.** The history is written to `suggestion-preferences.json` in the roaming profile and survives closing the app — a preference that had to be relearned every morning would be the same as no preference at all. Nothing leaves the machine, and *Settings → Suggestion learning* shows how much has been remembered with a button to forget all of it, for the shop that changes press or product mix.
  - **It can change its mind.** Counts are capped at the saturation point and every rival preset for that size loses a point when another is chosen, so switching from cards to labels at 90×50 takes the same three deliberate picks that established the old habit — rather than having to outvote a tally that has been growing for a year. A hand-edited history file cannot buy an unbeatable preference either: counts are clamped on load.

- **Creep compensation accepts grammage instead of caliper.** The paper can now be described the way it is actually quoted — "90 gsm" off the ream wrapper — rather than by a thickness nobody has measured. One or the other is authoritative, never both: picking a mode disables the other input and the imposition reads a single resolved figure, so two half-remembered numbers can never disagree.
  - **Grammage needs the paper family, and that is not a formality.** Mass per square metre says nothing about thickness on its own: 90 gsm gloss art is about 0.072 mm while 90 gsm bulky book paper is about 0.162 mm — more than twice as thick. Creep is a shift applied to page content, so a 2× error moves artwork further from correct than switching compensation off would. Five families ship with their mid-range bulk (gloss art, silk, uncoated offset, recycled, bulky), and *Custom* takes the figure from the mill's data sheet.
  - The dialog shows what the description resolves to — "0.117 mm (117 µm)" — in both modes, so the conversion is visible rather than implied, and a setup that resolves to nothing is refused at validation instead of silently imposing without compensation.
  - Existing templates are untouched: they carry only a thickness, the mode defaults to thickness, and they impose exactly as before.
- **Preflight now checks the safe zone.** Text that crosses the trim line is reported as an error; text that clears the trim but sits closer to it than the safe zone is a warning. A guillotine works to a few tenths of a millimetre and a clamped stack shifts, so text 1 mm from the edge survives the proof and loses its last character somewhere in the middle of the run — the single most common reason a job comes back. Only **text** is measured, never the combined content box: a background running past the trim is the entire point of bleed, and testing against it would raise a finding on essentially every correctly-prepared file. The template's own safe zone wins when it declares one, because a hang tag punched near the edge needs more room than a business card.
- **Preflight now checks total area coverage against the press limit.** Too much ink in one place does not dry: it sets off onto the back of the next sheet, blocks in the delivery, and on a web can pick the paper apart. It is one of the few defects that ruins the whole run rather than one copy, and nothing in preflight looked for it. A small heavy element is a warning; the same coverage over a postcard-sized area, or anything a fifth past the limit, is an error.
  - **The numbers are read from the file, not measured from a render.** The check uses the CMYK values the content stream actually declares, so a panel authored at 370% reports 370%. Recovering that from a rasterised page is not possible even in principle — rendering flattens to RGB, and converting back to CMYK rebuilds near-black as mostly K. Measured against this release's own test file, the same 370% panel comes back at about 105% through the raster route. An ink check built that way would quietly clear files that flood the press, which is worse than having no check at all.
  - **It says what it did not look at.** Placed images carry their own pixels and are not inspected, so when a flagged page also contains images the report says so rather than implying the page was cleared. The on-demand coverage scan in the file-info dialog keeps its place as a relative "which page is heaviest" signal and now states plainly that its averages under-report.
- **Preflight now checks how black is built** — two opposite mistakes, both worth a rerun. Small text set in a four-colour black shows every registration error as coloured fringing and belongs in K alone; a large solid in flat 100% K with nothing underneath dries to a washed-out, mottled grey and wants support from the other separations. Reported, never repaired: the right support values depend on the press, the stock and the ink limit, and a designer may have chosen flat K deliberately to keep a fold or a barcode clean.
- **Preflight now reports trapping — strictly what the file says about itself.** Three facts, each of which changes what happens next: whether the document declares a `/Trapped` state at all (PDF/X-1a and PDF/X-3 require an explicit True or False, and "Unknown" is reported separately from "nobody set it"); whether trap networks are already baked into the pages, which are tied to the separations and resolution they were built for and go stale when colour is converted or the job is re-imposed — and which PDF/X-4 does not permit at all; and whether a file that declares itself untrapped is carrying spot colours, since spot-to-process boundaries are exactly where a registration error shows as a white line.
  - **It does not decide where a trap belongs, and does not pretend to.** That means comparing every pair of adjacent objects for shared separations across the whole page — a trapping engine, not a preflight rule. A check that guessed at trap placement would be wrong in the direction that costs a reprint, so this one stays inside what it can actually know.
  - Single-separation work is exempt: one ink cannot mis-register against itself, and flagging greyscale jobs would be noise on exactly the jobs that can never benefit.
  - The spot-work warning is a per-profile switch, on for the three offset profiles and off for digital and large format. Shops that trap at the RIP would otherwise be told about every spot job they ever open, which is how a rule earns a reputation for crying wolf.
- **Fixed: the annotation repair could strip a file's trapping.** It cleared the entire `/Annots` array, and trap networks and printer marks live there alongside comments and form fields — so asking it to clean up mark-up could silently remove the trapping the file was prepared with, or the registration marks and colour bars placed as annotations. It now removes only interactive and mark-up annotations and keeps production ones, including any annotation it cannot identify: the cost of keeping a stray comment is cosmetic, the cost of dropping unrecognised trapping is a reprint.
- **Preflight now catches the same spot colour spelled several ways.** `PANTONE 185 C`, `PANTONE 185C` and `Pantone 185 C` in one document are three separations to a RIP — three plates and three wash-ups for an ink the shop ordered once. A file assembled from a client logo, a finisher's die line and an older panel gets there without anyone doing anything unusual, and it is normally discovered at make-ready. The stock suffix is deliberately **kept**: 185 C and 185 U are genuinely different inks, and folding them would turn a typo-catcher into a source of wrong advice.
- **Preflight now reports layers, and specifically when they disagree with themselves.** A layer switched off in the viewer can still be marked to print — which is how a "client comments" or die-line layer nobody can see on the proof ends up on the plate — and equally a visible layer can carry `PrintState /OFF`, so what was approved on screen never reaches the press. Both are reported with the layer names, the first as an error. The basic analysis has always known *whether* a file has layers; knowing *which* one and whether it prints is what an operator can act on.
- **Preflight now flags image encodings a RIP may refuse.** JPEG 2000 and JBIG2 are legal PDF and render perfectly in every viewer, which is exactly why they reach platesetting unnoticed; plenty of production RIPs still refuse them, PDF/X-1a forbids JPEG 2000 outright, and they are also the formats this app's own RGB repair cannot convert. 16-bit images are noted separately — valid, occasionally deliberate, double the data for a precision no press can hold.
- **The suggestion library takes your own products.** *Settings → Your own products* adds a signature folded card, a customer's fixed label size or the die-cut half the town orders, with its own name, size, category and press sheet. They appear alongside the built-ins, group under the same headings and take part in the same learning.
  - This closes a gap this release opened. 1.5.8 taught the ranking to learn which preset a shop prefers — but only among the forty built-ins, so a 100×210 folded card still came back as "Ticket" or "Bookmark", the operator picked the least-wrong entry, and the engine dutifully learned the wrong name and saved it onto the template. Learning preferences without being able to add products was half a mechanism.
  - Your products default to belonging to no measurement system, so they stay visible whether you work in millimetres or inches — hiding an operator's own product from them would be indefensible. Adding a product with an existing name updates it rather than creating a rival for the same size, and the name is the product's identity: renaming starts its learned history over.
- **Prepress profiles.** The thresholds preflight judges files against are now a named, editable set instead of constants in the code — ink limit, safe zone, the three resolution bands, and the colour and black policies. Five ship with the app, covering coated and uncoated sheet-fed offset, newsprint, digital and large format, with the ink limits and resolution floors each process actually calls for. A banner at 120 dpi is correct work, and judging it by the floor that suits a business card was rejecting every properly-prepared large-format job.
  - A profile describes the **press**; the template and export options still supply what belongs to the **job** — expected trim, bleed depth, PDF/X target. Keeping the two apart is what lets one profile serve every job on that press instead of being welded to the dimensions it was created with.
  - Editing a built-in and saving produces a profile of your own; the shipped ones cannot be overwritten, so a future release can still correct a threshold everywhere. Deleting the active profile falls back to the default rather than leaving preflight without one.
- **Hot folders can run the full prepress inspection**, with their own profile — the folder that accepts customer artwork can be strict while the folder fed by a known-good system stays fast. Off by default on purpose: the deep inspection re-parses every file, and 1.5.7 spent real effort removing exactly that kind of duplicate parse from the pipeline. Findings go to the log; they stop a job only when *Strict preflight* is also on, so switching the inspection on never changes what passes — only what you are told.

### Changed
- The settings dialog scrolls instead of clipping. It grew a prepress-profile card this release and no longer fits on a short screen; it still shrinks to its content when there is room.
- Suggestion cards show the match grade next to the confidence percentage, coloured from the grade rather than the raw number so the word and the colour can never disagree.
- **The suggestion engine still does no prepress validation.** Bleed, safe zones, crop marks, page errors and printability remain entirely Preflight's job — a second opinion from a size-matching engine would be a worse one, and two components disagreeing about whether a file is printable is how operators learn to trust neither.

## [1.5.7] - 2026-07-31

One place to browse a job: the sheet strip moves into the inspector, and a thumbnail click stops destroying the plan.

### Fixed
- **Preflight now reports RGB in vector artwork, not only in images.** The rule inspected image objects exclusively, so a file whose RGB lived in a logo, a tinted background or a chart — the common case — passed preflight completely clean and surprised the operator at export time. It is reported as a distinct finding, and the RGB repair handles it.
- **Applying a suggested template left the header reading "No template".** The suggestion engine built its templates with an empty name and a comment stating the UI would fill in the localised one — nothing ever did. An operator who had just picked "Leaflet A4" was told there was no template, and the imposed job still claimed none after planning. The name is now set when the suggestion is selected. It deliberately omits the dimension suffix the card shows: the template gets saved to disk, and a name baked with "in" reads wrong once the operator switches to millimetres — the dimensions live in the template itself and render in whatever unit is active.
- **Template suggestions now follow the unit preference.** A shop configured in inches was offered A4, DL and SRA3 products, even though the template editor's quick-start presets already switched to the US set on the same setting — the two halves of the app disagreed. Suggestions are now filtered to the matching family. Two deliberate exceptions keep the list useful: presets belonging to no national standard (roll labels, stickers, hang tags, tickets) always appear, and an **exact** dimensional match survives regardless of preference — a US shop opening a genuinely A4 file must still be told it is A4.
- **Tooltips and slot selection could land a few dozen pixels off for one frame.** On a frame that rebuilt the preview's scene cache, hit rectangles from the cache-drawing pass (in cache coordinates, offset by the overscan margin) were left in place alongside the correctly recomputed ones, so whichever matched first won. The stale set is now dropped before the rebuild.
- **An in-app update no longer kills the app mid-shutdown.** Accepting an update started the installer and immediately called `Environment.Exit`, which unwinds nothing: the application's exit path never ran, so pending diagnostics were dropped, the last log lines stayed in the buffer, and hot-folder jobs in flight were abandoned with their files stranded in `.work` and their ledger rows stuck at `reserved` until the next start recovered them. On top of that the installer was already running against the live process, racing its own teardown. The app now closes through the normal shutdown path — hot folders drain, telemetry flushes, logs close — and the installer is started only afterwards, against an idle installation. Its SHA-256 is re-checked immediately before launch, so a file swapped after download is refused instead of executed.
- **Feature-usage diagnostics were never actually sent.** The counters behind *Settings → Diagnostics* (export, template manager, hot folders, page edit, Save PDF, preflight) were incremented correctly and then died with the process: the only flush ran at startup — before the operator had opened anything — so every batch left the app with an empty `features` map. There was no periodic flush and no flush on exit, even though the code documented both. Counters are now pushed every 5 minutes and once more on shutdown, and a failed send hands them back to the queue so an offline spell no longer discards a whole session's usage. Empty batches are never sent.
- **Clicking a page thumbnail no longer throws away the imposition.** With a planned job on screen, a left click on a source-page thumbnail rebuilt the canvas in plain page-preview mode: the imposition disappeared, the sheet strip at the bottom of the canvas disappeared with it, and — unannounced — every manual slot edit (move / rotate / blank) was discarded along with its undo history. The click now jumps to the **sheet that page landed on**, flipping to the verso when the page only appears there, and leaves the plan and the edits untouched. A page that is not part of the plan (blanked slot, page range) reports that in the status bar instead of changing the view.
- Leaving the imposition for the plain page preview is now an explicit action ("Show source pages") that says up front when it is about to drop manual slot edits.

### Added
- **Preflight can now repair, not just report.** The file-info dialog's validation tab gained a Repairs panel offering only the fixes that apply to the file in front of you: **missing TrimBox / BleedBox**, **RGB → CMYK conversion** through an ICC profile, and **removal of annotations and form fields**. Each runs on a copy — the original is never opened for writing — and the result is written to a path you choose.
  - **The repaired file is re-checked and the outcome reports what actually changed**, listing the finding codes that are genuinely gone from the written file. A repair tool that says "3 fixes applied" is asking to be believed; this one shows the evidence, and says so plainly when a repair changed nothing.
  - Colour conversion reuses the very engine the PDF/X-4 export uses, so a repaired file behaves identically to an exported one. Without a CMYK ICC profile the repair is not offered at all rather than falling back to an approximation, and an image in an encoding the converter cannot read (JPEG 2000, 16-bit) is reported as unconverted instead of being passed off as CMYK.
  - RGB repair is offered even when the validator reports nothing: the RGB rule inspects images only, so vector artwork — a logo, a tinted background, a chart — never raised a finding. The repair scans the file itself.
  - Findings that cannot be repaired correctly in place — missing embedded fonts, low-resolution images, live transparency — deliberately have no fix. Guessing at them would put a plausible-looking wrong file on the press.
- **Hover descriptions for every production mark in the preview.** Pointing at a mark on the sheet now explains what it is and why it is there — crop marks, contour-cutter (OPOS) markers, registration marks, the control strip, fold marks, the slug line, the sheet barcode/QR, and the trim and bleed guides. Page slots already had a tooltip; the marks around them, which are the least self-explanatory things on the sheet, had none. Available in Polish and English like the rest of the interface. Marks take priority over the page slot underneath, so a hairline sitting on top of artwork is still hoverable.

### Changed
- **Sheet navigation moved from the canvas into the inspector's "Preview" tab.** The floating strip at the bottom-left of the canvas and the thumbnail grid in the inspector were two navigators that looked alike and behaved differently — one moved around inside the plan, the other tore the plan down. They are now one list behind an **Sheets / Source pages** switch, which selects Sheets automatically after planning.
- Sheet tiles carry more than a number: the sheet's filled-slot count, a `2×` badge for double-sided sheets, and an accent frame on the sheet currently displayed. The old strip had no current-sheet indicator at all, so on a long run there was no way to tell where you were without reading the toolbar counter.

- **Hot folders: the same content could be exported twice.** The ledger's "has this already been processed?" check and the reservation insert were two independent statements, so with `maxConcurrentJobs` above 1 two consumers could both pass the check for a file whose content had already been exported. The pair is now a single immediate transaction, and a unique index enforces it at the database level as well. Verified against a real database: eight concurrent attempts on an already-completed fingerprint now yield zero reservations, while thirty distinct fingerprints still proceed in parallel.
- **Hot folders: a data race could silently freeze the polling watcher.** The directory snapshot was a plain dictionary written concurrently by the polling loop and the start-up rescan for the whole life of a folder start. Concurrent writes to that type are undefined behaviour — in practice an exception, or an endless spin that pins a thread at 100% CPU with nothing in the log, surfacing as "the hot folder stopped seeing files".
- **Hot folders: a watcher could come back to life after being stopped.** The recovery path that rebuilds a `FileSystemWatcher` after an OS error did not check whether the folder had been disposed meanwhile, leaving an orphaned watcher holding a directory handle open — which also blocks a network share from being unmounted — and firing events into a dead pipeline.
- **Hot folders: one bad folder no longer breaks every reload, permanently.** A runner that failed to start aborted the whole configuration reload before the new configuration was published, leaving the internal state describing one generation and the runner list another. Every subsequent reload then failed outright until the application was restarted. Failures are now isolated per folder, as they always were on the initial start.
- **A locked `hotfolders.json` no longer stops the application from starting.** Only malformed JSON was handled; a file momentarily held open by a backup agent, antivirus or cloud sync propagated all the way out and exited the process with code 2 — before any window appeared, so the operator saw nothing happen at all.
- **PDF/X output intent could claim CMYK for an RGB or grayscale profile.** The ICC header was read with a call that is allowed to return fewer bytes than requested; the short read fell through to the CMYK default, producing a non-conformant file that passed our export and failed in the customer's RIP.
- **Grayscale conversion silently skipped images with a malformed `/Indexed` palette**, leaving colour artwork in a file that was supposed to be monochrome. The guard that the RGB converter already had is now applied here too.
- **A failed ICC profile load leaked a colour-system handle.** The constructor opened the sRGB profile first, so if the output profile was rejected the exception left the first handle open forever — no object existed to dispose it and there is no finalizer. A hot folder pointed at a bad profile leaked one handle per job, in a loop.

### Security
- **The sheet- and page-range fields could exhaust memory from a typo.** A range was expanded element by element with no limit, before any check against the document's page count could run: `1-20000000` — ten characters — allocated about 726 MB, and a slightly larger number ended the process. Ranges are now capped at 100 000 numbers with a clear message. Measured before and after.
- **Closed a path-traversal hole in marks profiles.** A template's `marksProfileRef` reached `Path.Combine` unchecked, so an imported template could point the read outside the profiles directory. Only saving validated the identifier; reading and deleting now do too, with the path builder itself as a last line of defence.
- **Updated dependencies with published advisories** — `System.Text.Json` to 8.0.5 (two high-severity) and `SQLitePCLRaw` to 3.0.4 / 3.50.3 (one high). Dependency auditing is now on for every build, in transitive mode, and the two advisories that have no available fix are listed explicitly so that anything **new** still fails loudly instead of being lost in a wall of known warnings.
- **Ghostscript is invoked through an argument list instead of a hand-quoted command line**, removing a class of bug around paths with spaces, UNC paths and trailing separators.
- **An environment variable can no longer downgrade the licence or telemetry endpoint to plain HTTP.** Those requests carry the machine fingerprint; only HTTPS, or loopback for development, is accepted.
- Both network services now share one HTTP client per process instead of creating one per request, which left sockets in `TIME_WAIT` and forced a fresh TLS handshake every time.

### Performance
- **Hot folder throughput: two WMI queries removed from in front of every job.** The per-job licence check re-read the disk serial and motherboard UUID each time — `Win32_DiskDrive` alone routinely costs hundreds of milliseconds, seconds on a machine with parked disks. A 500-file batch paid over a minute of pure overhead, and application start-up paid it again before the window appeared. Hardware cannot change while the process runs, so the fingerprint is cached; the licence signature and expiry are still verified for every job.
- **The same PDF is no longer parsed two or three times per export.** Analysis — the most expensive operation in the application — ran when the file was added, again for preflight and again inside the export pipeline. Results are now reused for as long as the file on disk is unchanged.
- **Colour conversion caches per-colour results.** Every `rg` operator in a content stream previously cost a separate call into the operating system's colour engine plus four array allocations; a real document uses a few dozen distinct colours.
- **Image conversion reads pixels by row rather than one at a time**, and decodes without the alpha channel it immediately discarded — 12 million redundant bounds checks and a 25% larger buffer on a single 4000×3000 photograph.
- **Diagnostics no longer read the settings file on every event.** The opt-out flag was loaded and deserialised from disk on each check, including from the UI thread.

### Removed
- The floating sheet strip and the `ShowSheetStrip` flag that gated it.
- Seven unused public members, among them the only renderer entry point that rasterised synchronously — it bypassed the concurrency gate, so any future use would have frozen the interface on a large page.
- Four directories that were created at every start and never written to, one of which implied support for custom language packs that does not exist.

## [1.5.6] - 2026-07-25

DeviceRGB image conversion in the imPRESS engine — PDF/X-4 from RGB files without Ghostscript.

### Added
- **RGB images no longer force the Ghostscript path.** The imPRESS engine now converts DeviceRGB images to DeviceCMYK itself, through the very ICC profile that becomes the OutputIntent (Windows colour engine, relative colorimetric). Handled: **JPEG (DCTDecode)**, **8-bit Flate/raw rasters** and **Indexed palettes** with an RGB base — converted pixel-by-pixel to 8-bit DeviceCMYK with Flate compression, in bounded chunks so peak memory stays flat.
- **Same fail-safe philosophy:** exotic flavours (JPEG 2000, 16-bit) are never guessed — if any RGB image would be left unconverted the engine aborts with a clear message instead of shipping a file with a false conformance claim. Exotic `/DeviceRGB` usage (cs/CS operators, shadings) still routes to Ghostscript.
- Verified end-to-end: a 4-page file of RGB photos → native PDF/X-4 with every image in DeviceCMYK, colours and printer marks intact (control render), PDF 1.6 header + GTS_PDFX + OutputIntent.

### Fixed
- **The app no longer stays closed after an in-app update.** The updater runs the installer silently, but the installer's "launch the app" step was flagged to be skipped in silent mode — so an update replaced the files and left the user with nothing running. The installer now relaunches the app after a silent install (at the original user's privilege level), while the interactive wizard keeps its usual final-page checkbox. Verified by installing an isolated probe package with `/SILENT` and confirming the payload was executed.
- **PDF/X-4 export failed with "select an ICC profile" — after the sheets had already been composed.** The job was imposed and written, then packaging aborted if no profile was selected in the options. The engine now falls back to a bundled production profile (PSO Coated v3 → ISO Coated v2 → any CMYK profile in the profile directories), logs the choice and completes the export. A hard error remains only when the machine has no CMYK profile at all.

## [1.5.5] - 2026-07-19

Anonymous, opt-out diagnostics + small UI fixes.

### Added
- **Lightweight anonymous diagnostics** to help improve the app. Sent only: a *random install id* (a locally-generated GUID — not the hardware fingerprint and not linkable to it), app and OS version, .NET version, license type, locale, startup time and coarse module-usage counters (export, template manager, hot folders, page edit, Save PDF, preflight — counts only).
- **Nothing can leak by construction:** the app only ever sends a fixed, predefined set of fields — never file names, paths, project names or content, typed data, per-click history or crash data (crash reporting is deliberately omitted).
- **Full control:** on by default, with a one-time first-run notice and a toggle under *Settings → Diagnostics*. Sending is backgrounded with a short timeout and never blocks the app; when offline it is simply skipped. Privacy policy updated (PL/EN). New trial-server endpoint `POST /api/telemetry` with dedicated tables and a storage smoke test.

### Fixed
- **Empty startup preview** no longer shows a misleading "SRA3 · 450 × 320 mm" (that was only a fallback shown when no template is loaded) — replaced with a neutral "Load a file and pick a template" hint; the real sheet size appears once a template is active.

## [1.5.4] - 2026-07-19

Zero-click 30-day trial on first launch.

### Added
- **The 30-day trial starts automatically on first launch — no form, no clicking.** With no valid license the app sends *only its hardware fingerprint* (MAC/disk/UUID hashes — no personal data, no e-mail, no consent form) to the license server and receives a signed 30-day trial that is saved automatically.
- **One trial per machine — a Windows reinstall doesn't help.** The "one trial per machine" record is server-authoritative and keyed on the hardware fingerprint, so reinstalling Windows or formatting the disk cannot reset the 30 days. A component-level dedup also catches a single-part swap (e.g. a new NIC).
- **Only the first launch needs internet;** the saved license then works offline (with the existing clock-rollback guard). No connection on first run → a clear message and manual activation; trial already used → an info note and a path to activate a paid license. The web form and manual activation remain available as a fallback.
- New trial-server endpoint `POST /api/auto-trial` (anonymous, fingerprint-only) plus a component-level dedup query; covered by a standalone dedup smoke test.

## [1.5.3] - 2026-07-14

UX fix pack: clean start with no template, an honest status bar, tabbed file info.

### Fixed
- **No template forced at startup.** The app no longer auto-loads the bundled "A4 saddle-stitched booklet" sample — the user picks a template (manager, suggestions or a file). Auto-load still honours a deliberately saved default `template.json` in the user profile.
- **Honest status bar.** Removed the always-green "Validation OK" pill (nothing had been checked yet) and the static "3.0 mm · CMYK · 300dpi" decoration shown before anything was loaded. The bleed pill now appears only when a template is active and shows its real bleed.
- **"Duplex: true" in the Polish UI** — booleans in the inspector now render as localized "Tak / Nie" (Yes / No) via a shared converter.
- **"Avalonia.Data.Binding" rendered instead of an empty state.** Four spots (the Template step tile, the inspector, the template-manager header) used `FallbackValue={loc:Loc …}` — a markup extension there yields a Binding OBJECT, and when the fallback fired the UI rendered its ToString(). A new `LocFallbackConverter` shows the localized placeholder ("no template" / "not saved") instead.

### Added
- **File → "Save PDF…" with export options.** Saves the selected source file (with page edits — rotation, grayscale) without imposition. A new options dialog offers the same choices as the imposition export: **format** (plain PDF / PDF/X-1a / X-3 / X-4), **engine** (Auto / imPRESS Export Engine / Ghostscript), **PDF version** (1.4–1.7) and **ICC profile**. Plain save is a copy with edits (optionally just re-stamping the version header); PDF/X produces a print-standard file — X-4 natively via the imPRESS engine, X-1a/X-3 via Ghostscript. Settings persist separately from the main export; the original on disk stays untouched.
- **Source-file pill.** After a file is analyzed the top bar shows its real parameters: PDF version, colour spaces (CMYK / RGB / CMYK+RGB) and — when the file contains images — the lowest effective resolution ("PDF 1.7 · RGB · min 287 dpi"). DPI is a free by-product of the standard analysis pass (pixel samples ÷ placed size), no extra scan.
- **"Preview" inspector tab — page thumbnails with edits.** A new first tab shows thumbnails of every page of the loaded file — **no page cap**. Bitmaps exist only for thumbnails inside a sliding visibility window (viewport + margin, ~90 items): scrolling moves the window and thumbnails outside it release their references, so a 1000-page book costs the same memory as a leaflet, and revisited pages come straight back from the renderer cache. Superseded edit working copies are cleaned from the temp directory. Right-click opens the edit menu: **rotate 90° CW / CCW / 180°** (lossless /Rotate flip), **grayscale this page** and **grayscale the whole file**. Non-destructive: every edit produces a temp working copy — the original PDF is never touched; analysis, preview, merge, imposition and export automatically use the edited version; a banner above the thumbnails flags active edits and "Revert file edits" restores the original. Edits chain (two 90° turns = 180°).
- **Thumbnails tab appears only after a file is loaded; clicking a thumbnail shows that page on the main canvas** (with an active imposition the canvas switches to the page preview — the plan survives, "Plan" brings the sheets back). Since thumbnails now navigate pages, the bottom page strip on the canvas shows **only for a planned imposition** (sheet numbers) and disappears in the page preview.
- **Tolerant stream decoder.** Some design-tool PDFs carry Flate content streams terminated without a final block — PdfSharp's strict inflater threw "Unexpected EOF" on them and the grayscale conversion crashed. The engine now avoids the eager bulk decode and falls back to a tolerant .NET inflate that keeps the decoded prefix — exactly what RIPs do. Regression-tested.
- **Fading page-number badge.** The "n / N" numbering stamp on the preview canvas now shows for 2 seconds after every page/sheet change and fades out smoothly — it no longer permanently covers page content. The export itself still prints the stamp at full ink.
- **Grayscale runs entirely on the imPRESS engine — no Ghostscript.** Vector colour operators (RGB and CMYK) in page and form content streams are rewritten to luminance gray; images (JPEG/DCT, 8-bit Flate RGB/CMYK rasters, Indexed palettes) are converted pixel-by-pixel to 8-bit DeviceGray with Flate compression. Single-page edits convert the extracted page in isolation and splice it back, so images shared with other pages stay untouched. Exotic image flavours (JPX, 16-bit) are left in colour rather than risking a corrupt file. Verified end-to-end with renders.

### Changed
- **File info window redesigned.** Tabs instead of expanders — Overview (file, metadata, properties) / Validation / Colour & PDF/X (standard, OutputIntent, spot colours, transparency) / Page content (boxes, images, fonts) / Ink coverage — the same tab pattern as the main-window inspector. All hardcoded light-theme colours replaced with theme-dictionary brushes, fixing the dialog in dark mode.

## [1.5.2] - 2026-07-14

Hot folders 2.0: imPRESS engine support, template picker, a simpler dialog; complete page-box set on exported sheets.

### Added
- **imPRESS Export Engine in hot folders.** Per-folder PDF/X engine selection (Auto / native / Ghostscript) and output PDF version for plain exports (1.4–1.7) — the same options as the export dialog, persisted in `hotfolders.json`.
- **Template picker.** Instead of typing a path, pick a template from the saved-templates list (the same library the template manager uses); a "From file…" button and an editable path field remain for out-of-library templates.
- **Simpler edit dialog.** The basic flow is name → input folder → output folder → template → export; work/archive/error folders (auto-created), file filters, success/failure behaviour, the naming pattern and the Ghostscript path moved into an "Advanced" expander.
- **Zero-config Ghostscript.** A path is no longer required for PDF/X — the pipeline resolves the bundled copy itself (settings → bundled → Program Files); applies to the GUI and CLI too.

### Fixed
- **Exported sheets now declare the full page-box set** (CropBox = BleedBox = TrimBox = MediaBox, including the cutting-guide page) — re-importing our own output used to trigger "BleedBox missing" warnings and a "document declares no bleed" error. Deep validation additionally recognises imPRESS-produced sheets (via the Producer field) and shows a single info note — the sheet is the final format; item bleed lives inside it — instead of false bleed errors.

## [1.5.1] - 2026-07-13

Fix pack after 1.5.0: localized preflight, no more silent manager ↔ job template divergence, About window sizing.

### Changed
- **Quick export preflight messages moved to the localization system** (Polish/English following the app language): `BLEED_LOW`, `RGB_ONLY`, `TRIM_MISMATCH`/`TRIM_ROTATED`, `SIGNATURE_PADDING`, `SADDLE_TOO_THICK`, `DUPLEX_ODD_PAGES`, `ENCRYPTED`, `NO_ICC`, the PDF/X rules and the rest. The deep prepress validation already had localized categories and recommended actions.

### Fixed
- **3 mm bleed silently became 0 on every "Use template" (the root cause of the recurring `BLEED_LOW` reports).** The main window's status pills (bleed, gutter, sheet size) bound `Run.Text` to template fields — and Avalonia's `Run.Text` binds two-way by default (WPF heritage). On a `CurrentTemplate` swap the stale displayed value was written *into the freshly applied template*: the preset carried 3 mm, the job received 0. Reproduced with a headless-UI test using the exact pill markup; fixed by forcing `Mode=OneWay` on every display-only field, with a regression test guarding the pattern. New template-chain diagnostics (preset → commit → adopt → plan) made the bisection possible and stay in the app.
- **"Template in the manager ≠ template of the job" clarity.** Closing the manager without "Use template" now prints which template stays active in the status bar, and the `BLEED_LOW` message names the template being judged and points to where to fix it.
- **Shared template reference.** The template editor (a singleton) handed the main window its live `Template` reference — a later manager session (even one abandoned with Cancel) could silently mutate the active job's template. "Use template" now hands over a deep copy.
- **`RGB_ONLY` message clarified:** it now states that PDF/X exports convert colours automatically (imPRESS engine / Ghostscript) while a plain export keeps RGB values unchanged.
- **About window:** a fixed height clipped the footer with the Close button; the window now sizes to its content.

## [1.5.0] - 2026-07-13

The imPRESS Export Engine release: native PDF/X-4 with ICC colour conversion — no Ghostscript required — a native ink-coverage scanner, and a redesigned template manager.

### Added
- **imPRESS Export Engine — native PDF/X-4 without Ghostscript.** A new in-house packaging engine embeds the OutputIntent with the selected ICC profile, full XMP metadata (`pdfxid:GTS_PDFXVersion`, IDs, dates, title), `/Trapped`, a TrimBox on every page and the PDF 1.6 version stamp — entirely natively. Honest scope: the engine packages print-ready files (CMYK/ICC) and converts vector RGB (below); transparency flattening, RGB images, and PDF/X-1a / X-3 remain the Ghostscript engine's job.
- **ICC colour conversion with no external libraries.** Vector RGB fills and strokes in the composed file are rewritten to DeviceCMYK through the selected ICC profile using the Windows colour engine (WCS/mscms, relative colorimetric intent). Vector-only RGB artwork no longer forces the Ghostscript path — the native engine converts it itself and reports the conversion in the export log.
- **Two-stage conformance gate.** Source analysis (RGB text/images, encryption, raster logo overlay) **plus** a deep scan of the composed file's decoded content streams and form XObjects for DeviceRGB operators. Vector RGB → native conversion; RGB images or an unreadable stream → Ghostscript. Fails safe: doubt never ships a non-conformant file with a false conformance claim.
- **Engine selection in the export dialog and CLI** (`--engine auto|native|gs`): Auto picks the native engine whenever the file qualifies; forcing native on a file with RGB images produces a clear message instead of a silent conformance failure.
- **Output PDF version selection (1.4–1.7)** for plain exports (`--pdf-version` in the CLI, a combo in the dialog). PDF/X modes pin the version their spec requires (X-1a/X-3 → 1.4, X-4 → 1.6).
- **All composer-drawn ink switched to DeviceCMYK** — backgrounds, sheet numbers, cutting guides, barcodes — fixing a PdfSharpCore trap where the default colour mode rewrote every colour as RGB. Verified end-to-end: both a CMYK job and a vector-RGB job package natively and render correctly (reds converted per SWOP); conformance markers (GTS_PDFX, XMP, OutputIntents, TrimBox) confirmed in the outputs.
- **Native ink-coverage scanner.** "Scan ink coverage" in the file-info window now runs on the imPRESS engine: pages are rendered by the internal rasterizer and pixels converted to CMYK through the export ICC profile (SWOP fallback). Ghostscript is no longer needed for the scan; the results (average C/M/Y/K + Σ, 250% warning threshold) are honestly labelled a composite page average, not separation densitometry.

### Changed
- **Template manager redesigned** to match the main window's visual language: a header bar with the template name, an active-binding pill and save actions; the editor split into "General" / "Marks" / "Binding & special" / "Extras" tabs (the same pattern as the main-window inspector) instead of one long scrolling form. Binding-specific cards (saddle, gatefold, fold panels, roll, Step & Repeat, creep) appear only when the chosen binding actually uses them; an enabled roll section never disappears, and a binding with no special options gets an explanatory empty state. The validation banner sits above the tabs so errors are visible from any tab. Every existing function is preserved.

### Fixed
- **Export metadata:** output files now sign themselves "imPRESS Studio" (Creator) and "imPRESS Export Engine" (Producer) — previously the internal module name "Imposition.App" leaked into the Creator field.
- **Confusing `X4_VERSION` preflight note** when exporting with PDF 1.7 selected: the report now states clearly that PDF 1.6 applies only to the PDF/X-4 copy (`.pdfx4.pdf`, an ISO 15930-7 requirement) and the main export keeps its own version setting.

### Tests
- 282/282 passing, including packaging read-back (version stamp, TrimBox, conformance markers via PdfPig), composed-RGB scan classification, ICC transform sanity on SWOP primaries, and a decoded re-scan proving all `rg`/`RG` operators are rewritten after conversion.

## [1.4.8] - 2026-07-06

Major release: ticket numbering, work-and-turn, front/back registration preview, sheet advisor, ink-coverage scan, PDF/X-3, Preflight 2.0, CLI parity and a batch of fixes.

### Added
- **Ticket numbering (variable data lite).** A consecutive number on every item — tickets, coupons, raffle stubs: start number, step (negative allowed), .NET format with the `{0}` token (e.g. `"No {0:0000}"`), 9 anchor positions, offsets and font size. The sequence is continuous across sheets (sheet order, then slot order), stamped on fronts; the preview shows exactly the numbers the export writes. New "Ticket numbering" section in the template editor, format/step validation, verified end-to-end with a rendered 15-up sheet.
- **Work-and-turn duplex mode (Ganging).** One form carries the fronts on the left half of the grid and the backs on the right half at exactly mirrored positions; the sheet is printed, turned left-to-right and printed again with the same form — **half the CTP plates**, and every cell yields a complete duplex product after cutting. Mode selector (Sheetwise / WorkAndTurn) next to the Duplex checkbox; validation enforces an even N-up X. Mirror geometry pinned by tests and verified visually.
- **Front/back registration preview.** A new canvas toolbar toggle overlays the back side **mirrored** at 50% opacity on the front — like holding the sheet against the light. Front/back layout misregistration is visible instantly; the overlay registers no hit areas (clicks still select front slots) and cooperates with the scene cache.
- **Sheet advisor.** "Compare sheets…" in the template editor ranks every paper size by utilisation for the current item (bleed/gutter/margin honoured, 90° item rotation considered): "SRA3: 15 pcs · 86% · 3×5". Pure math on the layout optimizer (new `SheetAdvisor` in Core, with tests).
- **Ink-coverage scan (inkcov).** An on-demand Ghostscript scan in the file-info window: average C/M/Y/K coverage per page plus the Σ total, pages above 250% flagged ⚠. Honestly labelled as a page average (not a per-pixel maximum) — no fake TAC guarantees.
- **PDF/X-3:2003 export** — the third target next to X-1a and X-4 (PDF 1.4, ICC-based colour, no live transparency); full chain: export dialog, PDFX_def, GS arguments, `.pdfx3.pdf` suffix, preflight rules (`X3_TRANSPARENCY`, `X3_VERSION`), hot folders and CLI.
- **CLI parity with the GUI:** new `impose` options `--pdfx none|x1a|x3|x4`, `--icc`, `--sheet-range`, `--page-range`, `--numbering`, `--strict-preflight` (legacy `--pdfx4` still works).
- **Hot folders:** per-folder sheet numbering and cutting-guide options (two new checkboxes in the edit dialog + `hotfolders.json` fields).
- **Ctrl+Z / Ctrl+Y for slot edits** — the previously unused `UndoRedoStack` is now wired to the inspector: every slot edit (move/rotate/blank) is undoable; the stack clears on a new plan.
- **Custom paper sizes survive restarts** — persisted in settings.json and re-registered at startup (they used to live only in process memory).
- **License activation dialog:** a "Renewal & transfer" section — a pre-filled renewal e-mail with the machine fingerprint, and local deactivation (with confirmation) as the first step of a license transfer.

### Fixed
- **Preview bitmap eviction race** — evicted bitmaps are now disposed on the UI thread (between frames); no more `ObjectDisposedException` risk during aggressive zooming of large jobs.
- **Eyedropper UI hitch** — sampling now reads only cached bitmaps (no synchronous render on the UI thread).
- **Step & Repeat duplex:** backs are mirrored across the sheet width with rotation pairing (AlternatingColumns/Checkerboard with even column counts now register front-to-back) and carry the back-side flag.
- **Working merge moved to the temp directory** — no more `merged_imposition.pdf` silently overwritten next to the user's source files; the default output path still lands next to the sources, and the previous working merge is cleaned up.
- **"Page range × slot edits" warning** — exporting with a source page range now states explicitly (status + history) that manual slot edits will not be part of that export.
- **Event-subscription leaks** — the main and preview view-models implement `IDisposable` and detach from long-lived services (renderer, units, localization) when the window closes.

### Also in this release (preflight & suggestions)
- **Page-count-driven template suggestions.** The suggestion library previously knew only flat products and rolls — a 16-page A5 PDF was suggested a "flyer". The engine now honours the source page count: **≥ 5 pages** surfaces **saddle-stitched A4/A5/A6 booklets** (2-up spread on SRA3, 8-page signature, duplex), **49+ pages** adds **perfect-bound A4/A5 catalogues**, and a document of **exactly 5–6 DL pages** proposes a **Z-fold DL leaflet** with correct fold geometry (panels meeting at the fold lines: zero bleed/margin on a landscape A4 sheet). Each carries a dedicated rationale explaining why it appeared. On a dimensional tie the page-count-justified product outranks the flat one, and fixed-capacity products (Z-fold = 6 pages) hide for longer documents.
- **New flat presets:** square flyers 148×148 and 210×210 mm, a 50×200 mm bookmark, and a B2 poster (1-up on a B2 sheet).
- **Deep preflight — new "Page content & interactive elements" category:**
  - `TEXT_TOO_SMALL` — glyphs below 4 pt (sizes read from the transformed text matrix, so content-stream scaling cannot fool it); reports the smallest size found.
  - `TEXT_REGISTRATION_BLACK` — text filled with C=M=Y=K=100%: 400% ink and coloured fringes on misregistration; body text should be K-100.
  - `HAIRLINE_STROKES` — paths stroked with an explicit width of 0 ("0 w") that vanish on the platesetter. Only the literal zero is flagged — the one value that survives any content-stream transform (PdfPig exposes raw, untransformed widths), so no false positives.
  - `BLANK_PAGES` — aggregated info listing pages with no text or images.
  - Annotation census (`ANNOTATIONS_MARKUP` warning for comments/stamps, `FORM_FIELDS` warning for AcroForm widgets — also when declared only in the catalog, `ANNOTATIONS_LINKS` info for harmless hyperlinks) via a new per-page annotation walker.
- **Quick preflight — binding and orientation diagnostics:** `TRIM_ROTATED` (pages matching the template trim only after a 90° turn get one aggregated hint instead of per-page mismatch warnings), `SIGNATURE_PADDING` (how many blank pages the signature will add), `SADDLE_TOO_THICK` (saddle stitch above 64 pages — consider perfect binding), `DUPLEX_ODD_PAGES` (odd page count in duplex ganging → empty last back).

### Changed
- Stricter template validation: negative spine width rejected; a safe zone consuming the whole item (2×safe ≥ shorter trim side) reports a clear error; legacy inline marks get numeric sanity checks (zero crop length, negative offsets, zero OPOS marker size while enabled).

### Tests
- 275/275 passing (40 new in this release), including an end-to-end fixture: a real PDF authored with 3 pt text, registration-black text, a zero-width stroke and a blank page runs through the deep analyzer and every new check fires. Ticket numbering, work-and-turn geometry and the new CLI options verified visually via CLI + Ghostscript renders.

## [1.4.7] - 2026-07-04

Full-code review release: correct scale for 90° roll rotation, versos survive Duplex=off, export matches the preview 1:1, one coordinate frame for `/Rotate` pages.

### Fixed
- **Roll-fed auto-rotation rendered content at the wrong scale.** The composer and the preview fitted the *unrotated* page into the rotated slot before spinning it — a non-square item (e.g. a 100×50 mm label) landed at `min(w/h, h/w)` scale, i.e. half size for 2:1, with white gaps inside every slot. Fitting now targets the slot with its dimensions swapped back (same rotation pivot), so the trim fills the slot exactly. Fixed in both `PdfComposer` and the preview canvas; verified visually on a rendered 6-up roll.
- **Unticking "Duplex" on saddle-stitch/perfect-bound/folded work silently dropped every verso from the export.** Double-sided binding strategies always plan back sides, but the composer skipped them when `Duplex=false` — the preview showed the full booklet while the exported PDF contained fronts only. Single-sidedness is now decided solely by the strategies (empty back placements); the composer renders every side the plan carries.
- **Manual slot edits (move / rotate / blank in the inspector) were silently discarded at export.** The export pipeline unconditionally re-planned the job, so edits lived only in the preview. Edited sheets are now written back to the job before export, and the pipeline plans only jobs without an existing plan (CLI, hot folders and the source-page-range path still plan as before). Note: after changing *template* parameters, click "Plan imposition" again before exporting.
- **Pages with `/Rotate` — one coordinate frame for geometry and preflight.** `PdfBoxParser` mixed two coordinate systems: MediaBox/CropBox arrived from PdfPig already rotated (visual frame) while TrimBox/BleedBox/ArtBox were read raw from the page dictionary. Consequences for `/Rotate 90/270` files: false `TRIM_MISMATCH` warnings, wrong bleed-fit geometry in composition and preview (double width/height swaps), and skewed bleed-validator comparisons. The parser now maps every box into the visual frame (90°/180°/270° transforms verified empirically against PdfPig glyph coordinates) and all consumers use the dimensions directly.
- **Memory leak in the preview with an active CMYK channel filter / overprint simulation:** the filtered-bitmap cache grew without bound while zooming (each zoom bucket added a multi-megabyte copy). The cache is now bounded (FIFO, 8 entries, evicted bitmaps disposed) and cleared on every job change; a dead guard in the disposal path was removed.
- **`PdfPreviewRenderer.Dispose()` destroyed the process-wide PDFium singleton** (`DocLib.Instance`), tearing the native library down under in-flight renders and any other consumer. The renderer no longer disposes the singleton.

### Changed
- Verso placements in Step & Repeat and Ganging finally carry `IsBackSide=true`, consistent with the remaining strategies (Wiro, Roll-fed, saddle stitch).
- Saddle-stitch validation now accounts for bleed on the outer sheet edges (2×bleed per dimension; the spine and inter-copy cut lines are unchanged). Templates sized with zero bleed allowance will now fail the sheet-fit check.
- Documentation: the marks-profile resolution order (`MarksProfileResolver`) and the accordion scheme in `FoldingStrategy` now match the actual, test-pinned behaviour.

## [1.4.6] - 2026-06-27

Resilience fixes: backup restore after a failed export, protection against malformed base64 activation keys, narrowed catch blocks in ICC and TrimBox readers.

### Fixed
- **Output file loss on a failed rename.** `PdfExporter` backed up the existing target (`.bak`) before the atomic rename; if the rename failed (disk full, antivirus lock), the catch block deleted only the temp file and never restored the backup. The backup is now restored automatically and any restore failure is logged separately.
- `LicenseValidator.SaveLicense` threw an unhandled `FormatException` on activation keys with invalid base64 characters (clipboard corruption, truncation). The decode is now guarded and raises a descriptive error instead.
- `ExportPipeline.ReadIccChannelCount` and `SourceTrimReader.Read` had bare `catch {}` blocks that swallowed even `OutOfMemoryException`/`StackOverflowException`; both now exclude critical exceptions from the fallback path.

## [1.4.5] - 2026-06-15

### Added
- **US template presets when units are set to inches.** The "Ready presets" list follows the active unit: ISO set (A4/A5/SRA3/DL) for millimetres, US set after switching to inches — Letter/Half-Letter saddle booklets, Letter perfect bound, Letter/Half-Letter ganging, Letter tri-fold (11×17), 4-panel accordion, 3-up rack cards (3.5×8.5″), 6/8/12-up US business cards (3.5×2″), 2×2″ roll stickers and Step & Repeat. US geometry throughout: 0.125″ bleed, 0.25″ safe zone and sheet margin, 13×19″ working sheet. Paper library gains `Half Letter`, `12×18` and `13×19`.

### Changed
- **The source page range resets whenever a new source is loaded.** Previously a range set for one file could silently carry over to the next job and truncate or overrun it. Persisted export settings (Ghostscript, ICC, PDF/X) are untouched.

## [1.4.4] - 2026-06-14

### Added
- **Grayscale (K-only) step wedge** on the second half of the marks edge: 8 patches in 12.5% steps from solid black to white, for tonal-reproduction and dot-gain control on the black separation. Preview draws both bars exactly as the exported PDF does.

### Changed
- **The CMYK control bar no longer overlaps the centre registration mark** — it is now centred on the first half of the edge (¼ of its length) instead of the full edge.

## [1.4.3] - 2026-06-13

### Added
- **Warning when the tracking barcode / QR overlaps printed content.** Overlap geometry is computed by the shared `BarcodeMarkLayout` used by both export and preview; an overlapping code is outlined with a red dashed frame on the canvas and flagged in the status line after planning.

### Changed
- **Per-type mark toggles in the preview, driven by the template.** The single "Marks" button was split into separate view filters (crop, registration, fold, slug, barcode/QR); each toggle is bounded by the template's marks profile — a mark type disabled in the template disappears from the preview automatically, and the profile re-resolves live after template edits.

## [1.4.2] - 2026-06-13

Major production release: page/sheet ranges, cutting guide, tracking codes, new binding layouts — plus hard imposition and hardening fixes.

### Added
- **Two independent export ranges:** a *source page range* (which source pages get imposed — applied before planning via a temp subset) and an *output sheet range* (which finished sheets get written — reprint a single damaged sheet without exporting the whole run). Shared `RangeExpression` parser validates expressions up front.
- **Cutting guide for the guillotine:** an optional page appended to the PDF with a sheet schematic and a numbered knife-position table in millimetres (vertical cuts from the left edge, horizontal from the top).
- **Barcode / QR tracking code** in the marks profile — Code 128 or QR with job metadata (same placeholders as the slug: `{jobid}`, `{sheet}`, `{sheets}`, …).
- **Saddle-stitch sheet layouts:** classic TwoUp, StackedCopies (N spread copies in rows — cut into strips then fold; digital workflow) and the 8-page signature (2×2 form, top row rotated 180°, folded twice).
- **Gatefold styles:** 3-panel (6 pages) and 4-panel (8 pages) with a configurable fold reduction so the inward-folding wings do not collide at the centre.
- 231 unit tests passing (range parser, page subset extraction, PDFX_def generator, mark layouts, binding fixes, and more).

### Fixed
- **PDF/X export produces a conformant OutputIntent and actually uses the selected ICC profile** (previously a dead option — the Ghostscript arguments were incomplete). Also fixed: a Ghostscript stdout/stderr pipe deadlock (streams now drained concurrently), `--permit-file-read` for GS 10.x SAFER mode, orphaned GS processes on cancel, and the PDF/X output file naming.
- **Creep compensation direction and sidedness** — the maximum shift previously landed on the *outer* sheet (inverted gradient); both leaves of each folded sheet now shift toward the spine correctly on front and back.
- **Z-fold back-side page order** is now [5, 6, 1] (was [6, 1, 5], a physically impossible leaf pairing).
- **Roll-fed duplex backs are mirrored across the web width** with the correct side flag, so multi-design runs register back-to-front.
- **Wiro supports duplex** (previously always single-sided); adjacent pages share a leaf with a mirrored verso.
- License-key validation runs fully in memory (no more temp-file writes to `%TEMP%`); downloaded update installers are verified against the GitHub asset SHA-256 digest before launch; sheet numbering `{1}` shows the true planned sheet total; cancellation-token lifecycle fixed in the main window; the composer no longer re-parses the whole source PDF just to read its page count; marks profiles store enums as readable names.

## [1.4.1] - 2026-06-10

### Fixed
- **Bleed now extends past the cut line, as prepress requires.** The composer used to scale the whole page (bleed included) *into* the trim cell, shrinking the trim and leaving white gaps between items instead of overlapping bleed. A single geometry helper (`BleedFit`, trim→cell mapping) is now shared by the export composer and the preview canvas, so what you see is what prints. Preview clips excess bleed at the sheet edge like the output does.
- **False `TRIM_MISMATCH` preflight warnings** — the analyzer reported the CropBox as "trim"; for correctly-made files with bleed, CropBox = trim + bleed, so every good file warned. The real TrimBox is now read (with the spec-mandated Trim→Crop→Media fallback).
- The preview ruler now respects the light theme.

### Added
- First regression coverage of the PDF composition layer: `BleedFit` unit tests plus a differential composer test (compose → rasterise → assert against the source raster).

## [1.4.0] - 2026-06-07

### Added
- **Operation history (the "History" tab)** in the right inspector: session journal of loads, plans, exports, edits — each entry stores a restorable workspace snapshot. Hovering an entry reveals **↺ restore**, which brings back the entire project state from that point (files, template, layout, export options). Entries are stored as localisation keys, so descriptions switch language live; the list is capped at 200 entries.
- Collapsible inspector panel — a slim chevron handle gives the preview the full window width.

### Changed
- Activation window polish: semantic status colouring (green active / red invalid), theme-aware cards, and the `.lic` file loader moved above the key field as the primary path.

## [1.3.6] - 2026-06-07

### Fixed
- Wrong "license control file was modified" lockout when switching licenses on the same machine (trial → paid, renewal). The clock-rollback stamp is HMAC-keyed per license, so a stamp from a *different* license is now treated as first-run instead of tampering.

### Added
- Restart reminder after loading a key or license file; license-server admin panel gained "Download .lic" and "Copy key" actions.

## [1.3.5] - 2026-06-06

### Added
- **Machine-ID export to a `.lic` file** from the activation window — upload it to the trial-license generator instead of copy-pasting the fingerprint.
- **PDF merge ("Merge…")** in the source panel: combines two or more checked files into a single PDF on disk, with a suggested `merged_<date>_<files>_<pages>.pdf` name.

## [1.3.4] - 2026-05-24

### Added
- **Step & Repeat as a dedicated binding:** replicates one page across an N×M grid with four per-copy rotation patterns (none / alternating rows / alternating columns / checkerboard at 180°) and two spacing modes (gap-based like ganging, or centre-to-centre pitch for machine-defined constants). Validation rejects pitches smaller than the item (overlapping copies). Ships with a quick-start preset and 7 new tests.
- **Marks v2 — named marks profiles** instead of loose checkboxes: a profile bundles crop / registration / control bar / fold / slug / contour-cut settings as a single JSON file. Control bar in four variants (CMYK process, CMYK+tints, GATF-11, Brunner-21); registration marks in four styles × three positions; slug line with `{filename}`, `{date[:format]}`, `{sheet}`, `{sheets}`, `{side}`, `{jobid}`, `{template}` placeholders; per-mark ink choice (process black vs. registration black). Fully backward compatible — v1.3.x templates render identically via a legacy adapter.
- **In-app marks profile editor** (list, edit, save-as, reset built-ins, delete) with export and preview consuming the same resolved profile.

### Changed
- Built-in template presets and marks profiles now have localised display names (PL + EN).

## [1.3.2] - 2026-05-23

### Changed
- **Full mm / inch support across the UI.** The model stays metric; conversion happens only at the UI boundary. All ~20 dimension fields in the template editor, the selection inspector, toolbar/status readouts and dialogs display and accept the active unit, with dynamic unit labels and live refresh on unit change.

### Added
- **ICC profile details in File Info:** the OUTPUT INTENT section now shows real embedded-profile data (colour space, profile name, registry) via a new minimal ICC parser; indirect `/DestOutputProfile` references are resolved and decoded.
- Bundled-ICC plumbing: every `.icc`/`.icm` dropped into the app's `icc/` folder ships with the installer and appears in the export dialog.
- US/ANSI paper presets (ANSI A–E, aliases for Letter/Tabloid) and six US-format template suggestions (Letter/Half-Letter/Quarter-Letter flyers, Tabloid/ANSI C/ANSI D posters).

## [1.3.1] - 2026-05-21

### Changed
- Topbar action buttons: "Plan" got a distinct light-blue style; "Export" text forced to white for full contrast on the accent background.

## [1.3.0] - 2026-05-18

### Fixed
- **Drag-and-drop from Explorer finally works.** Root cause: `[STAThread]` is lost after the first `await` in an async `Main` — Win32 `RegisterDragDrop` requires an STA thread, so the GUI now starts synchronously. Defence-in-depth `AllowDrop` registration, elevation detection at startup (UAC breaks drag-and-drop between integrity levels), and an Avalonia 11.0.10 → 11.2.7 upgrade close out the whole DnD bug class.
- Crop marks, registration marks and Summa OPOS markers are now rendered on the preview canvas (previously export-only), with a white-halo/black-core technique for visibility on any background and minimum pixel sizes at extreme zoom-out.
- Template suggestions: invisible suggestion cards under Avalonia 11.2 (theme-variant resource lookup), "Plan failed" after applying a suggestion (the adapter adopted rotated-fit N-up values inconsistent with the model), and bindings wrongly inherited from the workflow template (a business-card preset no longer becomes a saddle-stitch booklet).

### Added
- Empty-state onboarding: an SRA3 sheet mockup with a drop zone and a 1·2·3 checklist instead of a bare "no preview" label; full-canvas drag-hover overlay; inspector skeleton with a cheat sheet.
- Export progress with named phases in the status bar (analysis → preflight → planning → composing → PDF/X) plus a mini progress bar; Ghostscript auto-detection; the success message names both output files when PDF/X is enabled.

### Changed
- Floating canvas toolbar reorganised into four clear groups (zoom / view toggles / front–back / CMYK strip with an "All" button).
- Roll-fed: the item block is centred across the web, the default roll sheet margin is 5 mm so edge marks are not clipped, and a 2-page PDF suggests duplex automatically.
- Topbar, step rail and panels polish: license badge with expiry warning tint, hover actions on the file list, dynamic "Load N checked" label, fixed status-bar overlap on narrow windows, mojibake fixes.

## [1.2.4] - 2026-05-17

### Added
- **Context-aware template suggestions:** a two-tier dialog — presets adapted to *your current sheet, margins and workflow* on top (N-up recomputed by the layout optimizer per suggestion, with an amber warning when an item only fits 1×1), original library presets below.
- **Roll-fed duplex:** a 2-page PDF puts design A on every front and design B on every back; spread view and the front/back toggle now work for roll jobs, with an extreme-aspect-aware spread layout.
- **Measurement units (mm / inches)** in Settings, applied live via an observable units service.
- **Preview ruler** along the top and left canvas edges in the active unit, with automatic tick spacing, an origin marker and a unit chip.

### Fixed
- "Reset all" restores the default template, so the next load can plan and preview again.

## [1.2.3] - 2026-05-17

### Added
- **Preflight v2:** content-based bleed detection (a per-page content bounding box detects declared-but-unused bleed → white strips after trimming) and a narrative summary banner with four mood states above the issue list.
- **Template suggestions from PDF dimensions:** a 25-preset library (business cards, postcards, flyers, posters, labels…) matched by trim size with rotation awareness; auto-registers the "Roll 320" paper preset.
- **Layout optimizer (auto N-up):** tries both item orientations and reports e.g. "Optimal layout: 6 → 21 items per sheet (3×7)".
- Preview trim/bleed guides (cyan dashed trim, magenta dotted bleed) and a manual "Check for updates…" entry.

### Fixed
- Ganging duplex pairing: a 2-page PDF at 12-up now yields one sheet with 12 fronts and 12 backs (previously two single-sided sheets).
- Preview UX: 1-based sheet counter, front/back toggle in spread view, no more blank sheet when leaving spread view on single-sided jobs, CMYK bars drawn inside the sheet, overview-mode threshold tuned.

## [1.2.2] - 2026-05-16

### Fixed
- **Preview memory explosion on 1000+-slot rolls: 3.5 GB → ~70 MB.** Memory-budget LRU cache (hard 512 MB budget), per-bitmap scale clamping (64 MB cap), display-pixel-aware rendering (bitmaps sized to actual on-screen demand, Fiery-style), lower scale buckets, and per-cache-key cancellation of stale renders.
- Roll-fed forces single-sheet view (spread is a booklet concept; it made no sense for a continuous web).

### Added
- **Vector overview mode:** below 50% zoom the canvas draws vector placeholders with page numbers instead of rasterising PDFs — zero background renders while flying over sheets.

### Changed
- Roll-aware sheet layout: extreme-aspect sheets (>4:1) fit by the shorter dimension and align to the start of the web — no more 22-pixel sliver previews; standard formats unchanged.

## [1.2.1] - 2026-05-16

### Fixed
- **Template editor keeps its state** across dialog close/reopen (view-model lifetime change) and **validation blocks closing** with an invalid template: a red error banner lists every problem, all messages localised, template IDs auto-generate when blank, and the editor starts from sane defaults (SRA3 landscape, A4 trim).

### Added
- Roll length as a split/continuous toggle with a segment-length field (replacing the "0 = unlimited" anti-pattern), per-section enable toggles for Roll and Overlay sections, and a "50×50 stickers on a 320 mm roll" preset.

### Changed
- Sheet width/height auto-fill after choosing a paper preset; simplified sheet-size section; website updates (roll-fed, OPOS, logo overlay).

## [1.2.0] - 2026-05-15

### Added
- **Roll-fed binding (step-and-repeat on a web)** for label and sticker production: variable repeat count, automatic per-row packing, multi-segment splitting with a maximum segment length, and basic nesting (auto 0°/90° orientation choice).
- **Summa OPOS / OPOS-XY cutter markers:** four solid black squares (default 3 mm) at the corners of the placement bounding box for print-and-cut plotters; OPOS-XY adds a human-readable sheet label.
- **Per-sheet logo overlay:** stamps a PNG/JPG/single-page PDF on every output sheet with nine anchor positions, offsets and max-width scaling.
- Template editor panels for Roll, plotter markers and overlay; 65 unit tests passing.

## [1.1.1] - 2026-05-10

### Fixed
- **Critical: the application no longer writes to Program Files.** All dynamic data moved to `%APPDATA%` / `%LOCALAPPDATA%` behind a central `AppPaths` helper, with an idempotent one-time migration of 1.0.x licenses and templates, unified directory naming, and user-imported ICC profiles in `%APPDATA%\imPRESS Studio\icc`.
- Backend hardening: hard guard clauses on every I/O boundary, atomic settings writes with corrupted-config rotation, a startup summary log of all key paths, and less log noise.

### Added
- Hot-folder UX after real production testing: auto-defaulted `.work`/`.archive`/`.error` folders, real-time per-field validation in the edit dialog, a license-status banner, an orphan-recovery banner after a crash, and Start/Restart disabled while the license is invalid.

## [1.1.0] - 2026-05-09

### Added
- **Hot folders — flagship automation feature.** Watched input folders impose every dropped PDF with an assigned template and write the result to an output folder. Multiple concurrent folders with isolated runners; multi-layer copy-completion detection; polling fallback for unreliable SMB/DFS watchers; crash recovery via a WAL-mode SQLite ledger; fingerprint-based duplicate suppression (SHA-256 of the first 1 MB + size + mtime); Polly retry policies with exponential backoff and jitter; a global Ghostscript process throttle; per-job license validation.
- Hot folder manager UI (add/edit/delete/start/stop/restart, status list, last-error banner, full PL/EN localisation) and CLI verbs `hotfolder run|list|start|stop|reload`.
- Config in `%APPDATA%\imPRESS Studio\hotfolders.json` (atomic writes, strict validation, long-path support).
- Production resilience: an atomic file state machine (input → `.work` → output/archive/error), bounded job queue, watcher-overflow recovery with full rescans, and top-level exception guards on every loop.

## [1.0.8] - 2026-04-26

### Added
- Full ICC profile management in the export window (dropdown with auto-scan of the `icc/` folders, refresh, "open folder" helper).
- **Clock-rollback protection for licenses:** every successful validation stores an HMAC-signed high-water timestamp; turning the system clock back to dodge expiry is detected and refused.

## [1.0.7] - 2026-04-26

### Fixed
- **Export without a valid license — patched.** Clicking Export re-validates the license on disk every time; the previous cached check could be bypassed by deleting the license file mid-session.

### Changed
- **New hardware fingerprint:** hostname dropped (trivially changeable); three independent SHA-256 components — physical NIC MAC (virtual adapters filtered), system-disk serial, motherboard UUID — with a **2-of-3 tolerance**, so replacing one component does not invalidate the license. Pre-1.0.7 single-hash licenses still validate via a fallback.

### Added
- Publisher tooling: fingerprint string in the activation window, `tools/Issue-License.ps1`, extended `license fingerprint` / `license issue` CLI.

## [1.0.6] - 2026-04-26

### Changed
- **Progressive zoom:** while a sharper scale renders in the background, the canvas instantly shows the best already-cached bitmap (Google Maps-style); coarser scale buckets (5 levels instead of 19) and cancel-on-zoom-spam keep rendering costs flat; the CMYK/overprint filter cache is keyed by source-bitmap reference.

## [1.0.5] - 2026-04-26

### Added
- **CMYK channel toggles** in the preview toolbar — hide any subset of inks (on-screen separation simulation).
- **Colour eyedropper** — a floating readout of RGB and CMYK values under the cursor.

## [1.0.4] - 2026-04-26

### Added
- Parallel page rasterisation with a bounded thread pool, prefetching of adjacent sheets, zoom quantisation into cache buckets, and an LRU bitmap cache.

### Fixed
- Strict preflight no longer throws when an interactive report dialog is available; a race condition in the preflight report dialog (UI-thread marshalling); installer script guard with a readable error when `publish/` is missing.

## [1.0.3] - 2026-04-26

### Added
- **PDF/X-1a:2001 export** alongside PDF/X-4 (PDF 1.4, CMYK+spot, flattened transparency) with Ghostscript arguments selected per standard.
- **Preflight report dialog before export** — warnings (RGB-only, missing ICC, encrypted source, oversized pages…) are listed for the operator to confirm or cancel.
- Sheet-number stamp visible in the preview (previously export-only).

## [1.0.2] - 2026-04-26

### Added
- **Sheet numbering** (position, format `{0}/{1}`, font size, margin) stamped on every output sheet.
- Ghostscript file picker; Inno Setup installer (install location, Start menu folder, desktop shortcut); auto-update via GitHub Releases.

### Fixed
- Export options (Ghostscript path, ICC profile, preflight, sheet range) persist in `%APPDATA%` between sessions.

### Changed
- Versioning normalised to SemVer (1.01 → 1.0.2 line); Velopack replaced with Inno Setup.

## [1.0.1] - 2026-04-19

### Added
- Application menu bar (File, Edit, View, Tools, Settings, Help) with visible shortcuts; a Settings window with UI language choice (Polish/English); the i18n infrastructure (PL/EN dictionaries, XAML `{loc:Loc}` markup, live language switching); gang-run presets (DL 3-up, business cards 6/8/12-up).

## [1.0.0] - 2026-04-18

First official production release ("First Impress").

### Added
- Licensing system: activation by Base64 key or `.json` file, hardware binding, Trial/Standard/Pro editions; activation window with a copyable machine ID; license CLI (`keygen`, `fingerprint`, `issue`, `activate`, `info`).
- File-info panel with full PDF metadata; application icon and logo; rebranding from "Imposition Studio" to **imPRESS Studio**.

### Security
- RSA-2048 signature verification of licenses (the private key never ships with the application); SHA-256 hardware hash.

## [0.9] - 2026-04-10

### Added
- Multi-file loader with checkboxes, source-file merging before imposition, list reordering, per-file info window, "Reset all".

### Fixed
- Hang when loading an encrypted PDF; template name display in the title bar.

## [0.8] - 2026-03-28

### Added
- Statistics panel (paper area, utilisation, waste, cost estimate, weight) with real-time inputs for sheet price and grammage; binding info card; sheet thumbnail strip.

## [0.7] - 2026-03-15

### Added
- Interactive sheet preview on the imposition canvas: cursor-centred zoom, panning, 90° view rotation, spread view, creep visualisation, overprint simulation, keyboard shortcuts (Ctrl+P/E/O, Ctrl+0, F, R, ←/→).

## [0.6] - 2026-02-25

### Added
- Printer marks (crop, registration, CMYK bars, fold marks); creep compensation with configurable paper thickness; Z-fold and accordion options; custom paper sizes.

### Changed
- Extended margin model: bleed, safe zone, gutter, spine, sheet margin.

## [0.5] - 2026-02-10

### Added
- PDF/X-4 conversion via Ghostscript (ISO 15930-7) with ICC profiles; strict preflight; export sheet ranges; the `impose` CLI command.

## [0.4] - 2026-01-20

### Added
- Template manager with full parameter editing, JSON import/export, ready-made templates, and pre-run validation.

## [0.3] - 2026-01-08

### Added
- Perfect binding with spine support; initial Z-fold; configurable signatures (4/8/16 pages).

## [0.2] - 2025-12-18

### Added
- First working imposition: saddle stitch 2×1; PDF export via PdfSharpCore; drag & drop; status bar.

## [0.1] - 2025-12-01

### Added
- Project skeleton: Avalonia 11 + .NET 8 + MVVM; module split (Core / Pdf / Export / Ui / Utils / App); PDF loading and analysis via PdfPig; DI container; Serilog logging.
