/* File: /assets/css/pdf-tools.css
   Module: PDF Tools Suite (hub, tool console, editorial body, conversion gate)
   Scope: loaded only on /pdf-tools/ and its children by inc/pdf-tools.php.
   Light theme first, dark overrides at the bottom (html[data-theme="dark"]),
   matching the convention in qr-tool.css and portal.css. Colours come from the
   global tokens in style.css, never hardcoded outside the local block below. */

/* TOKEN SCOPE. Every root that can hold suite markup has to carry the tokens,
   because several of them are NOT inside .ch-page-pdf at run time.

   The gate is printed into wp_footer. The handoff modal, the form dialog, the
   signature studio and the page workspace are all appended to <body> by
   JavaScript, deliberately - a fixed, full viewport overlay nested inside a
   transformed or clipped ancestor is positioned against that ancestor rather
   than the viewport.

   The failure mode when a root is missing from this list is silent and ugly:
   `background-color: var(--ch-pdf-surface)` with nothing to resolve is an
   invalid declaration, the browser drops it, and the modal renders completely
   TRANSPARENT with the page showing through. Nothing errors. The signature
   dialog shipped that way.

   Anything portalled to <body> in future goes in both lists, here and in the
   dark block at the bottom of this file. */
.ch-page-pdf,
.ch-pdf-gate,
.ch-pdf-modal,
.ch-sig-modal,
.ch-ed {
    /* Surfaces, light to dark: the page well, the panels, the tiles inside the
       panels. A tile must never equal its panel or the controls disappear. */
    --ch-pdf-surface: #FFFFFF;
    --ch-pdf-well: #F1F5F9;
    --ch-pdf-tile: #F8FAFC;
    --ch-pdf-tile-hi: #FFFFFF;

    /* Hairlines: resting comes from --ch-border, these are the hover and
       pressed steps up the Slate ladder. */
    --ch-pdf-line-soft: #CBD5E1;
    --ch-pdf-line-hard: #94A3B8;

    /* Emerald at the darkness each theme needs to hold contrast AS TEXT.
       Emerald 500 fails on white, Emerald 600 fails on slate, so it flips. */
    --ch-pdf-accent: #059669;
    --ch-pdf-accent-bg: rgba(16, 185, 129, 0.09);
    --ch-pdf-accent-bg-hi: rgba(16, 185, 129, 0.16);
    --ch-pdf-accent-line: rgba(16, 185, 129, 0.45);

    /* Inline code chips.
       Own tokens, because the global --ch-code-bg is the PRISM BLOCK override:
       Slate 800, dark in BOTH themes, correct behind a syntax-highlighted
       listing and wrong behind a word in a sentence. Borrowing it here put
       near-black body text on a Slate 800 chip in light mode, which was
       unreadable, and only looked acceptable in dark mode by accident.

       Washes rather than solids, following the same grammar as the notices
       below, and for a load-bearing reason: a chip in this copy can land on the
       page itself, on a card (steps, spec, FAQ), on a shaded table header or
       inside an emerald callout. Those are four different backgrounds per
       theme, and no single solid colour is one step off all of them - in dark,
       #0F172A disappears into the page and #1E293B disappears into the cards.
       A wash is always a step off whatever it sits on. The TEXT stays solid,
       because that is the part that has to hold contrast. */
    --ch-pdf-code-bg: rgba(15, 23, 42, 0.07);
    --ch-pdf-code-text: #0F172A;
    --ch-pdf-code-line: rgba(15, 23, 42, 0.14);

    /* Notices in the house alert grammar: 8-10% wash, ~35% hairline, solid text. */
    --ch-pdf-warn-bg: rgba(245, 158, 11, 0.10);
    --ch-pdf-warn-line: rgba(245, 158, 11, 0.38);
    --ch-pdf-warn-text: #92400E;
    --ch-pdf-stop-bg: rgba(239, 68, 68, 0.07);
    --ch-pdf-stop-line: rgba(239, 68, 68, 0.34);
    --ch-pdf-stop-text: #B91C1C;

    --ch-pdf-radius: 14px;
    --ch-pdf-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px -16px rgba(15, 23, 42, 0.18);
    --ch-pdf-shadow-lift: 0 14px 26px -14px rgba(15, 23, 42, 0.24);
    --ch-pdf-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ch-pdf-mono: 'JetBrains Mono', 'Roboto Mono', ui-monospace, monospace;
}

/* HIDDEN GUARD. The user agent hides [hidden] with a bare element rule, so any
   class setting display (flex, grid, inline-flex) silently outguns it and the
   element can never hide. This suite toggles a lot of flex containers with the
   hidden attribute - stages, option rows, hub sections, both gate branches -
   and one rule at 0,2,0 covers every one of them plus every control added
   later. The gate is printed into the footer, outside .ch-page-pdf, so it
   needs its own branch. */
.ch-page-pdf [hidden],
.ch-pdf-gate[hidden],
.ch-pdf-gate [hidden] {
    display: none !important;
}

/* READ THE SELECTOR ABOVE CAREFULLY BEFORE EDITING IT.
   ".ch-pdf-gate [hidden]" with a space matches DESCENDANTS of the gate. It does
   not match the gate element itself, and the gate is the one node in this file
   that is (a) printed with the hidden attribute, (b) given display:flex by its
   own class, and (c) rendered into wp_footer, outside .ch-page-pdf, so the
   first selector never reaches it either.

   With only the descendant form, .ch-pdf-gate {display:flex} at 0,1,0 outguns
   the user agent's [hidden] {display:none} at 0,0,1 and the conversion modal is
   permanently open on page load - over the tool, for every visitor, before a
   single file has been dropped. That shipped. The attribute form on the same
   compound, ".ch-pdf-gate[hidden]" at 0,2,0, is what actually hides it, and it
   is the same pattern qr-tool.css already uses for .ch-qr-gate[hidden]. */

/* One shared focus grammar for the whole suite. */
.ch-page-pdf :is(a, button, summary, input, select):focus-visible,
.ch-pdf-gate :is(a, button):focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
    border-radius: 6px;
}

/* ==========================================================================
   HARD OVERRIDE - READ BEFORE ADDING ANY BUTTON TO THIS SUITE
   GeneratePress paints every bare <button> with its own background AND white
   text on :hover, :focus and :active, at specificity 0,1,1. A component rule
   written as ".ch-thing" (0,1,0) therefore loses its paint the instant the
   control is hovered, and a hover rule declaring a background but no colour
   only wins half the fight - which is how a light panel ends up with
   white-on-white labels and how icon buttons whose glyphs are currentColor
   turn into invisible blobs.

   Both halves are required for every new control:
   1. list ":focus" and ":active" alongside the base selector, pinning the
      resting paint at 0,2,0, which outguns GP;
   2. declare background-color AND color together in every :hover rule.
   Focus rings stay on :focus-visible via the grammar above, so pinning :focus
   to the resting paint costs nothing in accessibility terms.
   ========================================================================== */

/* ==========================================================================
   1. HERO
   ========================================================================== */
.ch-pdfhero {
    --ch-ph-panel: #FFFFFF;
    --ch-ph-panel-2: #F1F5F9;
    --ch-ph-line: #E2E8F0;
    --ch-ph-grid: rgba(15, 23, 42, 0.05);
    --ch-ph-glow: rgba(16, 185, 129, 0.17);
    --ch-ph-sheet: #FFFFFF;
    --ch-ph-sheet-line: #E2E8F0;
    --ch-ph-ink: rgba(15, 23, 42, 0.13);

    position: relative;
    isolation: isolate;
    overflow: hidden;
    max-width: 1200px;
    margin: 0 auto 1.75rem;
    padding: clamp(1.4rem, 3vw, 2.25rem);
    border: 1px solid var(--ch-ph-line);
    border-radius: 22px;
    background-color: var(--ch-ph-panel);
    background-image: linear-gradient(168deg, var(--ch-ph-panel) 0%, var(--ch-ph-panel) 52%, var(--ch-ph-panel-2) 100%);
    box-shadow: 0 30px 60px -44px rgba(15, 23, 42, 0.62);
}

/* Top rail: the "system online" hairline the dossier, the QR console and the
   Learning Center deck all run. Strongest cue that this is the same site. */
.ch-pdfhero-rail {
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    z-index: 2;
    pointer-events: none;
    background-image: linear-gradient(90deg, transparent 0%, var(--ch-cta) 20%, rgba(16, 185, 129, 0.3) 58%, transparent 100%);
}

.ch-pdfhero-grid {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        linear-gradient(var(--ch-ph-grid) 1px, transparent 1px),
        linear-gradient(90deg, var(--ch-ph-grid) 1px, transparent 1px);
    background-size: 46px 46px;
    mask-image: radial-gradient(ellipse 80% 70% at 15% 0%, #000 0%, transparent 72%);
    -webkit-mask-image: radial-gradient(ellipse 80% 70% at 15% 0%, #000 0%, transparent 72%);
}

.ch-pdfhero-aurora {
    position: absolute;
    top: -34%;
    right: -12%;
    width: 46rem;
    height: 46rem;
    z-index: 0;
    pointer-events: none;
    border-radius: 50%;
    background: radial-gradient(circle, var(--ch-ph-glow) 0%, transparent 66%);
}

.ch-pdfhero-inner {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.85fr);
    gap: clamp(1.5rem, 4vw, 3rem);
    align-items: center;
}

.ch-pdfhero-inner.is-stacked {
    grid-template-columns: minmax(0, 1fr);
}

.ch-pdfhero-copy { min-width: 0; }

.ch-pdfhero-crumbs {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.9rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdfhero-crumbs a { color: var(--ch-pdf-accent); text-decoration: none; }
.ch-pdfhero-crumbs a:hover { text-decoration: underline; }
.ch-pdfhero-crumbs span { display: inline-flex; align-items: center; }

.ch-pdfhero-kicker {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    padding: 0.32rem 0.7rem;
    border: 1px solid var(--ch-pdf-accent-line);
    border-radius: 999px;
    background-color: var(--ch-pdf-accent-bg);
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-pdf-accent);
}

.ch-pdfhero-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: var(--ch-cta);
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2);
}

.ch-pdfhero-title {
    margin: 0 0 0.9rem;
    font-size: clamp(1.85rem, 4.2vw, 2.7rem);
    line-height: 1.14;
    letter-spacing: -0.025em;
}

.ch-pdfhero-title span {
    display: block;
    margin-top: 0.4rem;
    font-size: 0.5em;
    font-weight: 500;
    letter-spacing: -0.01em;
    color: var(--ch-text-muted);
}

.ch-pdfhero-sub {
    max-width: 62ch;
    margin: 0 0 1.3rem;
    font-size: 1.03rem;
    line-height: 1.68;
    color: var(--ch-text-muted);
}

.ch-pdfhero-sub strong { color: var(--ch-text-main); }

.ch-pdfhero-trust {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.4rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.85rem;
    color: var(--ch-text-muted);
}

.ch-pdfhero-trust li {
    display: flex;
    align-items: center;
    gap: 0.42rem;
}

.ch-pdfhero-tick { display: inline-flex; color: var(--ch-pdf-accent); }

/* --- hero plate (ornament) --- */
.ch-pdfhero-plate {
    position: relative;
    padding: 1rem;
    border: 1px solid var(--ch-ph-line);
    border-radius: 16px;
    background-color: var(--ch-pdf-tile);
    box-shadow: 0 18px 38px -26px rgba(15, 23, 42, 0.42);
}

.ch-pdfhero-plate-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.85rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdfhero-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.16rem 0.5rem;
    border-radius: 999px;
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

.ch-pdfhero-badge-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: var(--ch-cta);
    animation: chPdfPulse 2.4s var(--ch-pdf-ease) infinite;
}

@keyframes chPdfPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.35; transform: scale(0.8); }
}

/* ==========================================================================
   1b. HERO SCENES
   One animated scene per tool, built from a shared vocabulary of parts.

   WHY THIS EXISTS. Every tool used to run the same scanning sweep. A sweep
   says "this machine is reading your document", which is true of Scan to PDF
   and of nothing else in the suite. Somebody landing on Sign PDF watched a
   scanner and was told the wrong thing about the product in the first second.

   WHAT KEEPS 39 OF THEM AFFORDABLE. The 20 conversions are FOUR scenes with a
   parameter - raster-in, raster-out, doc-in, doc-out - and differ only by the
   wordmark PHP prints into the card and by the tint the plate carries. The
   other 19 tools do genuinely different things and get their own scene.

   COLOUR IS NEVER NAMED HERE. Every accent resolves through --ch-tint from the
   .ch-tint-* class on the plate, so a scene inherits the same hue as the tool's
   tile on the hub, and dark mode is already handled by the tint block.

   NO SCRIPT. This is the largest thing above the fold on a page whose entire
   argument is that it does not make you wait for a download.
   ========================================================================== */

.ch-pdfhero-stage {
    position: relative;
    overflow: hidden;
    aspect-ratio: 4 / 3;
    border-radius: 10px;
    background-color: var(--ch-pdf-well);
}

/* --- shared parts --- */

/* A page. Absolutely centred, so a scene positions it by overriding the
   translate half of the transform rather than by re-declaring the box. */
.ch-hs-sheet {
    position: absolute;
    left: 50%;
    top: 50%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 7%;
    width: 38%;
    padding: 11% 10%;
    aspect-ratio: 1 / 1.414;
    border: 1px solid var(--ch-ph-sheet-line);
    border-radius: 3px;
    background-color: var(--ch-ph-sheet);
    box-shadow: 0 10px 22px -14px rgba(15, 23, 42, 0.5);
    transform: translate(-50%, -50%);
}

.ch-hs-line {
    display: block;
    width: var(--w, 80%);
    height: 4px;
    border-radius: 2px;
    background-color: var(--ch-ph-ink);
}

.ch-hs-line.is-accent { background-color: var(--ch-tint, var(--ch-cta)); opacity: 0.75; }

/* A format card: wordmark strip on top, contents below. The strip is the whole
   point of the conversion scenes, so it gets the tint and the mono face. */
.ch-hs-card {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 30%;
    aspect-ratio: 1 / 1.3;
    overflow: hidden;
    border: 1px solid var(--ch-ph-sheet-line);
    border-radius: 6px;
    background-color: var(--ch-ph-sheet);
    box-shadow: 0 10px 22px -14px rgba(15, 23, 42, 0.5);
}

.ch-hs-tag {
    display: block;
    padding: 0.28em 0;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.56rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-align: center;
    text-transform: uppercase;
}

.ch-hs-card-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    justify-content: center;
    gap: 9%;
    padding: 10% 12%;
}

.ch-hs-card-body .ch-hs-line { height: 3px; }

/* A photograph, drawn rather than loaded: horizon, sun, two peaks. */
.ch-hs-pic {
    position: relative;
    flex: 1;
    overflow: hidden;
    border-radius: 2px;
    background-color: var(--ch-tint-bg-hi, var(--ch-pdf-accent-bg-hi));
}

.ch-hs-pic::before {
    content: "";
    position: absolute;
    left: 16%;
    top: 14%;
    width: 24%;
    aspect-ratio: 1;
    border-radius: 50%;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    opacity: 0.55;
}

.ch-hs-pic::after {
    content: "";
    position: absolute;
    inset: auto -8% 0 -8%;
    height: 56%;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    opacity: 0.34;
    clip-path: polygon(0 100%, 32% 22%, 56% 68%, 74% 40%, 100% 100%);
}

/* A page thumbnail, for every scene that shuffles or drops pages. */
.ch-hs-row {
    position: absolute;
    left: 50%;
    top: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5%;
    width: 84%;
    transform: translate(-50%, -50%);
}

.ch-hs-tile {
    width: 19%;
    aspect-ratio: 1 / 1.3;
    border: 1px solid var(--ch-ph-sheet-line);
    border-radius: 3px;
    background-color: var(--ch-ph-sheet);
    background-image: linear-gradient(var(--ch-ph-ink) 0 0), linear-gradient(var(--ch-ph-ink) 0 0);
    background-size: 58% 3px, 42% 3px;
    background-position: 50% 38%, 50% 56%;
    background-repeat: no-repeat;
    box-shadow: 0 8px 16px -12px rgba(15, 23, 42, 0.5);
}

/* The transfer arrow every conversion scene runs through. */
.ch-hs-arrow {
    position: relative;
    width: 11%;
    height: 2px;
    border-radius: 2px;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    opacity: 0.5;
    animation: chHsArrow 3.2s var(--ch-pdf-ease) infinite;
}

.ch-hs-arrow::after {
    content: "";
    position: absolute;
    right: -1px;
    top: 50%;
    width: 7px;
    height: 7px;
    border-top: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    border-right: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    transform: translateY(-50%) rotate(45deg);
}

@keyframes chHsArrow {
    0%, 100% { opacity: 0.35; transform: translateX(-12%); }
    50%      { opacity: 1;    transform: translateX(12%); }
}

/* ---------------------------------------------------------------- ORGANIZE */

/* Merge: three pages converge, then the count collapses to one. */
.ch-hs--merge .ch-hs-sheet { width: 34%; animation: chHsMerge 5.2s var(--ch-pdf-ease) infinite; }
.ch-hs--merge .ch-hs-m1 { --x: -62%; --r: -8deg; z-index: 3; }
.ch-hs--merge .ch-hs-m2 { --x: 0%;   --r: 0deg;  z-index: 2; animation-delay: 0.12s; }
.ch-hs--merge .ch-hs-m3 { --x: 62%;  --r: 8deg;  z-index: 1; animation-delay: 0.24s; }

@keyframes chHsMerge {
    0%, 12%   { transform: translate(-50%, -50%) translateX(var(--x)) rotate(var(--r)); }
    46%, 82%  { transform: translate(-50%, -50%) translateX(0) rotate(0deg); }
    100%      { transform: translate(-50%, -50%) translateX(var(--x)) rotate(var(--r)); }
}

.ch-hs-seal {
    position: absolute;
    left: 50%;
    bottom: 11%;
    padding: 0.22em 0.6em;
    border-radius: 999px;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    transform: translateX(-50%);
    animation: chHsSeal 5.2s var(--ch-pdf-ease) infinite;
}

@keyframes chHsSeal {
    0%, 44%  { opacity: 0; transform: translateX(-50%) translateY(6px) scale(0.9); }
    56%, 80% { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
    92%,100% { opacity: 0; transform: translateX(-50%) translateY(6px) scale(0.9); }
}

/* Split: one page, a cut runs down it, the halves part. */
.ch-hs--split .ch-hs-half {
    width: 30%;
    animation: chHsSplit 5s var(--ch-pdf-ease) infinite;
}
.ch-hs--split .is-left  { --x: -34%; }
.ch-hs--split .is-right { --x: 34%; }

@keyframes chHsSplit {
    0%, 34%   { transform: translate(-50%, -50%) translateX(0); }
    62%, 88%  { transform: translate(-50%, -50%) translateX(var(--x)); }
    100%      { transform: translate(-50%, -50%) translateX(0); }
}

.ch-hs-cut {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 2px;
    height: 46%;
    background-image: linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 50%, transparent 0);
    background-size: 2px 8px;
    transform: translate(-50%, -50%);
    animation: chHsCut 5s var(--ch-pdf-ease) infinite;
}

@keyframes chHsCut {
    0%       { opacity: 0; clip-path: inset(0 0 100% 0); }
    18%, 40% { opacity: 1; clip-path: inset(0 0 0 0); }
    64%,100% { opacity: 0; clip-path: inset(0 0 0 0); }
}

/* Remove: the struck page collapses and the row closes the gap. */
.ch-hs--remove .ch-hs-tile { animation: chHsShift 5s var(--ch-pdf-ease) infinite; }
.ch-hs--remove .is-dropped { animation: chHsDrop 5s var(--ch-pdf-ease) infinite; }
.ch-hs--remove .ch-hs-t1,
.ch-hs--remove .ch-hs-t2 { animation: none; }

@keyframes chHsDrop {
    0%, 30%   { opacity: 1; transform: scale(1) translateY(0); }
    52%, 88%  { opacity: 0; transform: scale(0.72) translateY(-22%); }
    100%      { opacity: 1; transform: scale(1) translateY(0); }
}

@keyframes chHsShift {
    0%, 40%   { transform: translateX(0); }
    62%, 88%  { transform: translateX(-31%); }
    100%      { transform: translateX(0); }
}

.ch-hs-strike {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 15%;
    height: 2px;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    transform: translate(15%, -50%) rotate(-16deg);
    transform-origin: left center;
    animation: chHsStrike 5s var(--ch-pdf-ease) infinite;
}

@keyframes chHsStrike {
    0%, 16%  { opacity: 0; transform: translate(15%, -50%) rotate(-16deg) scaleX(0); }
    30%, 44% { opacity: 1; transform: translate(15%, -50%) rotate(-16deg) scaleX(1); }
    56%,100% { opacity: 0; transform: translate(15%, -50%) rotate(-16deg) scaleX(1); }
}

/* Extract: one page lifts out of the run and lands as its own document. */
.ch-hs--extract .is-lifted { animation: chHsLift 5.2s var(--ch-pdf-ease) infinite; z-index: 4; }

@keyframes chHsLift {
    0%, 20%   { transform: translate(0, 0) scale(1); opacity: 1; }
    52%, 74%  { transform: translate(112%, -46%) scale(1.16); opacity: 1; }
    86%, 100% { transform: translate(0, 0) scale(1); opacity: 1; }
}

.ch-hs--extract .ch-hs-catch {
    width: 24%;
    transform: translate(-50%, -50%) translate(112%, 44%);
    animation: chHsCatch 5.2s var(--ch-pdf-ease) infinite;
}

@keyframes chHsCatch {
    0%, 46%   { opacity: 0; }
    62%, 80%  { opacity: 1; }
    92%, 100% { opacity: 0; }
}

/* Reorder: the middle pair trades places, which is the whole tool. */
.ch-hs--reorder .ch-hs-t2 { animation: chHsSwapR 4.4s var(--ch-pdf-ease) infinite; z-index: 3; }
.ch-hs--reorder .ch-hs-t3 { animation: chHsSwapL 4.4s var(--ch-pdf-ease) infinite; }

@keyframes chHsSwapR {
    0%, 18%   { transform: translate(0, 0); }
    50%, 80%  { transform: translate(124%, -16%); }
    100%      { transform: translate(0, 0); }
}

@keyframes chHsSwapL {
    0%, 18%   { transform: translate(0, 0); }
    50%, 80%  { transform: translate(-124%, 16%); }
    100%      { transform: translate(0, 0); }
}

.ch-hs-grip {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    background-color: var(--ch-ph-sheet);
    transform: translate(-50%, -50%);
    animation: chHsGrip 4.4s var(--ch-pdf-ease) infinite;
}

@keyframes chHsGrip {
    0%, 18%  { transform: translate(-190%, -50%); opacity: 0.4; }
    50%, 80% { transform: translate(70%, -50%); opacity: 1; }
    100%     { transform: translate(-190%, -50%); opacity: 0.4; }
}

/* Scan: the one tool a sweeping beam was ever the right picture for. */
.ch-hs--scan .ch-hs-scanned { width: 40%; }

.ch-hs-frame {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 58%;
    aspect-ratio: 1 / 1.16;
    transform: translate(-50%, -50%);
    background:
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 0 0 / 22% 2px no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 0 0 / 2px 22% no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 100% 0 / 22% 2px no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 100% 0 / 2px 22% no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 0 100% / 22% 2px no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 0 100% / 2px 22% no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 100% 100% / 22% 2px no-repeat,
        linear-gradient(var(--ch-tint, var(--ch-pdf-accent)) 0 0) 100% 100% / 2px 22% no-repeat;
    animation: chHsFrame 4.6s var(--ch-pdf-ease) infinite;
}

@keyframes chHsFrame {
    0%, 100% { opacity: 0.5; transform: translate(-50%, -50%) scale(1); }
    50%      { opacity: 1;   transform: translate(-50%, -50%) scale(0.97); }
}

.ch-hs-beam {
    position: absolute;
    inset: 0 auto 0 -30%;
    width: 30%;
    background-image: linear-gradient(90deg, transparent, var(--ch-tint-bg-hi, rgba(16, 185, 129, 0.22)), transparent);
    animation: chHsSweep 4.6s var(--ch-pdf-ease) infinite;
}

@keyframes chHsSweep {
    0%        { transform: translateX(0); }
    60%, 100% { transform: translateX(460%); }
}

/* ------------------------------------------------------------- CONVERSIONS */

.ch-hs--raster-in,
.ch-hs--raster-out,
.ch-hs--doc-in,
.ch-hs--doc-out {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4%;
}

/* A stack of source images. Multiple in, one out, which is the difference
   between the raster scenes and the one-for-one Office scenes. */
.ch-hs-fan {
    position: relative;
    width: 30%;
    aspect-ratio: 1 / 1.3;
    flex: none;
}

.ch-hs-fan .ch-hs-card {
    position: absolute;
    inset: 0;
    width: 100%;
}

.ch-hs-fan .ch-hs-f3 { animation: chHsFan3 4.8s var(--ch-pdf-ease) infinite; }
.ch-hs-fan .ch-hs-f2 { animation: chHsFan2 4.8s var(--ch-pdf-ease) infinite; }
.ch-hs-fan .ch-hs-f1 { z-index: 3; }

@keyframes chHsFan3 {
    0%, 100% { transform: translate(-16%, -13%) rotate(-7deg); opacity: 0.5; }
    50%      { transform: translate(-22%, -18%) rotate(-10deg); opacity: 0.75; }
}

@keyframes chHsFan2 {
    0%, 100% { transform: translate(-8%, -6%) rotate(-3.5deg); opacity: 0.75; }
    50%      { transform: translate(-11%, -9%) rotate(-5deg); opacity: 0.9; }
}

/* Source hands off, target lights up as it receives. */
.ch-hs-source { flex: none; animation: chHsSend 3.2s var(--ch-pdf-ease) infinite; }
.ch-hs-target { flex: none; animation: chHsLand 3.2s var(--ch-pdf-ease) infinite; }

@keyframes chHsSend {
    0%, 100% { transform: translateX(0) scale(1); }
    50%      { transform: translateX(-5%) scale(0.97); }
}

@keyframes chHsLand {
    0%, 100% { transform: translateX(0) scale(0.97); box-shadow: 0 10px 22px -14px rgba(15, 23, 42, 0.5); }
    50%      { transform: translateX(4%) scale(1.04); box-shadow: 0 14px 26px -12px rgba(15, 23, 42, 0.55); }
}

/* doc-out unfreezes the page: the caret says the result can be typed into. */
.ch-hs-caret {
    position: absolute;
    right: 21%;
    top: 52%;
    width: 2px;
    height: 13%;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    animation: chHsBlink 1.1s steps(1, end) infinite;
}

@keyframes chHsBlink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }
@keyframes chHsPip   { 0%, 100% { opacity: 0.35; transform: scale(0.82); } 50% { opacity: 1; transform: scale(1); } }

/* Archive: the page gets sealed and stamped with the standard it now meets. */
.ch-hs--archive .ch-hs-vaulted { width: 36%; }

/* The seal ring lands INSIDE the sheet, not around the whole stage. At 46% it
   was wider than the page it was supposed to be stamping and the scene read as
   a big red circle with a document hiding behind it. */
.ch-hs-ring {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 24%;
    aspect-ratio: 1;
    border: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    border-radius: 50%;
    transform: translate(-50%, -50%);
    animation: chHsRing 4.4s var(--ch-pdf-ease) infinite;
}

@keyframes chHsRing {
    0%, 20%   { opacity: 0; transform: translate(-50%, -50%) scale(1.9); }
    46%, 74%  { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
    100%      { opacity: 0; transform: translate(-50%, -50%) scale(0.86); }
}

.ch-hs-stamp {
    position: absolute;
    left: 50%;
    top: 50%;
    padding: 0.2em 0.55em;
    border: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    border-radius: 4px;
    color: var(--ch-tint, var(--ch-pdf-accent));
    font-family: var(--ch-pdf-mono);
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    transform: translate(-50%, -50%) rotate(-11deg);
    animation: chHsStamp 4.4s var(--ch-pdf-ease) infinite;
}

@keyframes chHsStamp {
    0%, 26%   { opacity: 0; transform: translate(-50%, -50%) rotate(-11deg) scale(1.7); }
    44%, 78%  { opacity: 1; transform: translate(-50%, -50%) rotate(-11deg) scale(1); }
    92%, 100% { opacity: 0; transform: translate(-50%, -50%) rotate(-11deg) scale(1); }
}

/* -------------------------------------------------------------------- EDIT */

/* Rotate: a real quarter turn, held long enough to read as deliberate. */
.ch-hs--rotate .ch-hs-turn { width: 34%; animation: chHsTurn 5s var(--ch-pdf-ease) infinite; }

@keyframes chHsTurn {
    0%, 20%   { transform: translate(-50%, -50%) rotate(0deg); }
    44%, 66%  { transform: translate(-50%, -50%) rotate(90deg); }
    88%, 100% { transform: translate(-50%, -50%) rotate(180deg); }
}

/* A full dashed ring rather than two visible borders: the half ring read as a
   broken shape floating off the page instead of as an axis of rotation. */
.ch-hs-arc {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 54%;
    aspect-ratio: 1;
    border: 2px dashed var(--ch-tint, var(--ch-pdf-accent));
    border-radius: 50%;
    opacity: 0.42;
    transform: translate(-50%, -50%);
    animation: chHsArc 5s linear infinite;
}

@keyframes chHsArc { to { transform: translate(-50%, -50%) rotate(360deg); } }

/* Paginate: the folio counts up in the corner where it will actually print. */
.ch-hs--paginate .ch-hs-paged { width: 38%; }

.ch-hs-folio {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 1.4em;
    height: 1.4em;
    margin-left: 9%;
    margin-top: 19%;
    color: var(--ch-tint, var(--ch-pdf-accent));
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    font-weight: 700;
    transform: translate(-50%, -50%);
}

.ch-hs-folio b {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    opacity: 0;
    animation: chHsFolio 4.5s steps(1, end) infinite;
}

.ch-hs-folio b:nth-child(2) { animation-delay: 1.5s; }
.ch-hs-folio b:nth-child(3) { animation-delay: 3s; }

@keyframes chHsFolio { 0% { opacity: 1; } 33.34%, 100% { opacity: 0; } }

/* Watermark: the stamp lands across the page, at an angle, over the text. */
.ch-hs--watermark .ch-hs-stamped { width: 40%; }

.ch-hs-mark {
    position: absolute;
    left: 50%;
    top: 50%;
    color: var(--ch-tint, var(--ch-pdf-accent));
    font-family: var(--ch-pdf-mono);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    white-space: nowrap;
    transform: translate(-50%, -50%) rotate(-27deg);
    animation: chHsMark 4.6s var(--ch-pdf-ease) infinite;
}

@keyframes chHsMark {
    0%, 14%   { opacity: 0; transform: translate(-50%, -50%) rotate(-27deg) scale(1.35); }
    40%, 76%  { opacity: 0.5; transform: translate(-50%, -50%) rotate(-27deg) scale(1); }
    96%, 100% { opacity: 0; transform: translate(-50%, -50%) rotate(-27deg) scale(1); }
}

/* Crop: the frame walks in and the page follows it. */
.ch-hs--crop .ch-hs-cropped { width: 40%; animation: chHsCropSheet 4.8s var(--ch-pdf-ease) infinite; }

@keyframes chHsCropSheet {
    0%, 24%   { clip-path: inset(0 0 0 0); }
    58%, 84%  { clip-path: inset(11% 14% 11% 14%); }
    100%      { clip-path: inset(0 0 0 0); }
}

.ch-hs-crop {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 40%;
    aspect-ratio: 1 / 1.414;
    border: 2px solid var(--ch-tint, var(--ch-pdf-accent));
    border-radius: 2px;
    transform: translate(-50%, -50%);
    animation: chHsCropFrame 4.8s var(--ch-pdf-ease) infinite;
}

@keyframes chHsCropFrame {
    0%, 24%   { width: 40%; opacity: 0.55; }
    58%, 84%  { width: 29%; opacity: 1; }
    100%      { width: 40%; opacity: 0.55; }
}

/* Edit: a line gets typed in, with the nib that is doing it. */
.ch-hs--edit .ch-hs-typed { width: 40%; }
.ch-hs--edit .ch-hs-line:nth-child(3) { height: 4px; width: 0; background-color: var(--ch-tint, var(--ch-pdf-accent)); animation: chHsTypeLine 4.4s steps(14, end) infinite; }

@keyframes chHsTypeLine {
    0%       { width: 0; }
    56%,86%  { width: 74%; }
    100%     { width: 0; }
}

.ch-hs-type {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 2px;
    height: 4.4%;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    transform: translate(-50%, -50%);
    animation: chHsTypeCaret 4.4s steps(14, end) infinite;
}

@keyframes chHsTypeCaret {
    0%       { transform: translate(-380%, -50%); }
    56%,86%  { transform: translate(155%, -50%); }
    100%     { transform: translate(-380%, -50%); }
}

/* The real pen glyph. A rounded square rotated 45deg was meant to read as a
   nib and read as a blob. */
.ch-hs-nib {
    position: absolute;
    right: 16%;
    bottom: 15%;
    display: inline-flex;
    color: var(--ch-tint, var(--ch-pdf-accent));
    animation: chHsNib 4.4s var(--ch-pdf-ease) infinite;
}

@keyframes chHsNib {
    0%, 100% { opacity: 0.45; transform: translate(4px, 4px) rotate(-6deg); }
    50%      { opacity: 1;    transform: translate(0, 0) rotate(0deg); }
}

/* ---------------------------------------------------------------- SECURITY */

.ch-hs--unlock .ch-hs-secured,
.ch-hs--protect .ch-hs-secured { width: 38%; }

/* Protect scrambles the page as the lock shuts; unlock does it the other way,
   which is the same two keyframes read in opposite directions. */
.ch-hs--protect .ch-hs-line { animation: chHsScramble 4.4s var(--ch-pdf-ease) infinite; }
.ch-hs--unlock  .ch-hs-line { animation: chHsScramble 4.4s var(--ch-pdf-ease) infinite reverse; }

@keyframes chHsScramble {
    0%, 26%   { width: var(--w); opacity: 1; filter: none; }
    54%, 88%  { width: 34%; opacity: 0.55; filter: blur(1.4px); }
    100%      { width: var(--w); opacity: 1; filter: none; }
}

.ch-hs-lock {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 30px;
    height: 34px;
    transform: translate(-50%, -50%);
}

.ch-hs-lock .ch-hs-body {
    position: absolute;
    inset: auto 0 0 0;
    height: 19px;
    border-radius: 4px;
    background-color: var(--ch-tint, var(--ch-pdf-accent));
    box-shadow: 0 6px 14px -8px rgba(15, 23, 42, 0.7);
}

.ch-hs-lock .ch-hs-shackle {
    position: absolute;
    left: 50%;
    top: 0;
    width: 18px;
    height: 18px;
    border: 3px solid var(--ch-tint, var(--ch-pdf-accent));
    border-bottom: 0;
    border-radius: 9px 9px 0 0;
    transform-origin: bottom left;
}

.ch-hs--protect .ch-hs-shackle { animation: chHsShut 4.4s var(--ch-pdf-ease) infinite; }
.ch-hs--unlock  .ch-hs-shackle { animation: chHsShut 4.4s var(--ch-pdf-ease) infinite reverse; }

@keyframes chHsShut {
    0%, 22%   { transform: translateX(-50%) translateX(4px) rotate(22deg); }
    52%, 90%  { transform: translateX(-50%) rotate(0deg); }
    100%      { transform: translateX(-50%) translateX(4px) rotate(22deg); }
}

/* Sign: the stroke draws itself, which is the only honest picture of signing. */
.ch-hs--sign .ch-hs-signed { width: 40%; }

.ch-hs-sig {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 30%;
    margin-top: 7%;
    color: var(--ch-tint, var(--ch-pdf-accent));
    transform: translate(-50%, -50%) rotate(-3deg);
}

.ch-hs-sig svg { display: block; width: 100%; height: auto; overflow: visible; }

.ch-hs-sig path {
    stroke-dasharray: 220;
    stroke-dashoffset: 220;
    animation: chHsSign 4.8s var(--ch-pdf-ease) infinite;
}

@keyframes chHsSign {
    0%        { stroke-dashoffset: 220; }
    52%, 84%  { stroke-dashoffset: 0; }
    100%      { stroke-dashoffset: 220; }
}

.ch-hs-rule {
    position: absolute;
    inset: auto 0 -14% 0;
    height: 1px;
    background-color: var(--ch-ph-ink);
    opacity: 0.5;
}

/* Redact: the bars land and the text under them is gone, not covered. */
.ch-hs--redact .ch-hs-leaky { width: 40%; }

.ch-hs-bars {
    position: absolute;
    left: 50%;
    top: 50%;
    display: flex;
    flex-direction: column;
    gap: 9%;
    width: 27%;
    transform: translate(-50%, -50%);
}

.ch-hs-bars i {
    display: block;
    height: 7px;
    border-radius: 1px;
    background-color: #0F172A;
    transform-origin: left center;
    animation: chHsBar 4.6s var(--ch-pdf-ease) infinite;
}

.ch-hs-bars i:nth-child(1) { width: 96%; }
.ch-hs-bars i:nth-child(2) { width: 74%; animation-delay: 0.34s; }
.ch-hs-bars i:nth-child(3) { width: 88%; animation-delay: 0.68s; }

@keyframes chHsBar {
    0%, 8%    { transform: scaleX(0); }
    34%, 82%  { transform: scaleX(1); }
    100%      { transform: scaleX(0); }
}

/* ---------------------------------------------------------------- OPTIMIZE */

/* Repair: the file is in pieces, the pieces come back, the tick confirms it. */
.ch-hs-broken {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 34%;
    aspect-ratio: 1 / 1.414;
    transform: translate(-50%, -50%);
}

.ch-hs-shard {
    position: absolute;
    width: 50%;
    height: 50%;
    border: 1px solid var(--ch-ph-sheet-line);
    background-color: var(--ch-ph-sheet);
    animation: chHsMend 4.6s var(--ch-pdf-ease) infinite;
}

.ch-hs-shard.s1 { left: 0; top: 0; --dx: -34%; --dy: -26%; --dr: -13deg; }
.ch-hs-shard.s2 { right: 0; top: 0; --dx: 34%; --dy: -20%; --dr: 11deg; }
.ch-hs-shard.s3 { left: 0; bottom: 0; --dx: -28%; --dy: 24%; --dr: 9deg; }
.ch-hs-shard.s4 { right: 0; bottom: 0; --dx: 30%; --dy: 28%; --dr: -10deg; }

@keyframes chHsMend {
    0%, 14%   { transform: translate(var(--dx), var(--dy)) rotate(var(--dr)); opacity: 0.6; }
    52%, 86%  { transform: translate(0, 0) rotate(0deg); opacity: 1; }
    100%      { transform: translate(var(--dx), var(--dy)) rotate(var(--dr)); opacity: 0.6; }
}

.ch-hs-fixed {
    position: absolute;
    left: 50%;
    bottom: 10%;
    display: inline-flex;
    color: var(--ch-tint, var(--ch-pdf-accent));
    transform: translateX(-50%);
    animation: chHsSeal 4.6s var(--ch-pdf-ease) infinite;
}

/* OCR: a picture of words resolves into words the machine can select. */
.ch-hs--ocr .ch-hs-read { width: 40%; }
.ch-hs--ocr .ch-hs-line { animation: chHsResolve 4.6s var(--ch-pdf-ease) infinite; }

@keyframes chHsResolve {
    0%, 20%   { filter: blur(2.2px); opacity: 0.5; }
    56%, 88%  { filter: blur(0); opacity: 1; }
    100%      { filter: blur(2.2px); opacity: 0.5; }
}

.ch-hs-glyphs {
    position: absolute;
    left: 50%;
    bottom: 12%;
    display: flex;
    gap: 0.4em;
    transform: translateX(-50%);
    color: var(--ch-tint, var(--ch-pdf-accent));
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    font-weight: 700;
}

.ch-hs-glyphs b { opacity: 0; animation: chHsGlyph 4.6s var(--ch-pdf-ease) infinite; }
.ch-hs-glyphs b:nth-child(2) { animation-delay: 0.22s; }
.ch-hs-glyphs b:nth-child(3) { animation-delay: 0.44s; }

@keyframes chHsGlyph {
    0%, 28%   { opacity: 0; transform: translateY(5px); }
    54%, 84%  { opacity: 1; transform: translateY(0); }
    100%      { opacity: 0; transform: translateY(5px); }
}

.ch-hs-lens {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44%;
    height: 22%;
    border-radius: 4px;
    background-image: linear-gradient(180deg, transparent, var(--ch-tint-bg-hi, rgba(16, 185, 129, 0.22)), transparent);
    transform: translate(-50%, -50%);
    animation: chHsLens 4.6s var(--ch-pdf-ease) infinite;
}

@keyframes chHsLens {
    0%, 100% { transform: translate(-50%, -190%); opacity: 0.9; }
    50%      { transform: translate(-50%, 80%); opacity: 0.9; }
}

.ch-pdfhero-readout {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.5rem;
    margin: 0.85rem 0 0;
}

.ch-pdfhero-readout div {
    padding: 0.5rem 0.55rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-surface);
}

.ch-pdfhero-readout dt {
    font-family: var(--ch-pdf-mono);
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdfhero-readout dd {
    margin: 0.15rem 0 0;
    font-size: 0.82rem;
    font-weight: 600;
}

@media (max-width: 880px) {
    .ch-pdfhero-inner { grid-template-columns: minmax(0, 1fr); }
    .ch-pdfhero-plate { display: none; }
}

/* ==========================================================================
   2. HUB
   ========================================================================== */
.ch-pdf-hub {
    max-width: 1200px;
    margin: 0 auto 3rem;
}

.ch-pdf-hub-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.75rem;
}

.ch-pdf-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.ch-pdf-chip,
.ch-pdf-chip:focus,
.ch-pdf-chip:active {
    display: inline-flex;
    align-items: center;
    gap: 0.38rem;
    padding: 0.44rem 0.85rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-main);
    font-size: 0.83rem;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.16s, border-color 0.16s, color 0.16s;
}

.ch-pdf-chip:hover {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

.ch-pdf-chip.is-active,
.ch-pdf-chip.is-active:focus,
.ch-pdf-chip.is-active:active {
    background-color: var(--ch-text-main);
    border-color: var(--ch-text-main);
    color: #FFFFFF;
}

.ch-pdf-chip.is-active:hover {
    background-color: var(--ch-text-main);
    color: #FFFFFF;
}

.ch-pdf-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 240px;
    padding: 0 0.85rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-muted);
}

.ch-pdf-search input {
    flex: 1;
    min-width: 0;
    padding: 0.55rem 0;
    border: 0;
    background: none;
    color: var(--ch-text-main);
    font-size: 0.88rem;
}

.ch-pdf-search input:focus { outline: none; }
.ch-pdf-search:focus-within { border-color: var(--ch-pdf-accent-line); }

.ch-pdf-hub-section { margin-bottom: 2.5rem; }

.ch-pdf-hub-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(228px, 1fr));
    gap: 0.9rem;
}

.ch-pdf-hub-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 1.1rem 1.1rem 1.2rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
    text-decoration: none;
    color: inherit;
    transition: transform 0.2s var(--ch-pdf-ease), border-color 0.2s, box-shadow 0.2s;
}

.ch-pdf-hub-card:hover {
    transform: translateY(-3px);
    border-color: var(--ch-tint-line, var(--ch-pdf-accent-line));
    box-shadow: var(--ch-pdf-shadow-lift);
}

.ch-pdf-hub-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 10px;
    background-color: var(--ch-tint-bg, var(--ch-pdf-accent-bg));
    color: var(--ch-tint, var(--ch-pdf-accent));
    transition: background-color 0.2s;
}

.ch-pdf-hub-card:hover .ch-pdf-hub-ico {
    background-color: var(--ch-tint-bg-hi, var(--ch-pdf-accent-bg-hi));
}

/* ==========================================================================
   TOOL TILE TINTS
   A .ch-tint-* class sits on the CARD, not on the icon well, so the hover
   hairline can read the same token the glyph is painted with. Custom
   properties inherit downwards only, and a class on the well would leave the
   card with nothing to resolve.

   What the hue encodes is the FORMAT or the OPERATION, never decoration. The
   two halves of a conversion pair carry the same tint on purpose - JPG to PDF
   and PDF to JPG are one colour and one wordmark, told apart by which way the
   corner folds - so the eye can find "the JPG pair" without reading captions.

   The emerald here is not the brand emerald doing a second job. It is reserved
   for the two tools that ARE the promise of the suite, Merge and Sign, and the
   Live pill on every card stays emerald regardless, which is what keeps 39
   tinted tiles reading as one product rather than a paint chart.

   Text-grade steps (Tailwind 600-800), not the 500s: a 500 on a 12% wash of
   itself over white sits around 3:1 and turns a 22px glyph into a smudge. */
.ch-tint-emerald { --ch-tint: #047857; --ch-tint-bg: rgba(16, 185, 129, 0.10); --ch-tint-bg-hi: rgba(16, 185, 129, 0.18); --ch-tint-line: rgba(16, 185, 129, 0.45); }
.ch-tint-teal    { --ch-tint: #0F766E; --ch-tint-bg: rgba(20, 184, 166, 0.11); --ch-tint-bg-hi: rgba(20, 184, 166, 0.19); --ch-tint-line: rgba(20, 184, 166, 0.45); }
.ch-tint-cyan    { --ch-tint: #0E7490; --ch-tint-bg: rgba(6, 182, 212, 0.12);  --ch-tint-bg-hi: rgba(6, 182, 212, 0.20);  --ch-tint-line: rgba(6, 182, 212, 0.45); }
.ch-tint-sky     { --ch-tint: #0369A1; --ch-tint-bg: rgba(14, 165, 233, 0.11); --ch-tint-bg-hi: rgba(14, 165, 233, 0.19); --ch-tint-line: rgba(14, 165, 233, 0.45); }
.ch-tint-blue    { --ch-tint: #1D4ED8; --ch-tint-bg: rgba(59, 130, 246, 0.11); --ch-tint-bg-hi: rgba(59, 130, 246, 0.19); --ch-tint-line: rgba(59, 130, 246, 0.45); }
.ch-tint-indigo  { --ch-tint: #4338CA; --ch-tint-bg: rgba(99, 102, 241, 0.11); --ch-tint-bg-hi: rgba(99, 102, 241, 0.19); --ch-tint-line: rgba(99, 102, 241, 0.45); }
.ch-tint-violet  { --ch-tint: #6D28D9; --ch-tint-bg: rgba(139, 92, 246, 0.11); --ch-tint-bg-hi: rgba(139, 92, 246, 0.19); --ch-tint-line: rgba(139, 92, 246, 0.45); }
.ch-tint-fuchsia { --ch-tint: #A21CAF; --ch-tint-bg: rgba(217, 70, 239, 0.10); --ch-tint-bg-hi: rgba(217, 70, 239, 0.18); --ch-tint-line: rgba(217, 70, 239, 0.42); }
.ch-tint-rose    { --ch-tint: #BE123C; --ch-tint-bg: rgba(244, 63, 94, 0.10);  --ch-tint-bg-hi: rgba(244, 63, 94, 0.18);  --ch-tint-line: rgba(244, 63, 94, 0.42); }
.ch-tint-red     { --ch-tint: #B91C1C; --ch-tint-bg: rgba(239, 68, 68, 0.10);  --ch-tint-bg-hi: rgba(239, 68, 68, 0.18);  --ch-tint-line: rgba(239, 68, 68, 0.42); }
.ch-tint-orange  { --ch-tint: #C2410C; --ch-tint-bg: rgba(249, 115, 22, 0.11); --ch-tint-bg-hi: rgba(249, 115, 22, 0.19); --ch-tint-line: rgba(249, 115, 22, 0.45); }
.ch-tint-amber   { --ch-tint: #B45309; --ch-tint-bg: rgba(245, 158, 11, 0.13); --ch-tint-bg-hi: rgba(245, 158, 11, 0.22); --ch-tint-line: rgba(245, 158, 11, 0.48); }
.ch-tint-green   { --ch-tint: #15803D; --ch-tint-bg: rgba(34, 197, 94, 0.11);  --ch-tint-bg-hi: rgba(34, 197, 94, 0.19);  --ch-tint-line: rgba(34, 197, 94, 0.45); }
.ch-tint-slate   { --ch-tint: #475569; --ch-tint-bg: rgba(100, 116, 139, 0.13); --ch-tint-bg-hi: rgba(100, 116, 139, 0.21); --ch-tint-line: rgba(100, 116, 139, 0.45); }

.ch-pdf-hub-name {
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.ch-pdf-hub-desc {
    font-size: 0.84rem;
    line-height: 1.5;
    color: var(--ch-text-muted);
}

.ch-pdf-hub-status {
    position: absolute;
    top: 0.85rem;
    right: 0.85rem;
    padding: 0.15rem 0.45rem;
    border-radius: 999px;
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
    font-family: var(--ch-pdf-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.ch-pdf-hub-card.is-soon { opacity: 0.72; }
.ch-pdf-hub-card.is-soon:hover { opacity: 1; }

/* Rendered as a <span> because its page is not published yet. It keeps its
   place on the grid - the grid is the roadmap as well as the directory - but
   it must not look or behave like something that can be opened. */
.ch-pdf-hub-card.is-locked {
    cursor: default;
    opacity: 0.6;
}

.ch-pdf-hub-card.is-locked:hover {
    transform: none;
    border-color: var(--ch-border);
    box-shadow: none;
    opacity: 0.6;
}

.ch-pdf-hub-card.is-soon .ch-pdf-hub-ico {
    background-color: var(--ch-pdf-well);
    color: var(--ch-text-muted);
}

.ch-pdf-hub-card.is-soon .ch-pdf-hub-status {
    background-color: var(--ch-pdf-well);
    color: var(--ch-text-muted);
}

.ch-pdf-hub-empty {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 1.1rem 1.2rem;
    border: 1px solid var(--ch-pdf-warn-line);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-warn-bg);
    color: var(--ch-pdf-warn-text);
    font-size: 0.9rem;
}

.ch-pdf-hub-empty svg { flex: 0 0 auto; margin-top: 2px; }

/* ==========================================================================
   3. TOOL CONSOLE - SHELL AND DROP ZONE
   ========================================================================== */
.ch-pdf-app {
    position: relative;
    max-width: 1200px;
    margin: 0 auto 3rem;
}

.ch-pdf-notice {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
    margin: 0 0 1rem;
    padding: 0.8rem 1rem;
    border: 1px solid var(--ch-pdf-warn-line);
    border-radius: 10px;
    background-color: var(--ch-pdf-warn-bg);
    color: var(--ch-pdf-warn-text);
    font-size: 0.88rem;
    line-height: 1.55;
}

.ch-pdf-notice svg { flex: 0 0 auto; margin-top: 2px; }

.ch-pdf-notice.is-stop {
    border-color: var(--ch-pdf-stop-line);
    background-color: var(--ch-pdf-stop-bg);
    color: var(--ch-pdf-stop-text);
}

.ch-pdf-drop {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    min-height: 340px;
    padding: 2.5rem 1.5rem;
    border: 2px dashed var(--ch-pdf-line-soft);
    border-radius: 20px;
    background-color: var(--ch-pdf-surface);
    text-align: center;
    cursor: pointer;
    transition: border-color 0.2s, background-color 0.2s, transform 0.2s var(--ch-pdf-ease);
}

.ch-pdf-drop:hover {
    border-color: var(--ch-cta);
    background-color: var(--ch-pdf-accent-bg);
}

/* Whole-page drag state. The engine stamps .is-dragging on the app root the
   moment a file enters the window, not the moment it enters the dashed box,
   because a drop released two pixels outside a narrow target navigates the
   browser away from the page and destroys the batch. */
.ch-pdf-app.is-dragging .ch-pdf-drop {
    border-color: var(--ch-cta);
    background-color: var(--ch-pdf-accent-bg-hi);
    transform: scale(1.006);
}

.ch-pdf-app.is-dragging::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 40;
    pointer-events: none;
    border: 3px solid var(--ch-cta);
    border-radius: 4px;
    box-shadow: inset 0 0 60px -10px rgba(16, 185, 129, 0.35);
}

.ch-pdf-drop-ring {
    position: absolute;
    inset: 14px;
    border-radius: 14px;
    pointer-events: none;
    background-image: radial-gradient(ellipse 60% 60% at 50% 40%, var(--ch-pdf-accent-bg) 0%, transparent 70%);
}

.ch-pdf-drop-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 68px;
    height: 68px;
    margin-bottom: 0.3rem;
    border-radius: 18px;
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

.ch-pdf-drop-cta {
    padding: 0.85rem 1.9rem;
    border-radius: 10px;
    background-color: var(--ch-cta);
    color: #FFFFFF;
    font-size: 1.02rem;
    font-weight: 600;
    box-shadow: 0 12px 24px -14px rgba(16, 185, 129, 0.9);
}

.ch-pdf-drop:hover .ch-pdf-drop-cta { background-color: #059669; }

.ch-pdf-drop-hint { font-size: 0.92rem; color: var(--ch-text-muted); }

.ch-pdf-drop-spec {
    margin-top: 0.35rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-file-input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.ch-pdf-drop-privacy {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
    max-width: 75ch;
    margin: 1rem auto 0;
    font-size: 0.86rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
    text-align: left;
}

.ch-pdf-drop-privacy svg { flex: 0 0 auto; margin-top: 3px; color: var(--ch-pdf-accent); }

/* ==========================================================================
   4. WORKBENCH
   ========================================================================== */
.ch-pdf-work {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 330px;
    gap: 1.25rem;
    align-items: start;
}

.ch-pdf-canvas {
    min-width: 0;
    padding: 1.1rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-well);
}

.ch-pdf-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    margin-bottom: 0.9rem;
}

.ch-pdf-count {
    font-family: var(--ch-pdf-mono);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-bar-ops { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.ch-pdf-bop,
.ch-pdf-bop:focus,
.ch-pdf-bop:active {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.38rem 0.7rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-main);
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-bop:hover {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

.ch-pdf-bop.is-drop:hover {
    background-color: var(--ch-pdf-stop-bg);
    border-color: var(--ch-pdf-stop-line);
    color: var(--ch-pdf-stop-text);
}

/* --- file cards --- */
.ch-pdf-files {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(146px, 1fr));
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-pdf-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 0.6rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
    cursor: grab;
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s var(--ch-pdf-ease);
}

.ch-pdf-card:hover {
    border-color: var(--ch-pdf-line-hard);
    box-shadow: var(--ch-pdf-shadow);
}

.ch-pdf-card.is-dragged { opacity: 0.4; cursor: grabbing; }

.ch-pdf-card.is-over {
    border-color: var(--ch-cta);
    box-shadow: -3px 0 0 0 var(--ch-cta);
}

.ch-pdf-card.is-locked,
.ch-pdf-card.is-error {
    border-color: var(--ch-pdf-stop-line);
    background-color: var(--ch-pdf-stop-bg);
}

.ch-pdf-card-idx {
    position: absolute;
    top: -7px;
    left: -7px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 5px;
    border-radius: 999px;
    background-color: var(--ch-text-main);
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.68rem;
    font-weight: 600;
}

.ch-pdf-card-body {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    aspect-ratio: 1 / 1.3;
    margin-bottom: 0.55rem;
    border-radius: 6px;
    background-color: var(--ch-pdf-well);
    /* Slight inset so a full-bleed page preview reads as a sheet sitting in a
       tray rather than as the card's own background. */
    padding: 5px;
}

/* --- true page preview ---------------------------------------------------
   An SVG whose viewBox is the page in PDF points. preserveAspectRatio does the
   fitting, so the identical markup serves a 130px card and a full screen
   lightbox with no measuring code and no resize listener. */
.ch-pdf-page {
    display: block;
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 1px 3px rgba(15, 23, 42, 0.28));
}

.ch-pdf-page-sheet {
    /* The paper. Deliberately white in both themes: a preview of a printed page
       that adapts to a dark UI is not a preview of anything. */
    fill: #FFFFFF;
}

.ch-pdf-page-img {
    /* The rotation is ours, computed from the EXIF tag plus whatever the user
       asked for, and it is baked into the SVG transform. If the renderer also
       applied the file's own orientation the image would be turned twice, so
       that behaviour is switched off explicitly. */
    image-orientation: none;
}

.ch-pdf-thumb-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    /* Always a light plate, in both themes. A rasterised page is a photograph
       of paper: dropping it on a slate tile makes a perfectly good scan read as
       damaged, and the transparent regions of the page would show slate
       through the artwork. */
    background-color: #FFFFFF;
    animation: chPdfThumbIn 0.24s var(--ch-pdf-ease);
}

@keyframes chPdfThumbIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Page count over the rasterised preview. The plate badge says the same thing
   before the thumbnail arrives, so the information never disappears and never
   moves - it just changes surface. */
.ch-pdf-thumb-badge {
    position: absolute;
    right: 4px;
    bottom: 4px;
    padding: 0.1rem 0.36rem;
    border-radius: 4px;
    background-color: rgba(15, 23, 42, 0.82);
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    pointer-events: none;
}

.ch-pdf-plate {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.45rem;
    color: var(--ch-text-muted);
}

.ch-pdf-plate.is-busy { animation: chPdfBlink 1.1s ease-in-out infinite; }

@keyframes chPdfBlink {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.42; }
}

.ch-pdf-plate-badge {
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.ch-pdf-card-foot { display: flex; flex-direction: column; gap: 0.16rem; min-width: 0; }

.ch-pdf-card-name {
    overflow: hidden;
    font-size: 0.78rem;
    font-weight: 500;
    line-height: 1.3;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ch-pdf-card-meta,
.ch-pdf-card-err,
.ch-pdf-card-fix {
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
    letter-spacing: 0.02em;
    color: var(--ch-text-muted);
}

.ch-pdf-card-err { color: var(--ch-pdf-stop-text); }
.ch-pdf-card-fix { color: var(--ch-pdf-stop-text); text-decoration: underline; }

.ch-pdf-card-flag {
    margin-top: 0.15rem;
    padding: 0.12rem 0.35rem;
    border-radius: 4px;
    background-color: var(--ch-pdf-warn-bg);
    color: var(--ch-pdf-warn-text);
    font-family: var(--ch-pdf-mono);
    font-size: 0.62rem;
    line-height: 1.45;
}

.ch-pdf-card-date {
    display: inline-flex;
    align-items: center;
    gap: 0.22rem;
    margin-top: 0.1rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.63rem;
    color: var(--ch-text-muted);
}

.ch-pdf-card-date svg { flex: 0 0 auto; opacity: 0.8; }

/* Two control rows with different jobs. The top row acts on the FILE (inspect,
   turn, drop), the bottom row acts on its POSITION. Splitting them keeps a
   mis-tap on a phone from deleting a scan the user meant to move. */
.ch-pdf-card-ops {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    z-index: 2;
    display: flex;
    gap: 0.2rem;
    opacity: 0;
    transition: opacity 0.15s;
}

.ch-pdf-card:hover .ch-pdf-card-ops,
.ch-pdf-card:focus-within .ch-pdf-card-ops { opacity: 1; }

/* Touch pointers never hover, so the controls have to be permanent there. */
@media (hover: none) {
    .ch-pdf-card-ops { opacity: 1; }
}

.ch-pdf-card-nav {
    display: flex;
    gap: 0.3rem;
    margin-top: 0.5rem;
    padding-top: 0.45rem;
    border-top: 1px solid var(--ch-border);
}

.ch-pdf-cnav,
.ch-pdf-cnav:focus,
.ch-pdf-cnav:active {
    display: inline-flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    height: 26px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-muted);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.ch-pdf-cnav:hover {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

.ch-pdf-cop,
.ch-pdf-cop:focus,
.ch-pdf-cop:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 6px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-muted);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.ch-pdf-cop:hover {
    background-color: var(--ch-text-main);
    border-color: var(--ch-text-main);
    color: #FFFFFF;
}

.ch-pdf-cop.is-drop:hover {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

.ch-pdf-addmore,
.ch-pdf-addmore:focus,
.ch-pdf-addmore:active {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    margin-top: 0.85rem;
    padding: 0.85rem 1rem;
    border: 1px dashed var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: transparent;
    color: var(--ch-text-muted);
    font-size: 0.88rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background-color 0.15s;
}

.ch-pdf-addmore:hover {
    border-color: var(--ch-cta);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

.ch-pdf-addmore-plus {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background-color: var(--ch-cta);
    color: #FFFFFF;
}

.ch-pdf-reorder-hint {
    margin: 0.75rem 0 0;
    font-size: 0.8rem;
    color: var(--ch-text-muted);
}

/* --- options rail --- */
/* Three part column: a fixed title, a body that scrolls, and an action footer
   pinned to the bottom.

   The footer is the load-bearing part. PDF to HTML and HTML to PDF carry ten
   controls each, and in one flowing column that pushed the run button below the
   fold on an ordinary 1080p desktop - the primary action of the page, invisible
   until you scrolled past settings you had already finished with. Capping the
   rail at the viewport and pinning the meter and the button means the button is
   on screen at every window height, and the empty gap beside a short file list
   disappears with it. */
.ch-pdf-rail {
    position: sticky;
    top: 110px;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 130px);
    padding: 1.2rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
    box-shadow: var(--ch-pdf-shadow);
}

.ch-pdf-rail-title {
    flex: 0 0 auto;
    margin: 0 0 1.1rem;
    padding-bottom: 0.8rem;
    border-bottom: 1px solid var(--ch-border);
    font-size: 1.02rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

/* min-height:0 is not optional. A flex item defaults to min-height:auto, which
   refuses to shrink below its content, so without it the rail grows past
   max-height and the pinned footer leaves the screen again. */
.ch-pdf-rail-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Room for the scrollbar so a control never sits under it. */
    margin-right: -0.4rem;
    padding-right: 0.4rem;
    scrollbar-width: thin;
    scrollbar-color: var(--ch-border) transparent;
}

.ch-pdf-rail-scroll::-webkit-scrollbar { width: 5px; }
.ch-pdf-rail-scroll::-webkit-scrollbar-track { background: transparent; }
.ch-pdf-rail-scroll::-webkit-scrollbar-thumb { background: var(--ch-border); border-radius: 5px; }

.ch-pdf-rail-foot {
    flex: 0 0 auto;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--ch-border);
}

.ch-pdf-rail-body { display: flex; flex-direction: column; gap: 1.05rem; }

/* --- collapsed advanced options ------------------------------------------
   A tool with ten controls has two or three that everybody touches and seven
   that exist for the one person who needs them. Printing all ten at equal
   weight is how a rail becomes a wall. */
.ch-pdf-advanced {
    margin-top: 0.2rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
}

.ch-pdf-advanced > summary {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.6rem 0.75rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--ch-text-muted);
    cursor: pointer;
    list-style: none;
    transition: color 0.15s;
}

.ch-pdf-advanced > summary::-webkit-details-marker { display: none; }
.ch-pdf-advanced > summary:hover { color: var(--ch-pdf-accent); }

.ch-pdf-advanced-caret {
    display: inline-flex;
    transition: transform 0.18s var(--ch-pdf-ease);
}

.ch-pdf-advanced[open] .ch-pdf-advanced-caret { transform: rotate(90deg); }

.ch-pdf-advanced-count {
    margin-left: auto;
    font-family: var(--ch-pdf-mono);
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--ch-text-muted);
}

.ch-pdf-advanced-body {
    display: flex;
    flex-direction: column;
    gap: 1.05rem;
    padding: 0.3rem 0.75rem 0.85rem;
}

.ch-pdf-rail-note,
.ch-pdf-opt-hint {
    margin: 0;
    font-size: 0.76rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

.ch-pdf-opt { display: flex; flex-direction: column; gap: 0.45rem; }

.ch-pdf-opt-label {
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: -0.005em;
}

.ch-pdf-seg {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0.3rem;
}

.ch-pdf-seg-btn,
.ch-pdf-seg-btn:focus,
.ch-pdf-seg-btn:active {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    padding: 0.55rem 0.35rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-muted);
    font-size: 0.74rem;
    font-weight: 500;
    line-height: 1.25;
    text-align: center;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-seg-btn:hover {
    background-color: var(--ch-pdf-tile-hi);
    border-color: var(--ch-pdf-line-hard);
    color: var(--ch-text-main);
}

.ch-pdf-seg-btn.is-on,
.ch-pdf-seg-btn.is-on:focus,
.ch-pdf-seg-btn.is-on:active {
    border-color: var(--ch-cta);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

.ch-pdf-seg-btn.is-on:hover {
    background-color: var(--ch-pdf-accent-bg-hi);
    color: var(--ch-pdf-accent);
}

.ch-pdf-select,
.ch-pdf-input {
    width: 100%;
    padding: 0.55rem 0.65rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-family: inherit;
    font-size: 0.85rem;
}

.ch-pdf-select:focus,
.ch-pdf-input:focus { border-color: var(--ch-pdf-accent-line); outline: none; }

/* --- the paste field ---
   One tool has an input that is typed rather than dropped. It is set in the
   mono face because what goes in it is markup, and it scrolls horizontally
   rather than wrapping because a wrapped tag is harder to read than a scrolled
   one. resize: vertical is deliberate: growing it downward is useful, and
   letting it grow sideways would break the rail out of its column. */
.ch-pdf-area {
    width: 100%;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-family: var(--ch-pdf-mono);
    font-size: 0.76rem;
    line-height: 1.55;
    tab-size: 2;
    resize: vertical;
    min-height: 7rem;
    max-height: 26rem;
    overflow: auto;
}

.ch-pdf-area:focus { border-color: var(--ch-pdf-accent-line); outline: none; }

.ch-pdf-area::placeholder { color: var(--ch-pdf-line-hard); opacity: 1; }

.ch-pdf-opt.is-area { gap: 0.4rem; }

.ch-pdf-opt.is-switch { gap: 0.35rem; }

.ch-pdf-switch {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
    cursor: pointer;
}

.ch-pdf-switch input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.ch-pdf-switch-box {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    margin-top: 1px;
    border: 1px solid var(--ch-pdf-line-hard);
    border-radius: 5px;
    background-color: var(--ch-pdf-surface);
    color: transparent;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-switch input:checked + .ch-pdf-switch-box {
    border-color: var(--ch-cta);
    background-color: var(--ch-cta);
    color: #FFFFFF;
}

.ch-pdf-switch input:focus-visible + .ch-pdf-switch-box {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

.ch-pdf-switch-text { font-size: 0.82rem; font-weight: 500; line-height: 1.4; }

/* --- quota meter --- */
.ch-pdf-meter {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin: 1.2rem 0 0;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
}

.ch-pdf-meter.is-empty {
    border-color: var(--ch-pdf-warn-line);
    background-color: var(--ch-pdf-warn-bg);
}

.ch-pdf-meter.is-premium {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
}

.ch-pdf-meter-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
}

.ch-pdf-pips { display: inline-flex; gap: 3px; }

.ch-pdf-pip {
    width: 16px;
    height: 5px;
    border-radius: 3px;
    background-color: var(--ch-cta);
}

.ch-pdf-pip.is-spent { background-color: var(--ch-pdf-line-soft); }

.ch-pdf-meter-sub { font-size: 0.73rem; line-height: 1.5; color: var(--ch-text-muted); }

.ch-pdf-meter-cta {
    align-self: flex-start;
    margin-top: 0.15rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ch-pdf-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* --- run button --- */
.ch-pdf-run,
.ch-pdf-run:focus,
.ch-pdf-run:active {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    margin-top: 1.1rem;
    padding: 0.9rem 1.2rem;
    border: 0;
    border-radius: 10px;
    background-color: var(--ch-cta);
    color: #FFFFFF;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 14px 26px -16px rgba(16, 185, 129, 0.95);
    transition: background-color 0.16s, transform 0.16s var(--ch-pdf-ease);
}

.ch-pdf-run:hover:not(:disabled) {
    background-color: #059669;
    color: #FFFFFF;
    transform: translateY(-1px);
}

.ch-pdf-run:disabled,
.ch-pdf-run:disabled:hover {
    background-color: var(--ch-pdf-line-soft);
    color: var(--ch-pdf-surface);
    cursor: not-allowed;
    box-shadow: none;
    transform: none;
}

.ch-pdf-run-hint {
    margin: 0.5rem 0 0;
    font-size: 0.76rem;
    text-align: center;
    color: var(--ch-text-muted);
}

.ch-pdf-rail-privacy {
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
    margin: 1.1rem 0 0;
    padding-top: 0.9rem;
    border-top: 1px solid var(--ch-border);
    font-size: 0.74rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

.ch-pdf-rail-privacy svg { flex: 0 0 auto; margin-top: 2px; }

/* Below the two-column breakpoint the rail is not a column beside the canvas,
   it is a block stacked under it. Every one of the desktop constraints has to
   come off, and the scroll container above all: a max-height plus overflow on a
   full width block is a region a touch drag can enter and never leave, which is
   the exact trap the article sidebar shipped with in style.css. Nothing here
   may reintroduce it. */
@media (max-width: 900px) {
    .ch-pdf-work { grid-template-columns: minmax(0, 1fr); }

    .ch-pdf-rail {
        position: static;
        display: block;
        max-height: none;
    }

    .ch-pdf-rail-scroll {
        overflow: visible;
        min-height: 0;
        margin-right: 0;
        padding-right: 0;
    }

    /* The split file list caps itself on desktop so the run button stays in
       view inside the rail. On a phone the rail is a block in the normal flow,
       so the cap buys nothing and costs a 260px scroll box sitting in the
       middle of the page for a finger to get stuck in. Let it run its full
       height instead. */
    .ch-pdf-plan-files {
        max-height: none;
        overflow-y: visible;
    }
}

/* ==========================================================================
   4.5 PREVIEW LIGHTBOX
   Layout is a three row grid: a header that never scrolls, a stage that takes
   every pixel left over, and a footer of controls. That order matters on a
   phone in landscape, where the stage would otherwise push the close button
   and the navigation off the bottom of the screen.
   ========================================================================== */
.ch-pdf-lightbox[hidden] { display: none !important; }

.ch-pdf-lightbox {
    position: fixed;
    inset: 0;
    z-index: 9998;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(0.5rem, 2vw, 1.5rem);
}

.ch-pdf-lb-veil {
    position: absolute;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.78);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.ch-pdf-lb-card {
    position: relative;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    width: min(1100px, 100%);
    height: min(92vh, 100%);
    /* 100dvh keeps the dialog off the mobile browser's collapsing toolbar,
       which is the difference between a visible close button and a user
       reloading the page to escape. */
    max-height: 100dvh;
    overflow: hidden;
    border: 1px solid var(--ch-border);
    border-radius: 16px;
    background-color: var(--ch-pdf-surface);
    box-shadow: 0 40px 80px -30px rgba(15, 23, 42, 0.7);
    animation: chPdfLbIn 0.22s var(--ch-pdf-ease);
}

@keyframes chPdfLbIn {
    from { opacity: 0; transform: scale(0.985); }
    to   { opacity: 1; transform: none; }
}

.ch-pdf-lb-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.85rem 0.9rem 0.85rem 1.15rem;
    border-bottom: 1px solid var(--ch-border);
}

.ch-pdf-lb-id { min-width: 0; }

.ch-pdf-lb-count {
    display: block;
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-lb-title {
    margin: 0.1rem 0 0;
    overflow: hidden;
    font-size: 0.98rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Close is deliberately oversized and permanently visible. Hunting for the way
   out of a full screen overlay on a phone is the single worst thing a modal
   can do, so it clears the 44px touch target with room to spare. */
.ch-pdf-lb-x,
.ch-pdf-lb-x:focus,
.ch-pdf-lb-x:active {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.ch-pdf-lb-x:hover {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

.ch-pdf-lb-stage {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
    padding: clamp(0.75rem, 2.5vw, 1.75rem);
    background-color: var(--ch-pdf-well);
}

.ch-pdf-lb-stage .ch-pdf-page {
    max-width: 100%;
    max-height: 100%;
    filter: drop-shadow(0 10px 30px rgba(15, 23, 42, 0.35));
}

.ch-pdf-lb-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    background-color: #FFFFFF;
    box-shadow: 0 10px 30px -10px rgba(15, 23, 42, 0.5);
}

.ch-pdf-lb-none {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    margin: 0;
    color: var(--ch-text-muted);
    font-size: 0.9rem;
}

.ch-pdf-lb-foot {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: 0.75rem;
    padding: 0.8rem 1.15rem;
    border-top: 1px solid var(--ch-border);
}

.ch-pdf-lb-meta {
    margin: 0;
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    line-height: 1.5;
    color: var(--ch-text-muted);
}

.ch-pdf-lb-ops { display: flex; gap: 0.4rem; justify-self: center; }

.ch-pdf-lb-btn,
.ch-pdf-lb-btn:focus,
.ch-pdf-lb-btn:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-width: 44px;
    height: 44px;
    padding: 0 0.75rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-size: 0.84rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-lb-btn:hover:not(:disabled) {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

.ch-pdf-lb-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.ch-pdf-lb-keys {
    justify-self: end;
    margin: 0;
    font-size: 0.7rem;
    color: var(--ch-text-muted);
}

.ch-pdf-lb-keys kbd {
    padding: 0.1rem 0.32rem;
    border: 1px solid var(--ch-border);
    border-bottom-width: 2px;
    border-radius: 4px;
    background-color: var(--ch-pdf-tile);
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
}

/* Below the tablet break the footer stacks and the keyboard hint goes away,
   because there is no keyboard. */
@media (max-width: 720px) {
    .ch-pdf-lb-foot {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        gap: 0.6rem;
        text-align: center;
    }
    .ch-pdf-lb-ops { width: 100%; }
    .ch-pdf-lb-btn { flex: 1; }
    .ch-pdf-lb-keys { display: none; }
    .ch-pdf-lb-card { width: 100%; height: 100%; border-radius: 12px; }
}

/* ==========================================================================
   4.6 PAGE BOARD
   The five tools in the Organize group replace the file grid with a grid of
   PAGES. Everything here is sized for a document with hundreds of them, so the
   tile is deliberately small and the controls are permanent rather than
   revealed on hover: a board where you have to hover to find out what you can
   do is a board nobody explores on a touch screen.
   ========================================================================== */
.ch-pdf-boardbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.9rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--ch-border);
}

.ch-pdf-board { min-height: 120px; }

.ch-pdf-pgs {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
    gap: 0.7rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-pdf-pg-empty {
    margin: 0;
    padding: 2.4rem 1.4rem;
    border: 1px dashed var(--ch-pdf-line-soft);
    border-radius: 12px;
    color: var(--ch-text-muted);
    font-size: 0.9rem;
    line-height: 1.6;
    text-align: center;
}

.ch-pdf-pg {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* The tile face is SQUARE, and that is a load-bearing decision rather than a
   visual one. A quarter turn of a square frame stays inside the square, so
   rotating a page is one transform with no measuring, no compensating scale
   and no resize listener - and portrait and landscape pages sit in the same
   grid without ragged rows. */
.ch-pdf-pg-face {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1 / 1;
    padding: 5px;
    border: 1px solid var(--ch-border);
    border-radius: 9px;
    background-color: var(--ch-pdf-well);
    overflow: hidden;
    transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;
}

/* The cursor states what clicking the tile actually does, which differs by
   mode. Only in select mode is the face itself a control; in organize and scan
   it is a drag handle, and in split the control is the scissors on its edge.
   A pointer cursor over a tile that ignores clicks is a small lie the user
   tests once and then distrusts. */
[data-pg-mode="select"] .ch-pdf-pg-face { cursor: pointer; }
[data-pg-mode="organize"] .ch-pdf-pg-face,
[data-pg-mode="scan"] .ch-pdf-pg-face { cursor: grab; }
[data-pg-mode="organize"] .ch-pdf-pg.is-dragged .ch-pdf-pg-face,
[data-pg-mode="scan"] .ch-pdf-pg.is-dragged .ch-pdf-pg-face { cursor: grabbing; }
[data-pg-mode="split"] .ch-pdf-pg-face { cursor: default; }

.ch-pdf-pg-face:hover { border-color: var(--ch-pdf-line-hard); }

.ch-pdf-pg-face:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

.ch-pdf-pg-shot {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    transition: transform 0.2s var(--ch-pdf-ease);
}

.ch-pdf-pg-img {
    max-width: 100%;
    max-height: 100%;
    /* Always a white plate, in both themes. A rasterised page is a picture of
       paper: on a slate tile a perfectly good scan reads as damaged. */
    background-color: #FFFFFF;
    box-shadow: 0 1px 4px rgba(15, 23, 42, 0.24);
    animation: chPdfThumbIn 0.2s var(--ch-pdf-ease);
}

.ch-pdf-pg.is-rot-90  .ch-pdf-pg-shot { transform: rotate(90deg); }
.ch-pdf-pg.is-rot-180 .ch-pdf-pg-shot { transform: rotate(180deg); }
.ch-pdf-pg.is-rot-270 .ch-pdf-pg-shot { transform: rotate(270deg); }

.ch-pdf-pg-plate {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    border-radius: 5px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-muted);
}

.ch-pdf-pg-plate.is-busy { animation: chPdfPulse 1.6s var(--ch-pdf-ease) infinite; }

.ch-pdf-pg-num {
    position: absolute;
    top: 4px;
    left: 4px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: 5px;
    background-color: var(--ch-text-main);
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.65rem;
    font-weight: 600;
}

.ch-pdf-pg-num.is-cut { background-color: #DC2626; }

.ch-pdf-pg-tag {
    position: absolute;
    right: 4px;
    bottom: 4px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.15rem 0.35rem;
    border-radius: 5px;
    background-color: var(--ch-text-main);
    color: #FFFFFF;
    font-family: var(--ch-pdf-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.ch-pdf-pg-tag.is-on { background-color: var(--ch-cta); }
.ch-pdf-pg-tag.is-orphan { background-color: #94A3B8; }

.ch-pdf-pg-src {
    font-family: var(--ch-pdf-mono);
    font-size: 0.62rem;
    color: var(--ch-text-muted);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* --- selected, deleted, dragged ------------------------------------------ */
.ch-pdf-pg.is-picked .ch-pdf-pg-face {
    border-color: var(--ch-cta);
    box-shadow: 0 0 0 2px var(--ch-pdf-accent-bg-hi);
    background-color: var(--ch-pdf-accent-bg);
}

.ch-pdf-pg.is-cut .ch-pdf-pg-face {
    border-color: var(--ch-pdf-stop-line);
    background-color: var(--ch-pdf-stop-bg);
}

/* A deleted page stays on the board, faded, so it can be put back. Hiding it
   would mean the only way to undo a mis-click is to start the whole document
   again. */
.ch-pdf-pg.is-cut .ch-pdf-pg-shot { opacity: 0.26; filter: grayscale(1); }

.ch-pdf-pg.is-dragged { opacity: 0.4; }

.ch-pdf-pg.is-over .ch-pdf-pg-face {
    border-color: var(--ch-cta);
    box-shadow: -3px 0 0 0 var(--ch-cta);
}

/* --- split bands ---------------------------------------------------------
   Six hues, cycled. They are not decoration: the band IS the answer to "which
   of the files I am about to download does this page end up in", and it has to
   be readable at a glance across a screen holding sixty tiles. */
.ch-pdf-pg.is-band-0 .ch-pdf-pg-face { border-color: #10B981; box-shadow: inset 0 -4px 0 0 #10B981; }
.ch-pdf-pg.is-band-1 .ch-pdf-pg-face { border-color: #3B82F6; box-shadow: inset 0 -4px 0 0 #3B82F6; }
.ch-pdf-pg.is-band-2 .ch-pdf-pg-face { border-color: #A855F7; box-shadow: inset 0 -4px 0 0 #A855F7; }
.ch-pdf-pg.is-band-3 .ch-pdf-pg-face { border-color: #F59E0B; box-shadow: inset 0 -4px 0 0 #F59E0B; }
.ch-pdf-pg.is-band-4 .ch-pdf-pg-face { border-color: #EC4899; box-shadow: inset 0 -4px 0 0 #EC4899; }
.ch-pdf-pg.is-band-5 .ch-pdf-pg-face { border-color: #0EA5E9; box-shadow: inset 0 -4px 0 0 #0EA5E9; }

.ch-pdf-pg.is-band-0 .ch-pdf-pg-tag { background-color: #059669; }
.ch-pdf-pg.is-band-1 .ch-pdf-pg-tag { background-color: #2563EB; }
.ch-pdf-pg.is-band-2 .ch-pdf-pg-tag { background-color: #9333EA; }
.ch-pdf-pg.is-band-3 .ch-pdf-pg-tag { background-color: #B45309; }
.ch-pdf-pg.is-band-4 .ch-pdf-pg-tag { background-color: #DB2777; }
.ch-pdf-pg.is-band-5 .ch-pdf-pg-tag { background-color: #0284C7; }

.ch-pdf-pg.is-orphan .ch-pdf-pg-face { opacity: 0.5; border-style: dashed; }

/* --- per-tile controls --------------------------------------------------- */
.ch-pdf-pg-ops {
    display: flex;
    gap: 0.2rem;
    justify-content: center;
}

.ch-pdf-pg-nav {
    display: flex;
    gap: 0.2rem;
}

.ch-pdf-pgop,
.ch-pdf-pgop:focus,
.ch-pdf-pgop:active,
.ch-pdf-pgnav,
.ch-pdf-pgnav:focus,
.ch-pdf-pgnav:active {
    display: inline-flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    height: 24px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 5px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-muted);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.ch-pdf-pgop:hover,
.ch-pdf-pgnav:hover {
    background-color: var(--ch-text-main);
    border-color: var(--ch-text-main);
    color: #FFFFFF;
}

.ch-pdf-pgop.is-drop:hover { background-color: #DC2626; border-color: #DC2626; color: #FFFFFF; }
.ch-pdf-pgop.is-undo:hover { background-color: var(--ch-cta); border-color: var(--ch-cta); color: #FFFFFF; }

/* The scissors sit ON the trailing edge of the tile rather than in the gap
   after it. A wrapping grid has no dependable gap at the end of a row, and a
   control that vanishes at one breakpoint is a control that does not exist on
   a phone. */
.ch-pdf-pg-cut,
.ch-pdf-pg-cut:focus,
.ch-pdf-pg-cut:active {
    position: absolute;
    top: calc(50% - 32px);
    right: -13px;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 50%;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-muted);
    cursor: pointer;
    box-shadow: var(--ch-pdf-shadow);
    transition: background-color 0.15s, color 0.15s, border-color 0.15s, transform 0.15s;
}

.ch-pdf-pg-cut:hover {
    background-color: var(--ch-text-main);
    border-color: var(--ch-text-main);
    color: #FFFFFF;
    transform: scale(1.08);
}

.ch-pdf-pg-cut.is-on,
.ch-pdf-pg-cut.is-on:focus,
.ch-pdf-pg-cut.is-on:active {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

.ch-pdf-pg-cut.is-on:hover { background-color: #B91C1C; border-color: #B91C1C; color: #FFFFFF; }

/* --- the rail readout ---------------------------------------------------- */
.ch-pdf-planbox { margin-top: 1.1rem; }

.ch-pdf-plan {
    padding: 0.85rem 0.9rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
}

.ch-pdf-plan-head {
    display: block;
    margin-bottom: 0.6rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ch-pdf-accent);
}

.ch-pdf-plan-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.28rem 0;
    border-top: 1px solid var(--ch-border);
    font-size: 0.8rem;
    color: var(--ch-text-muted);
}

.ch-pdf-plan-row:first-of-type { border-top: 0; }

.ch-pdf-plan-row b {
    font-family: var(--ch-pdf-mono);
    font-size: 0.78rem;
    color: var(--ch-text-main);
    text-align: right;
    word-break: break-word;
}

.ch-pdf-plan-row.is-ok b { color: var(--ch-pdf-accent); }
.ch-pdf-plan-row.is-drop b { color: var(--ch-pdf-stop-text); }

.ch-pdf-plan-files {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    /* Twenty-four files is a tall list. Scrolling it inside the rail keeps the
       run button on screen, which is the control the list exists to justify. */
    max-height: 260px;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-pdf-plan-file {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.35rem 0.5rem;
    border-left: 3px solid var(--ch-pdf-line-soft);
    border-radius: 0 5px 5px 0;
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-plan-file b {
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--ch-text-main);
    word-break: break-all;
}

.ch-pdf-plan-file span {
    font-size: 0.7rem;
    color: var(--ch-text-muted);
}

.ch-pdf-plan-file.is-band-0 { border-left-color: #10B981; }
.ch-pdf-plan-file.is-band-1 { border-left-color: #3B82F6; }
.ch-pdf-plan-file.is-band-2 { border-left-color: #A855F7; }
.ch-pdf-plan-file.is-band-3 { border-left-color: #F59E0B; }
.ch-pdf-plan-file.is-band-4 { border-left-color: #EC4899; }
.ch-pdf-plan-file.is-band-5 { border-left-color: #0EA5E9; }

.ch-pdf-plan-more,
.ch-pdf-plan-note,
.ch-pdf-plan-warn {
    margin: 0.4rem 0 0;
    font-size: 0.76rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

.ch-pdf-plan-warn { color: var(--ch-pdf-warn-text); }

/* ==========================================================================
   4.7 SCANNER
   Printed into #chPdfPanel by the Scan to PDF operation. Two columns: the
   viewfinder and its controls on the left, the routes that do not involve this
   machine's camera on the right.
   ========================================================================== */
.ch-pdf-panel { margin-bottom: 1.1rem; }

.ch-pdf-scan {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 1rem;
    padding: 1rem;
    border: 1px solid var(--ch-border);
    border-radius: 14px;
    background-color: var(--ch-pdf-tile);
}

@media (max-width: 860px) {
    .ch-pdf-scan { grid-template-columns: 1fr; }
}

.ch-pdf-scan-stage {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    border-radius: 10px;
    /* Hardcoded near-black in both themes. A viewfinder is a piece of camera
       hardware, and one that turns pale in light mode looks broken. */
    background-color: #020617;
}

.ch-pdf-scan-video {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: none;
}

.ch-pdf-scan-stage.is-live .ch-pdf-scan-video { display: block; }

.ch-pdf-scan-idle {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 1.4rem;
    text-align: center;
    color: #94A3B8;
}

.ch-pdf-scan-idle b { color: #F1F5F9; font-size: 0.92rem; }
.ch-pdf-scan-idle span { font-size: 0.8rem; line-height: 1.55; max-width: 32ch; }

/* Framing guide. Not a crop, and it does not claim to be one - the capture is
   the whole frame. It is there because a document photographed square comes
   out straight, and nothing in this tool corrects perspective. */
.ch-pdf-scan-guide {
    position: absolute;
    inset: 8%;
    display: none;
    border: 1px dashed rgba(16, 185, 129, 0.55);
    border-radius: 6px;
    pointer-events: none;
}

.ch-pdf-scan-stage.is-live .ch-pdf-scan-guide { display: block; }

.ch-pdf-scan-stage.is-flash::after {
    content: "";
    position: absolute;
    inset: 0;
    background-color: #FFFFFF;
    animation: chPdfFlash 0.18s ease-out;
}

@keyframes chPdfFlash {
    from { opacity: 0.85; }
    to   { opacity: 0; }
}

.ch-pdf-scan-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.7rem;
}

.ch-pdf-scan-btn,
.ch-pdf-scan-btn:focus,
.ch-pdf-scan-btn:active {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.8rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-main);
    font-size: 0.82rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-scan-btn:hover {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

.ch-pdf-scan-btn.is-primary,
.ch-pdf-scan-btn.is-primary:focus,
.ch-pdf-scan-btn.is-primary:active {
    background-color: var(--ch-cta);
    border-color: var(--ch-cta);
    color: #FFFFFF;
}

.ch-pdf-scan-btn.is-primary:hover { background-color: #0F9F72; border-color: #0F9F72; color: #FFFFFF; }
.ch-pdf-scan-btn.is-on { background-color: #F59E0B; border-color: #F59E0B; color: #0F172A; }

/* A shutter, drawn as a shutter. A button labelled "Capture" would be honest
   and would also be the only control on this page that does not look like the
   thing it replaces. */
.ch-pdf-scan-shutter,
.ch-pdf-scan-shutter:focus,
.ch-pdf-scan-shutter:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    padding: 0;
    border: 3px solid var(--ch-cta);
    border-radius: 50%;
    background-color: transparent;
    cursor: pointer;
    transition: transform 0.12s var(--ch-pdf-ease), border-color 0.15s;
}

.ch-pdf-scan-shutter span {
    display: block;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background-color: var(--ch-cta);
    transition: background-color 0.15s;
}

.ch-pdf-scan-shutter:hover { transform: scale(1.06); }
.ch-pdf-scan-shutter:active { transform: scale(0.94); }
.ch-pdf-scan-shutter:disabled { opacity: 0.5; cursor: progress; }

.ch-pdf-scan-pick {
    flex: 1;
    min-width: 130px;
    max-width: 260px;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-main);
    font-size: 0.8rem;
}

.ch-pdf-scan-msg,
.ch-pdf-scan-readout {
    margin: 0.6rem 0 0;
    font-size: 0.8rem;
    line-height: 1.55;
}

.ch-pdf-scan-msg {
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--ch-pdf-warn-line);
    border-radius: 8px;
    background-color: var(--ch-pdf-warn-bg);
    color: var(--ch-pdf-warn-text);
}

.ch-pdf-scan-msg.is-stop {
    border-color: var(--ch-pdf-stop-line);
    background-color: var(--ch-pdf-stop-bg);
    color: var(--ch-pdf-stop-text);
}

.ch-pdf-scan-readout {
    font-family: var(--ch-pdf-mono);
    font-size: 0.72rem;
    color: var(--ch-text-muted);
}

.ch-pdf-scan-side {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}

.ch-pdf-scan-alt,
.ch-pdf-scan-alt:focus,
.ch-pdf-scan-alt:active {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.7rem 0.8rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
    color: var(--ch-text-muted);
    font-size: 0.78rem;
    line-height: 1.5;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s;
}

.ch-pdf-scan-alt:hover {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-text-muted);
}

.ch-pdf-scan-alt svg { flex: 0 0 auto; margin-top: 2px; color: var(--ch-pdf-accent); }
.ch-pdf-scan-alt b { display: block; margin-bottom: 0.1rem; color: var(--ch-text-main); font-size: 0.85rem; }

/* ==========================================================================
   4.8 HANDOFF MODAL
   "Use my phone instead". Printed onto <body> by the scanner operation, so it
   sits OUTSIDE .ch-page-pdf and cannot rely on the suite's hidden guard - it
   carries its own, in the attribute-on-the-same-compound form that the
   conversion gate had to learn the hard way. See the note at the top of this
   file before touching the selector.
   ========================================================================== */
.ch-pdf-modal[hidden] { display: none !important; }

.ch-pdf-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.2rem;
}

.ch-pdf-modal-veil {
    position: absolute;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.72);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.ch-pdf-modal-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: min(460px, 100%);
    max-height: min(90vh, 760px);
    overflow-y: auto;
    padding: clamp(1.6rem, 4vw, 2.2rem);
    border: 1px solid var(--ch-border);
    border-radius: 20px;
    background-color: #FFFFFF;
    color: #0F172A;
    text-align: center;
    box-shadow: 0 40px 80px -30px rgba(15, 23, 42, 0.6);
    animation: chPdfGateIn 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

.ch-pdf-modal-x,
.ch-pdf-modal-x:focus,
.ch-pdf-modal-x:active {
    position: absolute;
    top: 0.85rem;
    right: 0.85rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 9px;
    background-color: transparent;
    color: #64748B;
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}

.ch-pdf-modal-x:hover {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

.ch-pdf-modal-kicker {
    font-family: var(--ch-pdf-mono);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ch-pdf-accent);
}

.ch-pdf-modal-title {
    margin: 0.5rem 0 1.1rem;
    font-size: clamp(1.15rem, 3vw, 1.4rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.3;
}

.ch-pdf-modal-qr {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 190px;
    margin-bottom: 1.1rem;
}

/* The symbol is black on white in BOTH themes. A QR code recoloured to match a
   dark interface is a QR code with reduced contrast for the one device that
   has to read it from across a desk, and inverted codes fail outright on a
   meaningful share of phone cameras. */
.ch-pdf-scan-qr {
    display: block;
    border: 10px solid #FFFFFF;
    border-radius: 10px;
    background-color: #FFFFFF;
    box-shadow: 0 4px 18px -6px rgba(15, 23, 42, 0.35);
}

.ch-pdf-modal-qrnote {
    display: block;
    max-width: 24ch;
    font-size: 0.78rem;
    line-height: 1.55;
    color: #64748B;
}

.ch-pdf-modal-lead {
    margin: 0;
    font-size: 0.86rem;
    line-height: 1.65;
    color: #475569;
}

.ch-pdf-modal-lead strong { color: #0F172A; }

.ch-pdf-modal-fine {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 1.1rem 0 0;
    padding-top: 0.95rem;
    border-top: 1px solid var(--ch-border);
    font-size: 0.775rem;
    line-height: 1.6;
    text-align: left;
    color: #64748B;
}

.ch-pdf-modal-fine svg { flex: 0 0 auto; margin-top: 3px; color: var(--ch-pdf-accent); }
.ch-pdf-modal-fine strong { color: #0F172A; }

/* ==========================================================================
   5. PROCESSING AND RESULT
   ========================================================================== */
.ch-pdf-busy,
.ch-pdf-result,
.ch-pdf-roadmap {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: clamp(2rem, 5vw, 3.5rem) 1.5rem;
    border: 1px solid var(--ch-border);
    border-radius: 20px;
    background-color: var(--ch-pdf-surface);
    text-align: center;
    box-shadow: var(--ch-pdf-shadow);
}

.ch-pdf-busy-ico,
.ch-pdf-result-ico,
.ch-pdf-roadmap-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    margin-bottom: 1.1rem;
    border-radius: 18px;
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

/* Of the three wells sharing the rule above, the roadmap one is the only one
   that stands for a specific TOOL, so it takes that tool's tint from the panel
   class. Busy and result are process states and stay emerald on purpose: a
   result panel that changed colour per tool would read as a status code. */
.ch-pdf-roadmap-ico {
    background-color: var(--ch-tint-bg, var(--ch-pdf-accent-bg));
    color: var(--ch-tint, var(--ch-pdf-accent));
}

.ch-pdf-busy-ico { animation: chPdfBlink 1.4s ease-in-out infinite; }

.ch-pdf-busy-title,
.ch-pdf-result-title,
.ch-pdf-roadmap h2 {
    margin: 0 0 0.5rem;
    font-size: clamp(1.3rem, 3vw, 1.7rem);
    letter-spacing: -0.02em;
}

.ch-pdf-busy-sub {
    margin: 0 0 1.3rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    color: var(--ch-text-muted);
}

.ch-pdf-progress {
    overflow: hidden;
    width: min(460px, 100%);
    height: 8px;
    border-radius: 999px;
    background-color: var(--ch-pdf-well);
}

.ch-pdf-progress-bar {
    display: block;
    width: 0;
    height: 100%;
    border-radius: 999px;
    background-image: linear-gradient(90deg, #10B981, #34D399);
    transition: width 0.24s var(--ch-pdf-ease);
}

.ch-pdf-progress-pct {
    margin-top: 0.5rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--ch-pdf-accent);
}

.ch-pdf-busy-note,
.ch-pdf-result-sub {
    max-width: 62ch;
    margin: 1.2rem 0 0;
    font-size: 0.87rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
}

.ch-pdf-result-sub {
    margin-top: 0;
    margin-bottom: 1.6rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.78rem;
    letter-spacing: 0.03em;
}

/* --- conversion findings ---
   Printed at the moment of handover by the tools that have an answer between
   success and failure. PDF/A is the reason this exists: "converted, and here is
   exactly what still does not conform" is the honest result, and a caveat that
   is not on screen when the file is downloaded is a caveat met in front of
   somebody else. Left aligned inside a centred column on purpose, because these
   are sentences to read rather than a status to glance at. */
.ch-pdf-result-notes {
    width: min(640px, 100%);
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
    text-align: left;
}

.ch-pdf-result-notes[hidden] { display: none; }

.ch-pdf-note {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin-bottom: 0.5rem;
    padding: 0.65rem 0.8rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    font-size: 0.83rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
}

.ch-pdf-note:last-child { margin-bottom: 0; }

.ch-pdf-note svg { flex: 0 0 auto; margin-top: 0.15rem; }

.ch-pdf-note.is-ok {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-text-main);
}
.ch-pdf-note.is-ok svg { color: var(--ch-pdf-accent); }

.ch-pdf-note.is-warn {
    border-color: var(--ch-pdf-warn-line);
    background-color: var(--ch-pdf-warn-bg);
    color: var(--ch-pdf-warn-text);
}

.ch-pdf-note.is-stop {
    border-color: var(--ch-pdf-stop-line);
    background-color: var(--ch-pdf-stop-bg);
    color: var(--ch-pdf-stop-text);
}

.ch-pdf-note.is-info svg { color: var(--ch-pdf-line-hard); }

.ch-pdf-downloads {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.7rem;
    width: min(640px, 100%);
}

.ch-pdf-dl {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.85rem 1.5rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
    transition: background-color 0.16s, border-color 0.16s, transform 0.16s var(--ch-pdf-ease);
}

.ch-pdf-dl:hover {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    transform: translateY(-1px);
}

.ch-pdf-dl small {
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--ch-text-muted);
}

.ch-pdf-dl.is-primary {
    padding: 1.05rem 2.2rem;
    border-color: var(--ch-cta);
    background-color: var(--ch-cta);
    color: #FFFFFF;
    font-size: 1.05rem;
    box-shadow: 0 16px 30px -18px rgba(16, 185, 129, 0.95);
}

.ch-pdf-dl.is-primary:hover { background-color: #059669; color: #FFFFFF; }
.ch-pdf-dl.is-primary small { color: rgba(255, 255, 255, 0.78); }

.ch-pdf-restart,
.ch-pdf-restart:focus,
.ch-pdf-restart:active {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 1.3rem;
    padding: 0.5rem 0.9rem;
    border: 1px solid transparent;
    border-radius: 8px;
    background-color: transparent;
    color: var(--ch-text-muted);
    font-size: 0.84rem;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

.ch-pdf-restart:hover {
    border-color: var(--ch-border);
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
}

.ch-pdf-next {
    width: min(640px, 100%);
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--ch-border);
}

.ch-pdf-next-title {
    display: block;
    margin-bottom: 0.8rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-next-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.5rem;
}

.ch-pdf-next-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-size: 0.86rem;
    font-weight: 500;
    text-decoration: none;
    transition: border-color 0.15s, color 0.15s;
}

.ch-pdf-next-item:hover { border-color: var(--ch-pdf-accent-line); color: var(--ch-pdf-accent); }

/* --- roadmap panel for tools that have not shipped --- */
.ch-pdf-roadmap p {
    max-width: 66ch;
    margin: 0 0 0.9rem;
    font-size: 0.95rem;
    line-height: 1.7;
    color: var(--ch-text-muted);
}

.ch-pdf-roadmap-meta { font-size: 0.87rem; }
.ch-pdf-roadmap-meta a { color: var(--ch-pdf-accent); }

.ch-pdf-roadmap-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.8rem;
    margin-top: 0.7rem;
}

.ch-pdf-roadmap-cta {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.8rem 1.5rem;
    border-radius: 10px;
    background-color: var(--ch-cta);
    color: #FFFFFF;
    font-weight: 600;
    text-decoration: none;
}

.ch-pdf-roadmap-cta:hover { background-color: #059669; color: #FFFFFF; }

.ch-pdf-roadmap-alt {
    font-size: 0.87rem;
    color: var(--ch-text-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* ==========================================================================
   6. EDITORIAL CONTENT
   ========================================================================== */
.ch-pdf-content {
    max-width: 820px;
    margin: 0 auto;
}

.ch-pdf-lead {
    margin: 0 0 2rem;
    font-size: 1.08rem;
    line-height: 1.72;
    color: var(--ch-text-muted);
}

.ch-pdf-lead strong { color: var(--ch-text-main); }

.ch-pdf-toc {
    margin: 0 0 2.5rem;
    padding: 1.1rem 1.3rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-toc-title {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.7rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-toc ol {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 0.25rem 1.2rem;
    margin: 0;
    padding: 0 0 0 1.1rem;
    font-size: 0.88rem;
}

.ch-pdf-toc li { margin: 0.18rem 0; }
.ch-pdf-toc a { color: var(--ch-text-main); text-decoration: none; }
.ch-pdf-toc a:hover { color: var(--ch-pdf-accent); text-decoration: underline; }

.ch-pdf-section {
    margin-bottom: 3rem;
    /* Every section is a jump target and .site-header is position: sticky. */
    scroll-margin-top: 110px;
}

.ch-pdf-section h2 {
    margin: 0 0 1rem;
    font-size: clamp(1.5rem, 3vw, 1.9rem);
    letter-spacing: -0.015em;
}

.ch-pdf-section h3 {
    margin: 1.8rem 0 0.6rem;
    font-size: 1.15rem;
}

.ch-pdf-section p {
    margin: 0 0 1.1rem;
    line-height: 1.75;
}

.ch-pdf-section a {
    color: var(--ch-accent);
    text-underline-offset: 2px;
}

/* Scoped to the page rather than to .ch-pdf-content, so a <code> in the hero or
   in any block added later is styled too. carthacker_pdf_kses() permits the tag
   in every copy string, and a chip that is only styled in one region is a chip
   that ships unstyled the first time somebody uses it in another. */
.ch-page-pdf :not(pre) > code {
    padding: 0.12em 0.4em;
    border: 1px solid var(--ch-pdf-code-line);
    border-radius: 4px;
    background-color: var(--ch-pdf-code-bg);
    color: var(--ch-pdf-code-text);
    font-family: var(--ch-pdf-mono);
    font-size: 0.86em;
    word-break: break-word;
}

.ch-pdf-list,
.ch-pdf-oli {
    margin: 0 0 1.3rem;
    padding-left: 1.3rem;
}

.ch-pdf-list li,
.ch-pdf-oli li {
    margin-bottom: 0.6rem;
    line-height: 1.7;
}

.ch-pdf-list { list-style: none; padding-left: 0; }

.ch-pdf-list > li {
    position: relative;
    padding-left: 1.5rem;
}

.ch-pdf-list > li::before {
    content: '';
    position: absolute;
    top: 0.62em;
    left: 0.25rem;
    width: 7px;
    height: 7px;
    border-radius: 2px;
    background-color: var(--ch-cta);
}

.ch-pdf-steps {
    display: grid;
    gap: 1rem;
    margin: 1.6rem 0 1.4rem;
    padding: 0;
    list-style: none;
}

.ch-pdf-steps li {
    display: flex;
    gap: 0.9rem;
    padding: 1.1rem 1.2rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-step-num {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
    font-family: var(--ch-pdf-mono);
    font-size: 0.85rem;
    font-weight: 700;
}

.ch-pdf-steps h3 { margin: 0 0 0.3rem; font-size: 1rem; }
.ch-pdf-steps p { margin: 0; font-size: 0.92rem; line-height: 1.65; color: var(--ch-text-muted); }

.ch-pdf-table-wrap {
    overflow-x: auto;
    margin: 0 0 1.4rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-table {
    width: 100%;
    min-width: 520px;
    border-collapse: collapse;
    font-size: 0.88rem;
}

.ch-pdf-table th,
.ch-pdf-table td {
    padding: 0.72rem 0.9rem;
    border-bottom: 1px solid var(--ch-border);
    text-align: left;
    vertical-align: top;
    line-height: 1.55;
}

.ch-pdf-table thead th {
    background-color: var(--ch-pdf-well);
    font-family: var(--ch-pdf-mono);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-table tbody th { font-weight: 600; }
.ch-pdf-table tbody tr:last-child th,
.ch-pdf-table tbody tr:last-child td { border-bottom: 0; }

.ch-pdf-callout {
    display: flex;
    gap: 0.7rem;
    margin: 0 0 1.4rem;
    padding: 1.1rem 1.2rem;
    border: 1px solid var(--ch-pdf-accent-line);
    border-left-width: 3px;
    border-radius: 10px;
    background-color: var(--ch-pdf-accent-bg);
}

.ch-pdf-callout svg { flex: 0 0 auto; margin-top: 3px; color: var(--ch-pdf-accent); }
.ch-pdf-callout p { margin: 0; font-size: 0.92rem; line-height: 1.65; }

.ch-pdf-spec {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 0.6rem;
    margin: 0 0 1.4rem;
}

.ch-pdf-spec div {
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-spec dt {
    margin-bottom: 0.2rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-spec dd { margin: 0; font-size: 0.86rem; line-height: 1.5; }

/* --- FAQ accordion --- */
.ch-pdf-faq {
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    overflow: hidden;
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-faq-item + .ch-pdf-faq-item { border-top: 1px solid var(--ch-border); }

.ch-pdf-faq-item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.2rem;
    cursor: pointer;
    font-size: 0.98rem;
    font-weight: 600;
    line-height: 1.5;
    list-style: none;
    transition: background-color 0.15s, color 0.15s;
}

.ch-pdf-faq-item summary::-webkit-details-marker { display: none; }

.ch-pdf-faq-item summary::after {
    content: '';
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-right: 2px solid var(--ch-text-muted);
    border-bottom: 2px solid var(--ch-text-muted);
    transform: rotate(45deg) translateY(-2px);
    transition: transform 0.2s var(--ch-pdf-ease);
}

.ch-pdf-faq-item[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.ch-pdf-faq-item summary:hover { background-color: var(--ch-pdf-tile); color: var(--ch-pdf-accent); }
.ch-pdf-faq-item[open] summary { color: var(--ch-pdf-accent); }

.ch-pdf-faq-item p {
    margin: 0;
    padding: 0 1.2rem 1.2rem;
    font-size: 0.92rem;
    line-height: 1.72;
    color: var(--ch-text-muted);
}

/* --- suite navigation on tool pages --- */
.ch-pdf-related {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 1.2rem;
    padding: 1.3rem;
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-pdf-radius);
    background-color: var(--ch-pdf-surface);
}

.ch-pdf-related-head {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.55rem;
    font-family: var(--ch-pdf-mono);
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-pdf-related ul { margin: 0; padding: 0; list-style: none; }

.ch-pdf-related li {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    margin-bottom: 0.3rem;
    font-size: 0.86rem;
}

.ch-pdf-related a { color: var(--ch-text-main); text-decoration: none; }
.ch-pdf-related a:hover { color: var(--ch-pdf-accent); text-decoration: underline; }
.ch-pdf-related li.is-current span { font-weight: 600; color: var(--ch-pdf-accent); }

.ch-pdf-related em {
    font-family: var(--ch-pdf-mono);
    font-size: 0.6rem;
    font-style: normal;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
    opacity: 0.75;
}

/* ==========================================================================
   7. CONVERSION GATE (printed into the footer, outside .ch-page-pdf)
   ========================================================================== */
body.ch-pdf-locked { overflow: hidden; }

.ch-pdf-gate {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.2rem;
}

.ch-pdf-gate-veil {
    position: absolute;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.62);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}

.ch-pdf-gate-card {
    position: relative;
    width: min(620px, 100%);
    max-height: min(88vh, 900px);
    overflow-y: auto;
    padding: clamp(1.5rem, 4vw, 2.4rem);
    border: 1px solid var(--ch-border);
    border-radius: 20px;
    background-color: #FFFFFF;
    color: #0F172A;
    box-shadow: 0 40px 80px -30px rgba(15, 23, 42, 0.6);
    animation: chPdfGateIn 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes chPdfGateIn {
    from { opacity: 0; transform: translateY(14px) scale(0.98); }
    to   { opacity: 1; transform: none; }
}

.ch-pdf-gate-x,
.ch-pdf-gate-x:focus,
.ch-pdf-gate-x:active {
    position: absolute;
    top: 0.9rem;
    right: 0.9rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: transparent;
    color: #64748B;
    cursor: pointer;
}

.ch-pdf-gate-x:hover { background-color: #F1F5F9; color: #0F172A; }

.ch-pdf-gate-kicker {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.9rem;
    padding: 0.3rem 0.65rem;
    border: 1px solid rgba(16, 185, 129, 0.45);
    border-radius: 999px;
    background-color: rgba(16, 185, 129, 0.1);
    color: #059669;
    font-family: var(--ch-pdf-mono);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.ch-pdf-gate-kicker.is-warn {
    border-color: rgba(245, 158, 11, 0.45);
    background-color: rgba(245, 158, 11, 0.12);
    color: #B45309;
}

.ch-pdf-gate-title {
    margin: 0 0 0.7rem;
    font-size: clamp(1.25rem, 3.2vw, 1.62rem);
    line-height: 1.24;
    letter-spacing: -0.02em;
}

.ch-pdf-gate-title span { display: block; margin-top: 0.25rem; color: #059669; }

.ch-pdf-gate-desc {
    margin: 0 0 1.2rem;
    font-size: 0.93rem;
    line-height: 1.68;
    color: #475569;
}

.ch-pdf-gate-desc b { color: #0F172A; }

.ch-pdf-gate-list {
    display: grid;
    gap: 0.75rem;
    margin: 0 0 1.5rem;
    padding: 0;
    list-style: none;
}

.ch-pdf-gate-list li { display: flex; gap: 0.65rem; }

.ch-pdf-gate-ico {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background-color: rgba(16, 185, 129, 0.1);
    color: #059669;
}

.ch-pdf-gate-copy { font-size: 0.87rem; line-height: 1.6; color: #475569; }
.ch-pdf-gate-copy strong { display: block; margin-bottom: 0.1rem; color: #0F172A; }

.ch-pdf-gate-actions { display: flex; flex-direction: column; align-items: center; gap: 0.7rem; }

.ch-pdf-gate-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.95rem 1.4rem;
    border-radius: 10px;
    background-color: #10B981;
    color: #FFFFFF;
    font-size: 1rem;
    font-weight: 600;
    text-decoration: none;
    box-shadow: 0 16px 30px -18px rgba(16, 185, 129, 0.95);
    transition: background-color 0.16s, transform 0.16s;
}

.ch-pdf-gate-cta:hover { background-color: #059669; color: #FFFFFF; transform: translateY(-1px); }

.ch-pdf-gate-alt {
    font-size: 0.87rem;
    color: #475569;
    text-decoration: underline;
    text-underline-offset: 3px;
}

.ch-pdf-gate-alt:hover { color: #0F172A; }

.ch-pdf-gate-skip,
.ch-pdf-gate-skip:focus,
.ch-pdf-gate-skip:active {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    width: 100%;
    padding: 0.7rem 0.9rem;
    border: 1px solid #E2E8F0;
    border-radius: 10px;
    background-color: transparent;
    color: #64748B;
    font-size: 0.84rem;
    text-align: center;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.ch-pdf-gate-skip:hover { background-color: #F8FAFC; border-color: #CBD5E1; color: #0F172A; }
.ch-pdf-gate-skip small { font-size: 0.74rem; color: #94A3B8; }

.ch-pdf-gate-fine {
    margin: 1.2rem 0 0;
    font-size: 0.78rem;
    line-height: 1.6;
    text-align: center;
    color: #64748B;
}

.ch-pdf-gate-fine b { color: #0F172A; }

/* ==========================================================================
   8. MOTION PREFERENCES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    /* One rule for the whole scene system rather than a list of 40 selectors
       that would go stale the next time a motif lands. The scenes are pure
       ornament, so freezing them costs the page nothing. */
    .ch-hs,
    .ch-hs *,
    .ch-hs *::before,
    .ch-hs *::after { animation: none !important; }

    .ch-pdfhero-badge-dot,
    .ch-pdf-plate.is-busy,
    .ch-pdf-pg-plate.is-busy,
    .ch-pdf-pg-img,
    .ch-pdf-busy-ico { animation: none; }

    /* The rotation transform stays: it is information, not decoration. What
       goes is the transition, so a quarter turn is instant rather than
       animated. */
    .ch-pdf-pg-shot,
    .ch-pdf-pg-cut,
    .ch-pdf-scan-shutter { transition: none; }

    .ch-pdf-scan-stage.is-flash::after { animation: none; opacity: 0; }
    .ch-pdf-pg-cut:hover,
    .ch-pdf-scan-shutter:hover,
    .ch-pdf-scan-shutter:active { transform: none; }

    .ch-pdf-hub-card,
    .ch-pdf-dl,
    .ch-pdf-run,
    .ch-pdf-gate-cta,
    .ch-pdf-gate-card { transition: none; animation: none; }

    .ch-pdf-hub-card:hover,
    .ch-pdf-dl:hover,
    .ch-pdf-run:hover:not(:disabled),
    .ch-pdf-gate-cta:hover { transform: none; }
}

/* ==========================================================================
   9. DARK THEME
   Surfaces ladder (dark): page #0F172A > cards #1E293B > wells #0F172A
   > deep accents #020617. Hover lift #273449. Mirrors dark-mode.css.
   ========================================================================== */
/* The same root list as the light tokens at the top of this file. A root that
   appears there and not here renders in light surfaces on a dark page, which
   is the second half of the same bug. */
html[data-theme="dark"] .ch-page-pdf,
html[data-theme="dark"] .ch-pdf-gate,
html[data-theme="dark"] .ch-pdf-modal,
html[data-theme="dark"] .ch-sig-modal,
html[data-theme="dark"] .ch-ed {
    --ch-pdf-surface: #1E293B;
    --ch-pdf-well: #0F172A;
    --ch-pdf-tile: #263349;
    --ch-pdf-tile-hi: #2F3D55;

    --ch-pdf-line-soft: #475569;
    --ch-pdf-line-hard: #64748B;

    /* Emerald 400 rather than 600: the darker step fails contrast on slate. */
    --ch-pdf-accent: #34D399;
    --ch-pdf-accent-bg: rgba(16, 185, 129, 0.14);
    --ch-pdf-accent-bg-hi: rgba(16, 185, 129, 0.22);
    --ch-pdf-accent-line: rgba(52, 211, 153, 0.42);

    /* A slate lift rather than a slate solid, for the reason given on the light
       tokens: the chip has to stay visible on the page (#0F172A), on a card
       (#1E293B) and on a shaded table header, and no solid does all three. */
    --ch-pdf-code-bg: rgba(148, 163, 184, 0.18);
    --ch-pdf-code-text: #E2E8F0;
    --ch-pdf-code-line: rgba(148, 163, 184, 0.28);

    --ch-pdf-warn-bg: rgba(245, 158, 11, 0.14);
    --ch-pdf-warn-line: rgba(245, 158, 11, 0.4);
    --ch-pdf-warn-text: #FCD34D;
    --ch-pdf-stop-bg: rgba(239, 68, 68, 0.13);
    --ch-pdf-stop-line: rgba(239, 68, 68, 0.4);
    --ch-pdf-stop-text: #FCA5A5;

    --ch-pdf-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px -16px rgba(0, 0, 0, 0.7);
    --ch-pdf-shadow-lift: 0 14px 30px -14px rgba(0, 0, 0, 0.8);
}

/* Tool tile tints, dark. Same reason the emerald flips one step in the block
   above: a 600 or 700 hue that holds on white disappears into #1E293B. These
   are the 300-400 steps, and the washes carry more alpha because a 10% tint of
   a bright hue over a dark card is invisible where the same 10% over white is
   already a visible panel. */
html[data-theme="dark"] .ch-tint-emerald { --ch-tint: #34D399; --ch-tint-bg: rgba(16, 185, 129, 0.16); --ch-tint-bg-hi: rgba(16, 185, 129, 0.26); --ch-tint-line: rgba(52, 211, 153, 0.42); }
html[data-theme="dark"] .ch-tint-teal    { --ch-tint: #2DD4BF; --ch-tint-bg: rgba(45, 212, 191, 0.15); --ch-tint-bg-hi: rgba(45, 212, 191, 0.25); --ch-tint-line: rgba(45, 212, 191, 0.42); }
html[data-theme="dark"] .ch-tint-cyan    { --ch-tint: #22D3EE; --ch-tint-bg: rgba(34, 211, 238, 0.15); --ch-tint-bg-hi: rgba(34, 211, 238, 0.25); --ch-tint-line: rgba(34, 211, 238, 0.42); }
html[data-theme="dark"] .ch-tint-sky     { --ch-tint: #38BDF8; --ch-tint-bg: rgba(56, 189, 248, 0.15); --ch-tint-bg-hi: rgba(56, 189, 248, 0.25); --ch-tint-line: rgba(56, 189, 248, 0.42); }
html[data-theme="dark"] .ch-tint-blue    { --ch-tint: #60A5FA; --ch-tint-bg: rgba(96, 165, 250, 0.16); --ch-tint-bg-hi: rgba(96, 165, 250, 0.26); --ch-tint-line: rgba(96, 165, 250, 0.42); }
html[data-theme="dark"] .ch-tint-indigo  { --ch-tint: #A5B4FC; --ch-tint-bg: rgba(129, 140, 248, 0.18); --ch-tint-bg-hi: rgba(129, 140, 248, 0.28); --ch-tint-line: rgba(129, 140, 248, 0.45); }
html[data-theme="dark"] .ch-tint-violet  { --ch-tint: #C4B5FD; --ch-tint-bg: rgba(167, 139, 250, 0.18); --ch-tint-bg-hi: rgba(167, 139, 250, 0.28); --ch-tint-line: rgba(167, 139, 250, 0.45); }
html[data-theme="dark"] .ch-tint-fuchsia { --ch-tint: #E879F9; --ch-tint-bg: rgba(232, 121, 249, 0.15); --ch-tint-bg-hi: rgba(232, 121, 249, 0.25); --ch-tint-line: rgba(232, 121, 249, 0.42); }
html[data-theme="dark"] .ch-tint-rose    { --ch-tint: #FDA4AF; --ch-tint-bg: rgba(251, 113, 133, 0.16); --ch-tint-bg-hi: rgba(251, 113, 133, 0.26); --ch-tint-line: rgba(251, 113, 133, 0.42); }
html[data-theme="dark"] .ch-tint-red     { --ch-tint: #FCA5A5; --ch-tint-bg: rgba(248, 113, 113, 0.16); --ch-tint-bg-hi: rgba(248, 113, 113, 0.26); --ch-tint-line: rgba(248, 113, 113, 0.42); }
html[data-theme="dark"] .ch-tint-orange  { --ch-tint: #FDBA74; --ch-tint-bg: rgba(251, 146, 60, 0.16); --ch-tint-bg-hi: rgba(251, 146, 60, 0.26); --ch-tint-line: rgba(251, 146, 60, 0.42); }
html[data-theme="dark"] .ch-tint-amber   { --ch-tint: #FCD34D; --ch-tint-bg: rgba(251, 191, 36, 0.16); --ch-tint-bg-hi: rgba(251, 191, 36, 0.26); --ch-tint-line: rgba(251, 191, 36, 0.42); }
html[data-theme="dark"] .ch-tint-green   { --ch-tint: #86EFAC; --ch-tint-bg: rgba(74, 222, 128, 0.15); --ch-tint-bg-hi: rgba(74, 222, 128, 0.25); --ch-tint-line: rgba(74, 222, 128, 0.42); }
html[data-theme="dark"] .ch-tint-slate   { --ch-tint: #CBD5E1; --ch-tint-bg: rgba(148, 163, 184, 0.16); --ch-tint-bg-hi: rgba(148, 163, 184, 0.26); --ch-tint-line: rgba(148, 163, 184, 0.42); }

/* The tint flips to a 300-400 step in dark, and white type on Amber 300 is
   unreadable. Anything painted ON the tint takes slate ink instead. The
   redaction bars are exempt on purpose: a redaction bar is black in both
   themes because that is what a redaction bar is. */
html[data-theme="dark"] .ch-hs-tag,
html[data-theme="dark"] .ch-hs-seal {
    color: #0F172A;
}

html[data-theme="dark"] .ch-pdfhero {
    --ch-ph-panel: #1E293B;
    --ch-ph-panel-2: #0F172A;
    --ch-ph-line: #334155;
    --ch-ph-grid: rgba(148, 163, 184, 0.08);
    --ch-ph-glow: rgba(16, 185, 129, 0.14);
    --ch-ph-sheet: #E2E8F0;
    --ch-ph-sheet-line: #CBD5E1;
    --ch-ph-ink: rgba(15, 23, 42, 0.2);

    box-shadow: 0 30px 60px -44px rgba(0, 0, 0, 0.9);
}

html[data-theme="dark"] .ch-pdf-chip.is-active,
html[data-theme="dark"] .ch-pdf-chip.is-active:focus,
html[data-theme="dark"] .ch-pdf-chip.is-active:active,
html[data-theme="dark"] .ch-pdf-chip.is-active:hover {
    background-color: #F1F5F9;
    border-color: #F1F5F9;
    color: #0F172A;
}

html[data-theme="dark"] .ch-pdf-card-idx {
    background-color: #F1F5F9;
    color: #0F172A;
}

/* The rail's own scrollbar. Same treatment the article sidebar gets in
   dark-mode.css: --ch-border is too dark to see against a slate panel. */
html[data-theme="dark"] .ch-pdf-rail-scroll { scrollbar-color: #475569 transparent; }
html[data-theme="dark"] .ch-pdf-rail-scroll::-webkit-scrollbar-thumb { background: #475569; }

html[data-theme="dark"] .ch-pdf-cop:hover {
    background-color: #F1F5F9;
    border-color: #F1F5F9;
    color: #0F172A;
}

/* .ch-pdf-thumb-img deliberately has NO dark override: its white plate is
   theme-independent by design, for the reason documented on the base rule. */

html[data-theme="dark"] .ch-pdf-run:disabled,
html[data-theme="dark"] .ch-pdf-run:disabled:hover {
    background-color: #334155;
    color: #64748B;
}

html[data-theme="dark"] .ch-pdf-pip.is-spent { background-color: #475569; }

/* The lightbox chrome follows the theme; the paper inside it never does, for
   the same reason the card previews do not. */
html[data-theme="dark"] .ch-pdf-lb-x:hover {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

html[data-theme="dark"] .ch-pdf-cnav:hover {
    background-color: var(--ch-pdf-accent-bg-hi);
    color: var(--ch-pdf-accent);
}

html[data-theme="dark"] .ch-pdf-gate-card {
    border-color: #334155;
    background-color: #1E293B;
    color: #F1F5F9;
}

html[data-theme="dark"] .ch-pdf-gate-title span { color: #34D399; }
html[data-theme="dark"] .ch-pdf-gate-desc { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-desc b { color: #F1F5F9; }
html[data-theme="dark"] .ch-pdf-gate-copy { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-copy strong { color: #F1F5F9; }
html[data-theme="dark"] .ch-pdf-gate-kicker { color: #34D399; }
html[data-theme="dark"] .ch-pdf-gate-ico { color: #34D399; }
html[data-theme="dark"] .ch-pdf-gate-alt { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-alt:hover { color: #F1F5F9; }
html[data-theme="dark"] .ch-pdf-gate-fine { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-fine b { color: #F1F5F9; }

html[data-theme="dark"] .ch-pdf-gate-x { border-color: #334155; color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-x:hover { background-color: #273449; color: #F1F5F9; }

html[data-theme="dark"] .ch-pdf-gate-skip { border-color: #334155; color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-gate-skip:hover { background-color: #273449; border-color: #475569; color: #F1F5F9; }

/* --- page board ---------------------------------------------------------
   The page number chip and the tile controls invert, the same way the file
   card index does: a near-black chip on a slate tile disappears. The page
   image itself deliberately has NO dark override, for the reason documented
   on its base rule - it is a picture of paper. */
html[data-theme="dark"] .ch-pdf-pg-num,
html[data-theme="dark"] .ch-pdf-pg-tag {
    background-color: #F1F5F9;
    color: #0F172A;
}

html[data-theme="dark"] .ch-pdf-pg-num.is-cut { background-color: #EF4444; color: #FFFFFF; }
html[data-theme="dark"] .ch-pdf-pg-tag.is-on { background-color: #34D399; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg-tag.is-orphan { background-color: #475569; color: #E2E8F0; }

html[data-theme="dark"] .ch-pdf-pgop:hover,
html[data-theme="dark"] .ch-pdf-pgnav:hover,
html[data-theme="dark"] .ch-pdf-pg-cut:hover {
    background-color: #F1F5F9;
    border-color: #F1F5F9;
    color: #0F172A;
}

html[data-theme="dark"] .ch-pdf-pgop.is-drop:hover,
html[data-theme="dark"] .ch-pdf-pg-cut.is-on,
html[data-theme="dark"] .ch-pdf-pg-cut.is-on:focus,
html[data-theme="dark"] .ch-pdf-pg-cut.is-on:active {
    background-color: #DC2626;
    border-color: #DC2626;
    color: #FFFFFF;
}

html[data-theme="dark"] .ch-pdf-pgop.is-undo:hover {
    background-color: #34D399;
    border-color: #34D399;
    color: #0F172A;
}

/* The band hues step up two stops on slate. Emerald 500 and Blue 600 both fail
   as a 4px rule against #0F172A, and a band nobody can distinguish is a split
   preview that communicates nothing. */
html[data-theme="dark"] .ch-pdf-pg.is-band-0 .ch-pdf-pg-face { border-color: #34D399; box-shadow: inset 0 -4px 0 0 #34D399; }
html[data-theme="dark"] .ch-pdf-pg.is-band-1 .ch-pdf-pg-face { border-color: #60A5FA; box-shadow: inset 0 -4px 0 0 #60A5FA; }
html[data-theme="dark"] .ch-pdf-pg.is-band-2 .ch-pdf-pg-face { border-color: #C084FC; box-shadow: inset 0 -4px 0 0 #C084FC; }
html[data-theme="dark"] .ch-pdf-pg.is-band-3 .ch-pdf-pg-face { border-color: #FBBF24; box-shadow: inset 0 -4px 0 0 #FBBF24; }
html[data-theme="dark"] .ch-pdf-pg.is-band-4 .ch-pdf-pg-face { border-color: #F472B6; box-shadow: inset 0 -4px 0 0 #F472B6; }
html[data-theme="dark"] .ch-pdf-pg.is-band-5 .ch-pdf-pg-face { border-color: #38BDF8; box-shadow: inset 0 -4px 0 0 #38BDF8; }

html[data-theme="dark"] .ch-pdf-pg.is-band-0 .ch-pdf-pg-tag { background-color: #34D399; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg.is-band-1 .ch-pdf-pg-tag { background-color: #60A5FA; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg.is-band-2 .ch-pdf-pg-tag { background-color: #C084FC; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg.is-band-3 .ch-pdf-pg-tag { background-color: #FBBF24; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg.is-band-4 .ch-pdf-pg-tag { background-color: #F472B6; color: #0F172A; }
html[data-theme="dark"] .ch-pdf-pg.is-band-5 .ch-pdf-pg-tag { background-color: #38BDF8; color: #0F172A; }

html[data-theme="dark"] .ch-pdf-plan-file.is-band-0 { border-left-color: #34D399; }
html[data-theme="dark"] .ch-pdf-plan-file.is-band-1 { border-left-color: #60A5FA; }
html[data-theme="dark"] .ch-pdf-plan-file.is-band-2 { border-left-color: #C084FC; }
html[data-theme="dark"] .ch-pdf-plan-file.is-band-3 { border-left-color: #FBBF24; }
html[data-theme="dark"] .ch-pdf-plan-file.is-band-4 { border-left-color: #F472B6; }
html[data-theme="dark"] .ch-pdf-plan-file.is-band-5 { border-left-color: #38BDF8; }

/* --- scanner ------------------------------------------------------------
   The viewfinder is already hardcoded near-black in both themes, so nothing
   there moves. The chrome around it follows the theme like every other panel. */
html[data-theme="dark"] .ch-pdf-scan-btn.is-primary:hover {
    background-color: #34D399;
    border-color: #34D399;
    color: #0F172A;
}

html[data-theme="dark"] .ch-pdf-scan-btn.is-on { background-color: #FBBF24; border-color: #FBBF24; color: #0F172A; }

html[data-theme="dark"] .ch-pdf-scan-alt:hover { color: #E2E8F0; }

/* --- handoff modal ------------------------------------------------------ */
html[data-theme="dark"] .ch-pdf-modal-card {
    border-color: #334155;
    background-color: #1E293B;
    color: #F1F5F9;
}

html[data-theme="dark"] .ch-pdf-modal-kicker { color: #34D399; }
html[data-theme="dark"] .ch-pdf-modal-lead { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-modal-lead strong { color: #F1F5F9; }
html[data-theme="dark"] .ch-pdf-modal-fine { border-top-color: #334155; color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-modal-fine strong { color: #F1F5F9; }
html[data-theme="dark"] .ch-pdf-modal-fine svg { color: #34D399; }
html[data-theme="dark"] .ch-pdf-modal-qrnote { color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-modal-x { border-color: #334155; color: #94A3B8; }
html[data-theme="dark"] .ch-pdf-modal-x:hover { background-color: #DC2626; border-color: #DC2626; color: #FFFFFF; }

/* No dark override on .ch-pdf-scan-qr: the symbol stays black on white in both
   themes because the device reading it is a phone camera pointed at a screen,
   and an inverted code fails on a meaningful share of them. The white border
   is part of the symbol - it is the quiet zone a scanner needs. */
html[data-theme="dark"] .ch-pdf-gate-skip small { color: #64748B; }

/* ==========================================================================
   10. NARROW VIEWPORTS
   ========================================================================== */
@media (max-width: 640px) {
    .ch-pdf-drop { min-height: 260px; padding: 2rem 1rem; }
    .ch-pdf-drop-cta { width: 100%; padding: 0.9rem 1rem; }
    .ch-pdf-files { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
    .ch-pdf-hub-controls { flex-direction: column; align-items: stretch; }
    .ch-pdf-search { width: 100%; }
    .ch-pdf-seg { grid-auto-flow: row; grid-auto-columns: auto; }
    .ch-pdf-toc ol { grid-template-columns: minmax(0, 1fr); }
    .ch-pdf-downloads { flex-direction: column; }
    .ch-pdf-dl { justify-content: center; }
}

/* ==========================================================================
   11. THE EDIT GROUP: OPTION CONTROLS AND TILE OVERLAYS

   Five tools put something ON a page rather than rearranging pages, and they
   needed four controls the rail did not have (a colour, a slider, a number and
   a file) plus a way for a page tile to show what will be printed on it.

   Everything here is scoped under .ch-page-pdf like the rest of the file. The
   workspace in section 12 is not, and cannot be: it is appended to <body> so it
   can sit above a sticky header, exactly like the conversion gate.
   ========================================================================== */

/* --- Colour, slider, number, file ---------------------------------------- */

.ch-pdf-colorrow {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* The native well is exact and unusable for finding a corporate grey; the
   swatches are the eight colours documents actually get stamped in. Both write
   the same value, and the row is what makes this work with one thumb. */
.ch-pdf-colorwell,
.ch-pdf-colorwell:focus,
.ch-pdf-colorwell:active {
    flex: 0 0 auto;
    width: 42px;
    height: 34px;
    padding: 2px;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    background-color: var(--ch-pdf-tile);
    cursor: pointer;
}

.ch-pdf-swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 0.28rem;
}

.ch-pdf-swatch,
.ch-pdf-swatch:focus,
.ch-pdf-swatch:active {
    width: 22px;
    height: 22px;
    padding: 0;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 6px;
    cursor: pointer;
    transition: transform 0.12s var(--ch-pdf-ease), box-shadow 0.12s;
}

.ch-pdf-swatch:hover { transform: scale(1.12); }

.ch-pdf-swatch.is-on {
    box-shadow: 0 0 0 2px var(--ch-pdf-surface), 0 0 0 4px var(--ch-cta);
}

.ch-pdf-opt.is-range .ch-pdf-opt-label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.6rem;
}

.ch-pdf-rangeval {
    font-family: var(--ch-pdf-mono);
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--ch-pdf-accent);
}

.ch-pdf-slider {
    width: 100%;
    height: 22px;
    margin: 0;
    accent-color: #10B981;
    background: transparent;
    cursor: pointer;
}

.ch-pdf-input.is-num { max-width: 9rem; }

.ch-pdf-filebtn {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.55rem 0.7rem;
    border: 1px dashed var(--ch-pdf-line-hard);
    border-radius: 9px;
    background-color: var(--ch-pdf-tile);
    color: var(--ch-text-main);
    font-size: 0.82rem;
    cursor: pointer;
    transition: border-color 0.15s, background-color 0.15s, color 0.15s;
}

.ch-pdf-filebtn:hover {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

.ch-pdf-filebtn.is-set {
    border-style: solid;
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
}

.ch-pdf-filebtn span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The input is inside the label, so the label IS the control. Hiding it with
   display:none removes it from the tab order, which would make choosing a logo
   impossible from a keyboard; this keeps it focusable and invisible. */
.ch-pdf-filebtn input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.ch-pdf-filebtn:focus-within {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

.ch-pdf-notice.is-info {
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    color: var(--ch-pdf-accent);
}

/* --- Tile overlays -------------------------------------------------------- */

/* The shot box centres a letterboxed image, so an overlay that filled the box
   would sit against the tile rather than against the page. Giving the overlay
   the page's own aspect ratio and the same max-width/max-height rules makes it
   land exactly on the image, at any tile size, for any paper.

   container-type is what lets a stamp be sized as a percentage of the PAGE
   rather than of the tile, which is the only way a 10 pt page number stays
   proportionally 10 pt on a 118 px thumbnail and on a 700 px one. */
.ch-pdf-pg-shot { position: relative; }

.ch-pdf-pg-over {
    position: absolute;
    inset: 0;
    margin: auto;
    max-width: 100%;
    max-height: 100%;
    container-type: size;
    pointer-events: none;
}

.ch-pdf-pg-stamp,
.ch-pdf-pg-mark {
    position: absolute;
    display: inline-block;
    /* The pixel value is the fallback for a browser with no container query
       units. It is small and legible rather than correct, which beats a stamp
       that inherits the tile's 14px and covers the page. */
    font-size: 6px;
    line-height: 1;
    white-space: nowrap;
}

.ch-pdf-pg-stamp {
    transform: translate(-50%, -50%);
    padding: 0.18em 0.35em;
    border-radius: 2px;
}

.ch-pdf-pg-stamp.is-pill { background-color: rgba(15, 23, 42, 0.12); border-radius: 999px; }
.ch-pdf-pg-stamp.is-box  { box-shadow: inset 0 0 0 0.5px currentColor; }
.ch-pdf-pg-stamp.is-dot  { background-color: rgba(15, 23, 42, 0.12); border-radius: 50%; padding: 0.3em; }
.ch-pdf-pg-stamp.is-rule { border-top: 0.5px solid currentColor; padding-top: 0.25em; border-radius: 0; }

.ch-pdf-pg-mark {
    text-align: center;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.ch-pdf-pg-mark.is-img img {
    display: block;
    width: 100%;
    height: auto;
}

.ch-pdf-pg-crop {
    position: absolute;
    border: 1px solid #10B981;
    box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.42);
}

.ch-pdf-pg-edits,
.ch-pdf-pg-skip {
    position: absolute;
    left: 50%;
    bottom: 4%;
    transform: translateX(-50%);
    padding: 0.12rem 0.35rem;
    border-radius: 999px;
    font-family: var(--ch-pdf-mono);
    font-size: 0.55rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.ch-pdf-pg-edits { background-color: #059669; color: #FFFFFF; }
.ch-pdf-pg-skip  { background-color: rgba(15, 23, 42, 0.55); color: #E2E8F0; }

/* Four modes open a full page rather than only selecting one, so the tile face
   is a control in all four and says so. */
[data-pg-mode="rotate"] .ch-pdf-pg-face,
[data-pg-mode="stamp"] .ch-pdf-pg-face,
[data-pg-mode="crop"] .ch-pdf-pg-face,
[data-pg-mode="edit"] .ch-pdf-pg-face { cursor: zoom-in; }

.ch-pdf-pgop.is-open {
    background-color: var(--ch-pdf-accent-bg);
    border-color: var(--ch-pdf-accent-line);
    color: var(--ch-pdf-accent);
}

html[data-theme="dark"] .ch-pdf-pg-stamp.is-pill,
html[data-theme="dark"] .ch-pdf-pg-stamp.is-dot { background-color: rgba(15, 23, 42, 0.2); }
html[data-theme="dark"] .ch-pdf-swatch { border-color: #475569; }
html[data-theme="dark"] .ch-pdf-swatch.is-on { box-shadow: 0 0 0 2px #1E293B, 0 0 0 4px #34D399; }
html[data-theme="dark"] .ch-pdf-colorwell { border-color: #334155; background-color: #263349; }
html[data-theme="dark"] .ch-pdf-filebtn { border-color: #475569; background-color: #263349; color: #E2E8F0; }
html[data-theme="dark"] .ch-pdf-rangeval { color: #34D399; }

/* ==========================================================================
   12. THE PAGE WORKSPACE

   Appended to <body> by carthacker-editor.js rather than printed by PHP, for
   the same reason the conversion gate is: it is a fixed, full viewport surface
   and it has to sit above a sticky header and outside every transformed
   ancestor on the page.

   That has one consequence worth stating, because it is the trap this file
   already carries a warning about elsewhere: it is NOT inside .ch-page-pdf, so
   none of the --ch-pdf-* tokens reach it. It declares its own, and the dark
   overrides at the bottom of this section swap them.

   Layout is a three row grid: a header that never scrolls, a body split into a
   tool rail and a scrolling stage, and a footer of page navigation. The stage
   is the only thing that grows, so the close button and the page arrows are
   reachable at any viewport height, including a phone in landscape.
   ========================================================================== */

.ch-ed {
    --ed-surface: #FFFFFF;
    --ed-well: #E2E8F0;
    --ed-tile: #F1F5F9;
    --ed-line: #CBD5E1;
    --ed-text: #0F172A;
    --ed-muted: #64748B;
    --ed-accent: #059669;
    --ed-accent-bg: rgba(16, 185, 129, 0.1);
    --ed-accent-line: rgba(16, 185, 129, 0.45);
    --ed-mono: "JetBrains Mono", "Roboto Mono", ui-monospace, monospace;

    position: fixed;
    inset: 0;
    z-index: 9998;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(0.35rem, 1.6vw, 1.25rem);
    color: var(--ed-text);
    font-family: inherit;
}

.ch-ed [hidden] { display: none !important; }

.ch-ed :is(a, button, summary, input, select, textarea):focus-visible {
    outline: 2px solid #10B981;
    outline-offset: 2px;
    border-radius: 6px;
}

.ch-ed-veil {
    position: absolute;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.8);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.ch-ed-card {
    position: relative;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    width: min(1320px, 100%);
    height: min(94vh, 100%);
    max-height: 100dvh;
    overflow: hidden;
    border: 1px solid var(--ed-line);
    border-radius: 16px;
    background-color: var(--ed-surface);
    box-shadow: 0 40px 90px -30px rgba(15, 23, 42, 0.75);
    animation: chEdIn 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes chEdIn {
    from { opacity: 0; transform: scale(0.99); }
    to   { opacity: 1; transform: none; }
}

.ch-ed-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.9rem;
    padding: 0.7rem 0.75rem 0.7rem 1.1rem;
    border-bottom: 1px solid var(--ed-line);
}

.ch-ed-id { min-width: 0; }

.ch-ed-kicker {
    display: block;
    font-family: var(--ed-mono);
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--ed-accent);
}

.ch-ed-title {
    margin: 0.1rem 0 0;
    overflow: hidden;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ch-ed-headops {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 0.3rem;
}

.ch-ed-btn,
.ch-ed-btn:focus,
.ch-ed-btn:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-width: 40px;
    height: 40px;
    padding: 0 0.55rem;
    border: 1px solid var(--ed-line);
    border-radius: 9px;
    background-color: var(--ed-tile);
    color: var(--ed-text);
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.14s, border-color 0.14s, color 0.14s;
}

.ch-ed-btn.is-wide { min-width: 62px; font-family: var(--ed-mono); font-size: 0.72rem; }

.ch-ed-btn:hover:not(:disabled) {
    background-color: var(--ed-accent-bg);
    border-color: var(--ed-accent-line);
    color: var(--ed-accent);
}

.ch-ed-btn:disabled { opacity: 0.38; cursor: not-allowed; }

/* Oversized and permanent, like every other close in this suite. Hunting for
   the exit of a full screen overlay on a phone is the worst thing a modal can
   do, and this one can be opened by a mis-tap on a tile. */
.ch-ed-x,
.ch-ed-x:focus,
.ch-ed-x:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-left: 0.2rem;
    padding: 0;
    border: 1px solid var(--ed-line);
    border-radius: 10px;
    background-color: var(--ed-tile);
    color: var(--ed-text);
    cursor: pointer;
    transition: background-color 0.14s, border-color 0.14s, color 0.14s;
}

.ch-ed-x:hover { background-color: #DC2626; border-color: #DC2626; color: #FFFFFF; }

.ch-ed-body {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr);
    min-height: 0;
}

.ch-ed.is-view .ch-ed-body { grid-template-columns: minmax(0, 1fr); }

.ch-ed-rail {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    min-height: 0;
    padding: 0.9rem;
    overflow-y: auto;
    border-right: 1px solid var(--ed-line);
    background-color: var(--ed-surface);
}

.ch-ed-group {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.ch-ed-grouphead {
    font-family: var(--ed-mono);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--ed-muted);
}

.ch-ed-note {
    font-size: 0.72rem;
    line-height: 1.5;
    color: var(--ed-muted);
}

.ch-ed-tools {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.3rem;
}

.ch-ed-tool,
.ch-ed-tool:focus,
.ch-ed-tool:active {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 38px;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--ed-line);
    border-radius: 9px;
    background-color: var(--ed-tile);
    color: var(--ed-text);
    font-size: 0.74rem;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.14s, border-color 0.14s, color 0.14s;
}

.ch-ed-tool.is-wide { grid-column: 1 / -1; }

.ch-ed-tool:hover:not(:disabled) {
    background-color: var(--ed-accent-bg);
    border-color: var(--ed-accent-line);
    color: var(--ed-accent);
}

.ch-ed-tool:disabled { opacity: 0.38; cursor: not-allowed; }

.ch-ed-tool.is-on {
    background-color: #10B981;
    border-color: #10B981;
    color: #FFFFFF;
}

.ch-ed-tool.is-on:hover { background-color: #059669; border-color: #059669; color: #FFFFFF; }
.ch-ed-tool.is-drop:hover { background-color: rgba(239, 68, 68, 0.1); border-color: rgba(239, 68, 68, 0.4); color: #B91C1C; }

.ch-ed-grid2, .ch-ed-grid4 { display: grid; gap: 0.35rem; }
.ch-ed-grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ch-ed-grid4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.ch-ed-num, .ch-ed-field, .ch-ed-check {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.72rem;
    color: var(--ed-muted);
}

.ch-ed-check { flex-direction: row; align-items: center; gap: 0.4rem; }

.ch-ed-num input,
.ch-ed-field input,
.ch-ed-field select,
.ch-ed-field textarea {
    width: 100%;
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--ed-line);
    border-radius: 8px;
    background-color: var(--ed-surface);
    color: var(--ed-text);
    font-family: inherit;
    font-size: 0.8rem;
}

.ch-ed-field textarea { resize: vertical; min-height: 3.5rem; }
.ch-ed-num input[type="color"] { height: 32px; padding: 2px; cursor: pointer; }

/* --- The nine position cells ---------------------------------------------- */

.ch-ed-posgrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 3px;
    width: 100%;
    max-width: 150px;
    aspect-ratio: 3 / 4;
    padding: 3px;
    border: 1px solid var(--ed-line);
    border-radius: 8px;
    background-color: var(--ed-well);
}

.ch-ed-pos,
.ch-ed-pos:focus,
.ch-ed-pos:active {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    border-radius: 5px;
    background-color: var(--ed-surface);
    cursor: pointer;
    transition: background-color 0.14s;
}

.ch-ed-pos span {
    width: 40%;
    height: 3px;
    border-radius: 2px;
    background-color: var(--ed-line);
}

.ch-ed-pos:hover { background-color: var(--ed-accent-bg); }
.ch-ed-pos:hover span { background-color: var(--ed-accent); }
.ch-ed-pos.is-on { background-color: #10B981; }
.ch-ed-pos.is-on span { background-color: #FFFFFF; }

/* --- The stage ------------------------------------------------------------ */

.ch-ed-stage {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    min-height: 0;
    padding: clamp(0.6rem, 2vw, 1.4rem);
    overflow: auto;
    background-color: var(--ed-well);
    /* A background that reads as "not paper", so the edge of a white page is
       unmistakable even when the crop frame is off. */
    background-image:
        linear-gradient(45deg, rgba(15, 23, 42, 0.035) 25%, transparent 25%, transparent 75%, rgba(15, 23, 42, 0.035) 75%),
        linear-gradient(45deg, rgba(15, 23, 42, 0.035) 25%, transparent 25%, transparent 75%, rgba(15, 23, 42, 0.035) 75%);
    background-size: 22px 22px;
    background-position: 0 0, 11px 11px;
}

.ch-ed-sheet {
    position: relative;
    flex: 0 0 auto;
    margin: auto;
    background-color: #FFFFFF;
    box-shadow: 0 12px 40px -12px rgba(15, 23, 42, 0.55);
    /* Without this the browser claims every drag as a scroll gesture and the
       crop frame cannot be moved with a finger at all. */
    touch-action: none;
}

.ch-ed-canvas { display: block; }

.ch-ed-layer { position: absolute; inset: 0; }

.ch-ed-wait {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    padding: 0.5rem 0.85rem;
    border-radius: 999px;
    background-color: rgba(15, 23, 42, 0.82);
    color: #F1F5F9;
    font-family: var(--ed-mono);
    font-size: 0.7rem;
    white-space: nowrap;
}

/* Every mode names its own pointer, because the sheet does something different
   in each and a crosshair over a surface that only pans is a small lie. */
.ch-ed.is-crop .ch-ed-sheet { cursor: crosshair; }
.ch-ed.is-place .ch-ed-sheet { cursor: grab; }
.ch-ed.is-edit .ch-ed-sheet[data-tool="draw"] { cursor: crosshair; }
.ch-ed.is-edit .ch-ed-sheet[data-tool="text"] { cursor: text; }

/* --- Crop ----------------------------------------------------------------- */

.ch-ed-shade {
    position: absolute;
    background-color: rgba(15, 23, 42, 0.5);
}

.ch-ed-crop {
    position: absolute;
    box-shadow: inset 0 0 0 1px #10B981, 0 0 0 1px rgba(255, 255, 255, 0.55);
    cursor: move;
}

/* The rule of thirds, drawn the way every camera draws it. Not decoration: it
   is how somebody trims a scan to the block of text without putting the block
   off centre. */
.ch-ed-crop::before,
.ch-ed-crop::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.ch-ed-crop::before {
    background-image:
        linear-gradient(to right, transparent 33.33%, rgba(255, 255, 255, 0.45) 33.33%, rgba(255, 255, 255, 0.45) calc(33.33% + 1px), transparent calc(33.33% + 1px)),
        linear-gradient(to right, transparent 66.66%, rgba(255, 255, 255, 0.45) 66.66%, rgba(255, 255, 255, 0.45) calc(66.66% + 1px), transparent calc(66.66% + 1px));
}

.ch-ed-crop::after {
    background-image:
        linear-gradient(to bottom, transparent 33.33%, rgba(255, 255, 255, 0.45) 33.33%, rgba(255, 255, 255, 0.45) calc(33.33% + 1px), transparent calc(33.33% + 1px)),
        linear-gradient(to bottom, transparent 66.66%, rgba(255, 255, 255, 0.45) 66.66%, rgba(255, 255, 255, 0.45) calc(66.66% + 1px), transparent calc(66.66% + 1px));
}

.ch-ed-cropsize {
    position: absolute;
    left: 50%;
    bottom: -1.7rem;
    transform: translateX(-50%);
    padding: 0.15rem 0.45rem;
    border-radius: 999px;
    background-color: #10B981;
    color: #FFFFFF;
    font-family: var(--ed-mono);
    font-size: 0.66rem;
    white-space: nowrap;
}

/* 24px rather than 10px. A crop handle is dragged with a fingertip on half the
   traffic these tools get, and a handle smaller than the finger is a handle
   that only works with a mouse. */
.ch-ed-hand {
    position: absolute;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background-color: #10B981;
    box-shadow: 0 0 0 2px #FFFFFF, 0 1px 4px rgba(15, 23, 42, 0.4);
}

.ch-ed-hand.is-nw { left: -12px; top: -12px; cursor: nwse-resize; }
.ch-ed-hand.is-n  { left: 50%; top: -12px; margin-left: -12px; cursor: ns-resize; }
.ch-ed-hand.is-ne { right: -12px; top: -12px; cursor: nesw-resize; }
.ch-ed-hand.is-e  { right: -12px; top: 50%; margin-top: -12px; cursor: ew-resize; }
.ch-ed-hand.is-se { right: -12px; bottom: -12px; cursor: nwse-resize; }
.ch-ed-hand.is-s  { left: 50%; bottom: -12px; margin-left: -12px; cursor: ns-resize; }
.ch-ed-hand.is-sw { left: -12px; bottom: -12px; cursor: nesw-resize; }
.ch-ed-hand.is-w  { left: -12px; top: 50%; margin-top: -12px; cursor: ew-resize; }

/* --- Place ---------------------------------------------------------------- */

.ch-ed-guide {
    position: absolute;
    background-color: rgba(16, 185, 129, 0.28);
}

.ch-ed-guide.is-v { left: 50%; top: 0; bottom: 0; width: 1px; }
.ch-ed-guide.is-h { top: 50%; left: 0; right: 0; height: 1px; }

.ch-ed-stamp {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    transform-origin: 50% 50%;
}

.ch-ed-stamp img { width: 100%; height: 100%; object-fit: contain; }

.ch-ed-stamp.is-movable {
    outline: 1px dashed rgba(16, 185, 129, 0.85);
    outline-offset: 4px;
    cursor: grab;
}

.ch-ed-stamptext {
    display: block;
    line-height: 1;
    white-space: nowrap;
    font-weight: inherit;
}

/* --- Edit objects --------------------------------------------------------- */

.ch-ed-obj {
    position: absolute;
    cursor: move;
}

.ch-ed-obj.is-on { outline: 1px solid #10B981; outline-offset: 2px; }

/* A stroke is the whole page in SVG terms, so a pointer target over it would
   swallow every click on the page behind it. The handles are on the box tools;
   a drawing is selected by clicking the ink itself. */
.ch-ed-obj.is-draw { pointer-events: none; }
.ch-ed-obj.is-draw path { pointer-events: stroke; cursor: move; }
.ch-ed-obj.is-draw.is-on { outline: none; }
.ch-ed-obj.is-draw.is-on path { filter: drop-shadow(0 0 2px #10B981); }

.ch-ed-objtext {
    display: block;
    white-space: pre-wrap;
    word-break: break-word;
}

.ch-ed-obj.is-image img { width: 100%; height: 100%; object-fit: fill; }

.ch-ed-obj.is-link {
    border: 1px dashed #2563EB;
    background-color: rgba(37, 99, 235, 0.09);
}

/* Anchor text is centred in its own click target, matching what the writer
   does. A label drawn outside the rectangle is the one link nobody can follow. */
.ch-ed-obj.is-link .ch-ed-objtext.is-anchor {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    text-align: center;
    white-space: nowrap;
}

.ch-ed-linktag {
    position: absolute;
    left: 0;
    top: 100%;
    max-width: 220px;
    overflow: hidden;
    padding: 0.1rem 0.3rem;
    border-radius: 4px;
    background-color: #2563EB;
    color: #FFFFFF;
    font-family: var(--ed-mono);
    font-size: 0.6rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ch-ed-obj .ch-ed-hand { width: 18px; height: 18px; }
.ch-ed-obj .ch-ed-hand.is-nw { left: -9px; top: -9px; }
.ch-ed-obj .ch-ed-hand.is-ne { right: -9px; top: -9px; }
.ch-ed-obj .ch-ed-hand.is-se { right: -9px; bottom: -9px; }
.ch-ed-obj .ch-ed-hand.is-sw { left: -9px; bottom: -9px; }

/* --- Footer --------------------------------------------------------------- */

.ch-ed-foot {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.9rem;
    padding: 0.65rem 1rem;
    border-top: 1px solid var(--ed-line);
}

.ch-ed-nav { display: flex; align-items: center; gap: 0.4rem; }

.ch-ed-pageno {
    min-width: 8.5rem;
    font-family: var(--ed-mono);
    font-size: 0.72rem;
    text-align: center;
    color: var(--ed-muted);
}

.ch-ed-pageno em { color: var(--ed-accent); font-style: normal; }

.ch-ed-hint {
    margin: 0;
    font-size: 0.74rem;
    line-height: 1.45;
    color: var(--ed-muted);
}

.ch-ed-done,
.ch-ed-done:focus,
.ch-ed-done:active {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    height: 42px;
    padding: 0 1.1rem;
    border: 0;
    border-radius: 10px;
    background-color: #10B981;
    color: #FFFFFF;
    font-size: 0.86rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.14s;
}

.ch-ed-done:hover { background-color: #059669; color: #FFFFFF; }

/* --- Narrow viewports ------------------------------------------------------ */

/* The rail goes horizontal rather than away. Every one of its controls is the
   point of the tool it belongs to, and a phone that can crop but cannot reach
   Detect the margins is a phone getting a worse tool than a laptop. */
@media (max-width: 860px) {
    .ch-ed { padding: 0; }
    .ch-ed-card { width: 100%; height: 100%; border-radius: 0; border: 0; }
    .ch-ed-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }

    .ch-ed-rail {
        flex-direction: row;
        gap: 0.75rem;
        max-height: 40vh;
        padding: 0.6rem 0.75rem;
        overflow-x: auto;
        border-right: 0;
        border-bottom: 1px solid var(--ed-line);
    }

    .ch-ed-group { flex: 0 0 auto; min-width: 190px; }
    .ch-ed-foot { grid-template-columns: auto minmax(0, 1fr); row-gap: 0.5rem; }
    .ch-ed-hint { display: none; }
    .ch-ed-done { grid-column: 2; justify-self: end; }
    .ch-ed-title { max-width: 42vw; }
}

@media (prefers-reduced-motion: reduce) {
    .ch-ed-card { animation: none; }
    .ch-ed-btn, .ch-ed-tool, .ch-ed-pos, .ch-ed-x, .ch-ed-done { transition: none; }
}

/* --- Dark ----------------------------------------------------------------- */

html[data-theme="dark"] .ch-ed {
    --ed-surface: #1E293B;
    --ed-well: #0F172A;
    --ed-tile: #263349;
    --ed-line: #334155;
    --ed-text: #E2E8F0;
    --ed-muted: #94A3B8;
    --ed-accent: #34D399;
    --ed-accent-bg: rgba(16, 185, 129, 0.16);
    --ed-accent-line: rgba(52, 211, 153, 0.42);
}

/* The sheet stays white in both themes and the stage stays a checker. A page is
   a picture of paper: inverted, a perfectly good scan reads as damaged, and a
   crop frame drawn against a dark page stops showing what will be lost. */
html[data-theme="dark"] .ch-ed-stage {
    background-image:
        linear-gradient(45deg, rgba(148, 163, 184, 0.06) 25%, transparent 25%, transparent 75%, rgba(148, 163, 184, 0.06) 75%),
        linear-gradient(45deg, rgba(148, 163, 184, 0.06) 25%, transparent 25%, transparent 75%, rgba(148, 163, 184, 0.06) 75%);
}

html[data-theme="dark"] .ch-ed-tool.is-drop:hover { color: #FCA5A5; }
html[data-theme="dark"] .ch-ed-hand { box-shadow: 0 0 0 2px #0F172A, 0 1px 4px rgba(0, 0, 0, 0.6); }

/* ===========================================================================
   THE DIALOG
   ---------------------------------------------------------------------------
   The form modal behind Protect PDF and Unlock PDF. It reuses the handoff
   modal's shell - veil, card, close button, kicker, title - and adds the
   fields, because two dialogs that look almost the same is worse than one that
   looks the same.

   The card switches from centred to left aligned here. Centred prose is right
   for a QR code and three sentences; it is wrong for a stack of labelled
   inputs, where a ragged left edge makes every label start in a different
   place and the eye has to hunt for each one.
   =========================================================================== */

.ch-pdf-modal.is-form .ch-pdf-modal-card {
    align-items: stretch;
    width: min(440px, 100%);
    text-align: left;
}

.ch-pdf-modal.is-form .ch-pdf-modal-title { text-align: left; }
.ch-pdf-modal.is-form .ch-pdf-modal-lead { text-align: left; margin-bottom: 0.2rem; }

.ch-pdf-dlg-fields {
    display: flex;
    flex-direction: column;
    gap: 0.95rem;
    margin-top: 1.15rem;
}

.ch-pdf-dlg-row {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.ch-pdf-dlg-label {
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #475569;
}

.ch-pdf-dlg-inputwrap {
    position: relative;
    display: block;
}

.ch-pdf-dlg-input {
    width: 100%;
    padding: 0.7rem 0.85rem;
    padding-right: 2.7rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: #0F172A;
    font-family: inherit;
    font-size: 0.95rem;
    line-height: 1.4;
    transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

/* A password field is monospaced so a revealed password can be read back one
   character at a time. Proportional dots, and a proportional l against 1
   against I, are how a password gets retyped three times. */
.ch-pdf-dlg-input[type="password"],
.ch-pdf-dlg-input[type="text"] {
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    letter-spacing: 0.02em;
}

.ch-pdf-dlg-input:focus {
    outline: none;
    border-color: var(--ch-pdf-accent);
    box-shadow: 0 0 0 3px var(--ch-pdf-accent-bg-hi);
}

.ch-pdf-dlg-eye,
.ch-pdf-dlg-eye:focus,
.ch-pdf-dlg-eye:active {
    position: absolute;
    top: 50%;
    right: 0.45rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: none;
    color: #64748B;
    cursor: pointer;
}

.ch-pdf-dlg-eye:hover { background-color: rgba(15, 23, 42, 0.06); color: #0F172A; }
.ch-pdf-dlg-eye.is-on { color: var(--ch-pdf-accent); background-color: var(--ch-pdf-accent-bg); }

/* --- Strength meter --------------------------------------------------------
   Four steps, and the colour is the only thing that changes between them. The
   width tracks the estimated entropy continuously, so typing a fifth word
   visibly moves it even while the label stays on "Very strong". */

.ch-pdf-dlg-meter {
    display: block;
    margin-top: 0.15rem;
}

.ch-pdf-dlg-meter i {
    display: block;
    height: 4px;
    width: 0;
    border-radius: 999px;
    background-color: #CBD5E1;
    transition: width 0.2s ease, background-color 0.2s ease;
}

.ch-pdf-dlg-meter b {
    display: block;
    margin-top: 0.3rem;
    font-size: 0.76rem;
    font-weight: 500;
    color: #64748B;
}

.ch-pdf-dlg-meter[data-level="1"] i { background-color: #DC2626; }
.ch-pdf-dlg-meter[data-level="2"] i { background-color: #EA580C; }
.ch-pdf-dlg-meter[data-level="3"] i { background-color: #059669; }
.ch-pdf-dlg-meter[data-level="4"] i { background-color: #059669; }

.ch-pdf-dlg-meter[data-level="1"] b { color: #B91C1C; }
.ch-pdf-dlg-meter[data-level="2"] b { color: #C2410C; }
.ch-pdf-dlg-meter[data-level="3"] b,
.ch-pdf-dlg-meter[data-level="4"] b { color: var(--ch-pdf-accent); }

/* --- Other field types ---------------------------------------------------- */

.ch-pdf-dlg-hint {
    display: block;
    font-size: 0.8rem;
    line-height: 1.5;
    color: #64748B;
}

.ch-pdf-dlg-hint strong { color: #334155; }

.ch-pdf-dlg-note {
    margin: 0;
    padding: 0.7rem 0.85rem;
    border-radius: 10px;
    border: 1px solid var(--ch-border);
    background-color: var(--ch-pdf-tile);
    font-size: 0.84rem;
    line-height: 1.6;
    color: #475569;
}

.ch-pdf-dlg-note strong { color: #0F172A; }

.ch-pdf-dlg-note.is-warn {
    border-color: rgba(217, 119, 6, 0.35);
    background-color: rgba(245, 158, 11, 0.08);
    color: #78350F;
}

.ch-pdf-dlg-note.is-warn strong { color: #78350F; }

.ch-pdf-dlg-switch {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    cursor: pointer;
}

.ch-pdf-dlg-switch input { position: absolute; opacity: 0; pointer-events: none; }

.ch-pdf-dlg-file {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 0.85rem;
    border: 1px dashed var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: #475569;
    font-size: 0.88rem;
    cursor: pointer;
    transition: border-color 0.16s ease, color 0.16s ease;
}

.ch-pdf-dlg-file:hover { border-color: var(--ch-pdf-accent-line); color: #0F172A; }
.ch-pdf-dlg-file input { position: absolute; opacity: 0; width: 0; height: 0; }
.ch-pdf-dlg-file svg { flex: 0 0 auto; color: var(--ch-pdf-accent); }
.ch-pdf-dlg-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ch-pdf-dlg-file.is-set {
    border-style: solid;
    border-color: var(--ch-pdf-accent-line);
    background-color: var(--ch-pdf-accent-bg);
    color: #0F172A;
}

.ch-pdf-dlg-error {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 0.9rem 0 0;
    padding: 0.65rem 0.8rem;
    border: 1px solid rgba(220, 38, 38, 0.35);
    border-radius: 10px;
    background-color: rgba(220, 38, 38, 0.07);
    color: #991B1B;
    font-size: 0.86rem;
    line-height: 1.55;
}

.ch-pdf-dlg-error[hidden] { display: none; }
.ch-pdf-dlg-error svg { flex: 0 0 auto; margin-top: 2px; }

.ch-pdf-dlg-actions {
    display: flex;
    gap: 0.6rem;
    justify-content: flex-end;
    margin-top: 1.4rem;
}

.ch-pdf-dlg-btn,
.ch-pdf-dlg-btn:focus,
.ch-pdf-dlg-btn:active {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.66rem 1.1rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
    color: #334155;
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease;
}

.ch-pdf-dlg-btn:hover { border-color: var(--ch-pdf-line-hard); color: #0F172A; }

.ch-pdf-dlg-btn.is-go {
    border-color: transparent;
    background-color: var(--ch-cta, #10B981);
    color: #FFFFFF;
}

.ch-pdf-dlg-btn.is-go:hover { background-color: #059669; }
.ch-pdf-dlg-btn.is-go[disabled] { opacity: 0.55; cursor: not-allowed; }

/* The busy state names itself rather than spinning. A visitor who has just
   typed a password wants to know the CHECK is running, not that something
   unspecified is happening. */
.ch-pdf-dlg-btn.is-busy span { visibility: hidden; }

.ch-pdf-dlg-btn.is-busy::after {
    content: "Checking";
    position: absolute;
    font-weight: 600;
}

/* ===========================================================================
   THE SIGNATURE STUDIO
   ---------------------------------------------------------------------------
   Two surfaces: the panel above the page board where a signature is made and
   kept, and the full screen capture dialog where it is drawn, typed or
   photographed.
   =========================================================================== */

.ch-sig-studio {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px;
    gap: 1.1rem;
    padding: 1.05rem 1.15rem;
    border: 1px solid var(--ch-border);
    border-radius: 14px;
    background-color: var(--ch-pdf-tile);
}

@media (max-width: 760px) {
    .ch-sig-studio { grid-template-columns: minmax(0, 1fr); }
}

.ch-sig-studio-head {
    display: block;
    margin-bottom: 0.65rem;
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #64748B;
}

/* --- The chip row ----------------------------------------------------------
   Signatures are wide and short, so the chips are too. A square tile would
   letterbox every one of them and make a signature look like a stamp. */

.ch-sig-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.55rem;
    min-height: 62px;
    margin-bottom: 0.85rem;
}

.ch-sig-chips-empty {
    align-self: center;
    max-width: 46ch;
    font-size: 0.86rem;
    line-height: 1.55;
    color: #64748B;
}

.ch-sig-chip {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 132px;
    height: 62px;
    padding: 0.3rem 0.5rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
    cursor: pointer;
    transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.ch-sig-chip:hover { border-color: var(--ch-pdf-line-hard); }

.ch-sig-chip.is-on {
    border-color: var(--ch-pdf-accent);
    box-shadow: 0 0 0 3px var(--ch-pdf-accent-bg-hi);
}

.ch-sig-chip img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.ch-sig-chip-x,
.ch-sig-chip-x:focus,
.ch-sig-chip-x:active {
    position: absolute;
    top: -7px;
    right: -7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 999px;
    background-color: var(--ch-pdf-surface);
    color: #64748B;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.16s ease, color 0.16s ease;
}

.ch-sig-chip:hover .ch-sig-chip-x,
.ch-sig-chip:focus-within .ch-sig-chip-x { opacity: 1; }
.ch-sig-chip-x:hover { color: #DC2626; border-color: rgba(220, 38, 38, 0.5); }

/* Touch has no hover, so the delete control is permanent there. An invisible
   control on a phone is not a control. */
@media (hover: none) {
    .ch-sig-chip-x { opacity: 1; }
}

.ch-sig-studio-bar { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.ch-sig-studio-side {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    justify-content: flex-start;
}

.ch-sig-studio-msg {
    margin: 0.7rem 0 0;
    font-size: 0.85rem;
    color: #64748B;
}

.ch-sig-studio-msg[hidden] { display: none; }
.ch-sig-studio-msg.is-ok { color: var(--ch-pdf-accent); font-weight: 500; }
.ch-sig-studio-msg.is-stop { color: #B91C1C; }

.ch-sig-studio-fine {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    margin: 0;
    font-size: 0.8rem;
    line-height: 1.5;
    color: #64748B;
}

.ch-sig-studio-fine svg { flex: 0 0 auto; margin-top: 3px; color: var(--ch-pdf-accent); }
.ch-sig-studio-fine strong { color: #0F172A; }
.ch-sig-studio-fine[hidden] { display: none; }

/* ===========================================================================
   THE CAPTURE DIALOG
   =========================================================================== */

.ch-sig-modal {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.ch-sig-veil {
    position: absolute;
    inset: 0;
    background-color: rgba(15, 23, 42, 0.72);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.ch-sig-card {
    position: relative;
    display: flex;
    flex-direction: column;
    width: min(620px, 100%);
    max-height: min(92vh, 780px);
    overflow-y: auto;
    padding: clamp(1.2rem, 3.4vw, 1.7rem);
    border: 1px solid var(--ch-border);
    border-radius: 18px;
    background-color: var(--ch-pdf-surface);
    color: #0F172A;
    box-shadow: 0 40px 80px -30px rgba(15, 23, 42, 0.6);
}

.ch-sig-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.ch-sig-kicker {
    display: block;
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-pdf-accent);
}

.ch-sig-title {
    margin: 0.3rem 0 0;
    font-size: clamp(1.15rem, 3vw, 1.4rem);
    font-weight: 700;
    line-height: 1.25;
}

.ch-sig-x,
.ch-sig-x:focus,
.ch-sig-x:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    flex: 0 0 auto;
    padding: 0;
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: #64748B;
    cursor: pointer;
}

.ch-sig-x:hover { color: #0F172A; border-color: var(--ch-pdf-line-hard); }

.ch-sig-tabs {
    display: flex;
    gap: 0.35rem;
    margin: 1.1rem 0 1rem;
    padding: 0.28rem;
    border-radius: 12px;
    background-color: var(--ch-pdf-well);
}

.ch-sig-tab,
.ch-sig-tab:focus,
.ch-sig-tab:active {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.6rem 0.5rem;
    border: 0;
    border-radius: 9px;
    background: none;
    color: #475569;
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.16s ease, color 0.16s ease;
}

.ch-sig-tab:hover { color: #0F172A; }

.ch-sig-tab.is-on {
    background-color: var(--ch-pdf-surface);
    color: #0F172A;
    box-shadow: 0 1px 3px rgba(15, 23, 42, 0.12);
}

.ch-sig-body { display: flex; flex-direction: column; gap: 0.9rem; }

/* --- The pad ---------------------------------------------------------------
   touch-action: none is the single most important declaration in this file.
   Without it the first downward stroke of a signature scrolls the page instead
   of drawing, on every touchscreen, and the tool appears to be broken. */

.ch-sig-padwrap {
    position: relative;
    border: 1px dashed var(--ch-pdf-line-soft);
    border-radius: 14px;
    background-color: var(--ch-pdf-tile);
    overflow: hidden;
}

.ch-sig-pad {
    display: block;
    width: 100%;
    height: clamp(170px, 32vw, 230px);
    touch-action: none;
    cursor: crosshair;
}

.ch-sig-padhint {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    pointer-events: none;
    text-align: center;
    padding: 1rem;
}

.ch-sig-padhint[hidden] { display: none; }
.ch-sig-padhint b { font-size: 1.05rem; color: #334155; }
.ch-sig-padhint span { max-width: 34ch; font-size: 0.84rem; color: #94A3B8; }

/* The ruled line people sign on. It sits where the baseline of a signature
   naturally falls rather than in the middle of the box, because a signature
   written across the centre of a tall pad crops badly at the top. */
.ch-sig-baseline {
    position: absolute;
    left: 8%;
    right: 8%;
    bottom: 27%;
    height: 1px;
    background-color: var(--ch-pdf-line-soft);
    opacity: 0.7;
    pointer-events: none;
}

.ch-sig-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.7rem;
}

.ch-sig-row.is-stack { flex-direction: column; align-items: stretch; }

.ch-sig-tools { display: flex; gap: 0.4rem; }
.ch-sig-inks { display: flex; gap: 0.4rem; }

.ch-sig-ink,
.ch-sig-ink:focus,
.ch-sig-ink:active {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 2px solid transparent;
    border-radius: 999px;
    background-color: var(--ink);
    box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.16);
    cursor: pointer;
    transition: transform 0.14s ease, border-color 0.14s ease;
}

.ch-sig-ink:hover { transform: scale(1.08); }
.ch-sig-ink.is-on { border-color: #0F172A; transform: scale(1.08); }

.ch-sig-btn,
.ch-sig-btn:focus,
.ch-sig-btn:active {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.6rem 1rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-surface);
    color: #334155;
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.16s ease, color 0.16s ease, background-color 0.16s ease;
}

.ch-sig-btn:hover { border-color: var(--ch-pdf-line-hard); color: #0F172A; }
.ch-sig-btn.is-small { padding: 0.45rem 0.75rem; font-size: 0.82rem; }

.ch-sig-btn.is-go {
    border-color: transparent;
    background-color: var(--ch-cta, #10B981);
    color: #FFFFFF;
}

.ch-sig-btn.is-go:hover { background-color: #059669; }
.ch-sig-btn.is-go[disabled] { opacity: 0.5; cursor: not-allowed; }

.ch-sig-field { display: flex; flex-direction: column; gap: 0.4rem; }

.ch-sig-field span {
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #475569;
}

.ch-sig-field input {
    width: 100%;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: #0F172A;
    font-family: inherit;
    font-size: 1rem;
}

.ch-sig-field input:focus {
    outline: none;
    border-color: var(--ch-pdf-accent);
    box-shadow: 0 0 0 3px var(--ch-pdf-accent-bg-hi);
}

/* --- Typeface picker -------------------------------------------------------
   Each button is set in the face it offers and shows the name being typed, so
   the choice is made by looking at the answer rather than at a label. */

.ch-sig-faces {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 0.45rem;
}

.ch-sig-face,
.ch-sig-face:focus,
.ch-sig-face:active {
    padding: 0.55rem 0.4rem;
    border: 1px solid var(--ch-pdf-line-soft);
    border-radius: 10px;
    background-color: var(--ch-pdf-tile);
    color: #334155;
    font-size: 1.15rem;
    line-height: 1.3;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: border-color 0.16s ease, background-color 0.16s ease;
}

.ch-sig-face:hover { border-color: var(--ch-pdf-line-hard); }

.ch-sig-face.is-on {
    border-color: var(--ch-pdf-accent);
    background-color: var(--ch-pdf-accent-bg);
    color: #0F172A;
}

.ch-sig-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 110px;
    padding: 0.8rem;
    border: 1px solid var(--ch-border);
    border-radius: 12px;
    background-color: var(--ch-pdf-tile);
    overflow: hidden;
}

.ch-sig-preview.is-shot { min-height: 150px; }
.ch-sig-empty { font-size: 0.86rem; color: #94A3B8; }
.ch-sig-typed { font-size: clamp(1.8rem, 6vw, 2.6rem); line-height: 1.2; white-space: nowrap; }

/* The upload preview is a canvas with an alpha channel, so it needs something
   behind it that proves the transparency is real. A checker does that; a plain
   white panel would look identical whether the paper came out or not. */
.ch-sig-shot {
    background-image:
        linear-gradient(45deg, rgba(148, 163, 184, 0.22) 25%, transparent 25%, transparent 75%, rgba(148, 163, 184, 0.22) 75%),
        linear-gradient(45deg, rgba(148, 163, 184, 0.22) 25%, transparent 25%, transparent 75%, rgba(148, 163, 184, 0.22) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
    border-radius: 8px;
}

.ch-sig-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 2rem 1.2rem;
    border: 1px dashed var(--ch-pdf-line-soft);
    border-radius: 14px;
    background-color: var(--ch-pdf-tile);
    text-align: center;
    transition: border-color 0.16s ease, background-color 0.16s ease;
}

.ch-sig-drop.is-over {
    border-color: var(--ch-pdf-accent);
    background-color: var(--ch-pdf-accent-bg);
}

.ch-sig-drop svg { color: var(--ch-pdf-accent); }
.ch-sig-drop b { font-size: 1rem; color: #0F172A; }
.ch-sig-drop span { max-width: 40ch; font-size: 0.85rem; line-height: 1.55; color: #64748B; }
.ch-sig-drop .ch-sig-btn { margin-top: 0.4rem; }

.ch-sig-slider { display: flex; flex-direction: column; gap: 0.35rem; }

.ch-sig-slider span {
    font-family: var(--ch-font-mono, "JetBrains Mono", monospace);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #475569;
}

.ch-sig-slider input { width: 100%; accent-color: var(--ch-cta, #10B981); }

.ch-sig-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.86rem;
    color: #475569;
    cursor: pointer;
}

.ch-sig-check input { accent-color: var(--ch-cta, #10B981); }

.ch-sig-note {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.6;
    color: #64748B;
}

.ch-sig-note strong { color: #334155; }

.ch-sig-msg {
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
    margin: 0.9rem 0 0;
    padding: 0.6rem 0.75rem;
    border-radius: 10px;
    font-size: 0.85rem;
    line-height: 1.55;
}

.ch-sig-msg[hidden] { display: none; }
.ch-sig-msg svg { flex: 0 0 auto; margin-top: 2px; }

.ch-sig-msg.is-stop {
    border: 1px solid rgba(220, 38, 38, 0.35);
    background-color: rgba(220, 38, 38, 0.07);
    color: #991B1B;
}

.ch-sig-msg.is-warn {
    border: 1px solid rgba(217, 119, 6, 0.35);
    background-color: rgba(245, 158, 11, 0.08);
    color: #78350F;
}

.ch-sig-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.8rem;
    margin-top: 1.2rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--ch-border);
}

.ch-sig-remember {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: #475569;
    cursor: pointer;
}

.ch-sig-remember input { accent-color: var(--ch-cta, #10B981); }
.ch-sig-actions { display: flex; gap: 0.5rem; margin-left: auto; }

/* The redaction badge on a page tile is the one badge in the suite that is not
   emerald. Marking a page as "about to have its content destroyed" in the same
   colour as "done" is a mistake somebody makes exactly once. */
.ch-pdf-pg-edits.is-danger {
    background-color: rgba(220, 38, 38, 0.92);
    color: #FFFFFF;
}

/* ===========================================================================
   DARK
   =========================================================================== */

html[data-theme="dark"] .ch-pdf-dlg-label,
html[data-theme="dark"] .ch-sig-field span,
html[data-theme="dark"] .ch-sig-slider span { color: #94A3B8; }

html[data-theme="dark"] .ch-pdf-dlg-input,
html[data-theme="dark"] .ch-sig-field input {
    border-color: #334155;
    background-color: #0F172A;
    color: #E2E8F0;
}

html[data-theme="dark"] .ch-pdf-dlg-input:focus,
html[data-theme="dark"] .ch-sig-field input:focus {
    border-color: #34D399;
    box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.18);
}

html[data-theme="dark"] .ch-pdf-dlg-eye:hover { background-color: rgba(226, 232, 240, 0.1); color: #E2E8F0; }
html[data-theme="dark"] .ch-pdf-dlg-eye.is-on { color: #34D399; background-color: rgba(52, 211, 153, 0.14); }

html[data-theme="dark"] .ch-pdf-dlg-hint,
html[data-theme="dark"] .ch-pdf-dlg-meter b,
html[data-theme="dark"] .ch-sig-note,
html[data-theme="dark"] .ch-sig-chips-empty,
html[data-theme="dark"] .ch-sig-studio-msg,
html[data-theme="dark"] .ch-sig-studio-fine,
html[data-theme="dark"] .ch-sig-check,
html[data-theme="dark"] .ch-sig-remember,
html[data-theme="dark"] .ch-sig-drop span { color: #94A3B8; }

html[data-theme="dark"] .ch-pdf-dlg-hint strong,
html[data-theme="dark"] .ch-sig-note strong,
html[data-theme="dark"] .ch-sig-studio-fine strong,
html[data-theme="dark"] .ch-pdf-dlg-note strong,
html[data-theme="dark"] .ch-sig-drop b,
html[data-theme="dark"] .ch-sig-padhint b { color: #E2E8F0; }

html[data-theme="dark"] .ch-pdf-dlg-note {
    border-color: #334155;
    background-color: #0F172A;
    color: #94A3B8;
}

html[data-theme="dark"] .ch-pdf-dlg-note.is-warn {
    border-color: rgba(251, 191, 36, 0.35);
    background-color: rgba(120, 53, 15, 0.28);
    color: #FCD34D;
}

html[data-theme="dark"] .ch-pdf-dlg-note.is-warn strong { color: #FDE68A; }

html[data-theme="dark"] .ch-pdf-dlg-error,
html[data-theme="dark"] .ch-sig-msg.is-stop {
    border-color: rgba(248, 113, 113, 0.4);
    background-color: rgba(127, 29, 29, 0.28);
    color: #FCA5A5;
}

html[data-theme="dark"] .ch-sig-msg.is-warn {
    border-color: rgba(251, 191, 36, 0.35);
    background-color: rgba(120, 53, 15, 0.28);
    color: #FCD34D;
}

html[data-theme="dark"] .ch-pdf-dlg-btn,
html[data-theme="dark"] .ch-sig-btn,
html[data-theme="dark"] .ch-sig-x {
    border-color: #334155;
    background-color: #1E293B;
    color: #CBD5E1;
}

html[data-theme="dark"] .ch-pdf-dlg-btn:hover,
html[data-theme="dark"] .ch-sig-btn:hover,
html[data-theme="dark"] .ch-sig-x:hover { border-color: #475569; color: #F8FAFC; }

html[data-theme="dark"] .ch-pdf-dlg-btn.is-go,
html[data-theme="dark"] .ch-sig-btn.is-go { border-color: transparent; background-color: #10B981; color: #062B21; }
html[data-theme="dark"] .ch-pdf-dlg-btn.is-go:hover,
html[data-theme="dark"] .ch-sig-btn.is-go:hover { background-color: #34D399; }

html[data-theme="dark"] .ch-pdf-dlg-file,
html[data-theme="dark"] .ch-sig-drop,
html[data-theme="dark"] .ch-sig-padwrap,
html[data-theme="dark"] .ch-sig-preview,
html[data-theme="dark"] .ch-sig-face {
    border-color: #334155;
    background-color: #0F172A;
}

html[data-theme="dark"] .ch-pdf-dlg-file.is-set,
html[data-theme="dark"] .ch-sig-face.is-on,
html[data-theme="dark"] .ch-sig-drop.is-over {
    border-color: rgba(52, 211, 153, 0.5);
    background-color: rgba(52, 211, 153, 0.12);
    color: #E2E8F0;
}

html[data-theme="dark"] .ch-sig-studio,
html[data-theme="dark"] .ch-sig-card,
html[data-theme="dark"] .ch-sig-chip {
    border-color: #334155;
    background-color: #1E293B;
    color: #E2E8F0;
}

html[data-theme="dark"] .ch-sig-chip-x { border-color: #334155; background-color: #1E293B; color: #94A3B8; }
html[data-theme="dark"] .ch-sig-tabs { background-color: #0F172A; }
html[data-theme="dark"] .ch-sig-tab { color: #94A3B8; }
html[data-theme="dark"] .ch-sig-tab.is-on { background-color: #1E293B; color: #F8FAFC; }
html[data-theme="dark"] .ch-sig-tab:hover { color: #E2E8F0; }
html[data-theme="dark"] .ch-sig-title { color: #F8FAFC; }
html[data-theme="dark"] .ch-sig-kicker { color: #34D399; }
html[data-theme="dark"] .ch-sig-foot { border-top-color: #334155; }
html[data-theme="dark"] .ch-sig-baseline { background-color: #475569; }
html[data-theme="dark"] .ch-sig-ink { box-shadow: inset 0 0 0 1px rgba(226, 232, 240, 0.2); }
html[data-theme="dark"] .ch-sig-ink.is-on { border-color: #F8FAFC; }
html[data-theme="dark"] .ch-sig-studio-head { color: #94A3B8; }

/* ===========================================================================
   OVERLAY SIZING ON REAL DEVICES
   ---------------------------------------------------------------------------
   Three separate problems, all of which only appear on hardware.

   1. vh is not the visible viewport on a phone. Mobile Safari and Chrome for
      Android measure vh against the LARGEST viewport, the one you get after the
      URL bar has scrolled away. A card capped at 92vh is therefore taller than
      the screen the moment the page loads, and its footer - which is where the
      confirm button lives - sits below the fold with nothing to scroll, because
      the veil behind it has locked the body.

      dvh is the dynamic viewport and is the correct unit. The vh line stays as
      the fallback for anything that does not know dvh; the dvh line overrides
      it where it is understood.

   2. A landscape phone is about 380 px tall. A dialog designed around a
      comfortable portrait height has no room, so the signature pad and the
      paddings contract rather than the card scrolling, because scrolling to
      reach a drawing surface is worse than a smaller drawing surface.

   3. Two buttons side by side stop fitting at around 380 px once the padding
      and the gap are counted. They stack, and they go full width, because a
      44 px tall full width target is the correct answer on a touchscreen.
   =========================================================================== */

.ch-sig-card {
    max-height: min(92vh, 780px);
    max-height: min(92dvh, 780px);
}

.ch-pdf-modal-card {
    max-height: min(90vh, 760px);
    max-height: min(90dvh, 760px);
}

/* BORDER BOX, stated rather than inherited.

   Every one of these is `width: 100%` with horizontal padding on top - the
   password field carries 2.7rem of it to make room for the reveal button. Under
   the default content-box that adds up OUTSIDE the declared width, so each
   field is wider than the card that contains it and the reveal button hangs off
   the right edge into the veil.

   The theme does set border-box globally, which is why this was invisible
   during development. Relying on that is still wrong: these elements are
   portalled to <body>, so they are one stylesheet load order change away from
   being outside whatever sets it. */
.ch-pdf-dlg-input,
.ch-pdf-dlg-file,
.ch-pdf-dlg-note,
.ch-pdf-dlg-error,
.ch-pdf-dlg-btn,
.ch-sig-field input,
.ch-sig-slider input,
.ch-sig-btn,
.ch-sig-face,
.ch-sig-preview,
.ch-sig-drop,
.ch-sig-pad,
.ch-sig-padwrap,
.ch-sig-card,
.ch-pdf-modal-card { box-sizing: border-box; }

/* MIN-WIDTH ZERO, and this one is not optional.

   Both cards are flex ITEMS: the overlay that centres them is display:flex. A
   flex item defaults to `min-width: auto`, which means it refuses to shrink
   below the widest thing inside it - here the three-tab strip and the longest
   unbroken run of the explanatory copy.

   On a desktop there is always room, so the card looks perfect and the bug is
   invisible. At 390 px the card computes wider than the screen, sits with the
   veil's alignment offset on the left, and the Upload tab, the Clear button and
   the confirm button are all off the right edge with no way to reach them,
   because the body is scroll locked.

   The children need it for the same reason one level down. */
.ch-sig-card,
.ch-pdf-modal-card { min-width: 0; }

.ch-sig-tabs,
.ch-sig-body,
.ch-sig-row,
.ch-sig-foot,
.ch-pdf-dlg-fields,
.ch-pdf-dlg-row,
.ch-pdf-dlg-actions,
.ch-sig-studio,
.ch-sig-studio-main { min-width: 0; }

.ch-sig-tab { min-width: 0; }

/* The tab label is the thing that would otherwise set the floor. It ellipses
   rather than forcing the card wider than the phone. */
.ch-sig-tab span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The editor already solved this and its answer is the one being copied here:
   below a breakpoint it stops being a card and becomes the whole screen. */

/* --- Narrow: phones in portrait ------------------------------------------ */
@media (max-width: 480px) {

    .ch-sig-modal,
    .ch-pdf-modal {
        padding: 0;
        align-items: flex-end;
    }

    /* Full bleed, anchored to the bottom edge. A dialog that fills the screen
       is easier to use one handed than one floating in the middle with 8 px of
       veil showing round it, and the bottom edge is where the thumb is.

       Every card in this family, not only the form one. Applying it to the form
       dialog alone left the handoff modal keeping its 460 px width and its four
       rounded corners while the container had lost its padding, so it sat
       against the bottom edge with a gap down each side and none underneath. */
    .ch-sig-card,
    .ch-pdf-modal-card {
        width: 100%;
        max-width: 100%;
        max-height: 94dvh;
        border-radius: 18px 18px 0 0;
        border-bottom: 0;
        padding: 1.15rem 1rem calc(1rem + env(safe-area-inset-bottom));
    }

    /* Stacked, full width, primary action LOWEST.

       The card is anchored to the bottom edge, so the lowest control is the one
       nearest the thumb, and the one the visitor came to press should be the
       one they can reach without shifting their grip. Reading order stays
       natural too: the option, then cancel, then the action. */
    .ch-pdf-dlg-actions,
    .ch-sig-actions {
        flex-direction: column;
        width: 100%;
        margin-left: 0;
    }

    .ch-pdf-dlg-btn,
    .ch-sig-actions .ch-sig-btn {
        width: 100%;
        min-height: 46px;
    }

    .ch-sig-foot {
        flex-direction: column;
        align-items: stretch;
        gap: 0.9rem;
    }

    /* The "keep this signature" option modifies what the confirm button does,
       so it is read before it rather than stranded underneath it. */
    .ch-sig-remember { order: 1; }
    .ch-sig-actions { order: 2; }

    /* The typeface picker becomes two across rather than shrinking to a width
       where none of the samples are legible. */
    .ch-sig-faces { grid-template-columns: repeat(2, minmax(0, 1fr)); }

    .ch-sig-title { font-size: 1.1rem; }
    .ch-sig-drop { padding: 1.4rem 0.9rem; }

    /* The studio panel above the page board: the certificate button drops under
       the signature chips instead of fighting them for width. */
    .ch-sig-studio { padding: 0.85rem 0.9rem; }
    .ch-sig-chip { width: 118px; height: 56px; }
}

/* --- Short: phones in landscape, and small windows ----------------------- */
@media (max-height: 560px) and (orientation: landscape) {

    .ch-sig-modal,
    .ch-pdf-modal { padding: 0.5rem; align-items: center; }

    .ch-sig-card,
    .ch-pdf-modal-card {
        max-height: 96dvh;
        padding: 0.9rem 1.1rem;
    }

    /* The pad shrinks rather than the card scrolling. A drawing surface you
       have to scroll to reach is not a drawing surface. */
    .ch-sig-pad { height: clamp(96px, 30dvh, 150px); }

    .ch-sig-tabs { margin: 0.7rem 0 0.6rem; }
    .ch-sig-body { gap: 0.6rem; }
    .ch-sig-foot { margin-top: 0.8rem; padding-top: 0.7rem; }

    /* The explanatory paragraphs are the first thing to go when there is no
       room. They are worth reading once, not on every signature. */
    .ch-sig-note,
    .ch-pdf-modal.is-form .ch-pdf-modal-fine { display: none; }

    /* The second line of the pad hint goes too, for the same reason and one
       more: on a pad this short the ruled baseline sits where that line of text
       is, and a guide crossing its own instructions reads as a rendering
       fault. "Sign here" alone says everything the control needs to. */
    .ch-sig-padhint span { display: none; }
    .ch-sig-baseline { bottom: 22%; }
}

/* --- Coarse pointers: fingers, not mice ---------------------------------- */
@media (pointer: coarse) {

    /* 44 px is the smallest target that can be hit reliably with a thumb, and
       every one of these is a control somebody taps while holding a phone in
       one hand. */
    .ch-sig-tab,
    .ch-pdf-dlg-btn,
    .ch-sig-btn { min-height: 44px; }

    .ch-sig-x,
    .ch-pdf-modal-x { width: 44px; height: 44px; }

    .ch-pdf-dlg-eye { width: 40px; height: 40px; }

    .ch-sig-ink { width: 36px; height: 36px; }

    /* The ink swatch row and the undo/clear pair sit on one line on a desktop
       and need room to breathe when they are being tapped. */
    .ch-sig-row { gap: 0.85rem; }

    /* A range input with a 16 px thumb is unusable with a finger. */
    .ch-sig-slider input { height: 32px; }
}

/* --- Reduced motion ------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .ch-pdf-modal-card,
    .ch-sig-card { animation: none; }
}

/* --- Dark, for the two rules above that set their own surfaces ------------ */
html[data-theme="dark"] .ch-sig-card,
html[data-theme="dark"] .ch-pdf-modal-card {
    /* The card colour comes from the token block at the top of this file now
       that the modal roots carry the tokens. This branch only exists to keep
       the border readable against the veil, which is the same near-black in
       both themes. */
    border-color: #334155;
}
