/* Unpixel — the one stylesheet behind the converter (ADR-0054, R1).
   Moved verbatim out of public/index.html's <style> on 2026-08-21; served as
   written, no build step. The home page and every page that embeds the
   converter load this file. The two marked blocks below — QoL-8 tokens and
   QoL-8 site chrome — are ALSO carried inline by the content pages that do
   not load this file (this file styles bare main/button/select/aside/
   inputs, which would restyle a prose page): this copy is the reference,
   and test/pages.test.js holds every inline copy byte-identical to it.
   Referenced as /app.css?v=<8 hex of sha256(app.css ‖ app.js)> — the same
   test recomputes that token, so a change here that is not re-referenced
   on every page is a red build, never a stale cache.
   Visual system v2 (the 2026-09 redesign): one centred upload card on the
   first screen with the three steps as its footer strip, the workspace
   filling the viewport once an image is in, the Download screen in two
   columns while it sells the file on screen. */
  * { box-sizing: border-box; margin: 0; padding: 0; }
  [hidden] { display: none !important; }

  /* ==== QoL-8 tokens — hand-synced VERBATIM on every public/*.html (test/pages.test.js pins the block) ==== */
  :root {
    /* type */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    --fs-2xs: 11px; --fs-xs: 12px; --fs-sm: 13px; --fs-base: 14px;
    --fs-md: 15px; --fs-lg: 17px; --fs-xl: 20px; --fs-2xl: 26px; --fs-3xl: 34px; --fs-4xl: 46px;
    --lh: 1.5; --lh-tight: 1.2;
    /* spacing — 4px grid */
    --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
    --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px; --sp-12: 48px; --sp-16: 64px;
    /* layout */
    --container: 1280px;
    /* surfaces & lines */
    --bg: #f6f6f9; --bg-sunken: #eeeef4; --card: #ffffff;
    --line: #e5e5ee; --line-strong: #d3d3df;
    /* ink */
    --text: #0e0f1a; --text-2: #3e3f51; --muted: #666778; --faint: #9a9aac;
    /* accent — the indigo seed, and the violet it leans into */
    --accent: #4f46e5; --accent-strong: #4338ca; --accent-weak: #eef0ff;
    --accent-line: #d6d3fa; --on-accent: #ffffff; --accent-2: #7c3aed;
    --ring: rgba(79, 70, 229, .4);
    /* semantic */
    --ok: #16a34a; --ok-deep: #166534; --ok-weak: #ecfdf5; --ok-line: #bbf7d0;
    --warn: #b45309; --warn-weak: #fdf3e4;
    --danger: #dc2626; --danger-weak: #fdeded;
    /* radii · elevation · motion */
    --r-sm: 7px; --r-md: 10px; --r-lg: 14px; --r-xl: 18px; --r-2xl: 24px; --r-full: 999px;
    --shadow-1: 0 1px 2px rgba(14, 15, 26, .06);
    --shadow-2: 0 1px 2px rgba(14, 15, 26, .04), 0 10px 30px -10px rgba(14, 15, 26, .14);
    --shadow-3: 0 2px 6px rgba(14, 15, 26, .05), 0 26px 60px -14px rgba(14, 15, 26, .24);
    --shadow-accent: 0 1px 2px rgba(67, 56, 202, .28), 0 8px 20px -8px rgba(79, 70, 229, .6);
    --t-fast: .12s; --t-med: .22s; --ease: cubic-bezier(.2, .6, .2, 1);
  }
  /* ==== /tokens ==== */

  html, body { height: 100%; }
  body {
    font: var(--fs-base)/var(--lh) var(--font-sans);
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    display: flex;
    flex-direction: column;
    /* App-scoped (the :root token block is pinned across pages): the boundary
       of a field you type into — 3.4:1 on the card, where --line-strong's
       hairline reads 1.4:1 (QoL-9 contrast pass; decorative hairlines keep
       --line-strong on purpose). */
    --field-line: #8a8a99;
    /* The app's glyphs, drawn once as masks and painted in currentColor, so
       an icon always matches the text of the button it sits in. */
    --i-upload: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 15V4M7.5 8.5 12 4l4.5 4.5M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3'/%3E%3C/svg%3E");
    --i-download: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4v11M7.5 10.5 12 15l4.5-4.5M4 20h16'/%3E%3C/svg%3E");
    --i-camera: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 8.5h3l1.8-2.5h6.4L17 8.5h3V19H4z'/%3E%3Ccircle cx='12' cy='13.5' r='3.4'/%3E%3C/svg%3E");
    --i-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
    --i-resume: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 12a8.5 8.5 0 1 0 2.6-6.1L3.5 8.5'/%3E%3Cpath d='M3.5 3.5v5h5M12 8v4.5l3 2'/%3E%3C/svg%3E");
    --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
    --i-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='5' y='11' width='14' height='9' rx='2'/%3E%3Cpath d='M8.5 11V8a3.5 3.5 0 0 1 7 0v3'/%3E%3C/svg%3E");
    --i-eye: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E");
    --i-curve: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 19C7 9 17 15 19 5'/%3E%3Crect x='2.5' y='16.5' width='5' height='5' rx='1'/%3E%3Crect x='16.5' y='2.5' width='5' height='5' rx='1'/%3E%3C/svg%3E");
    --i-layers: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5 3 8.5l9 5 9-5z'/%3E%3Cpath d='M3 12.5l9 5 9-5M3 16.5l9 5 9-5'/%3E%3C/svg%3E");
    --i-file: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.5 3H14l4 4v14H6.5z'/%3E%3Cpath d='M14 3v4h4M9.5 12.5h5M9.5 16h5'/%3E%3C/svg%3E");
    /* Safe-area (QoL-9): the app page declares viewport-fit=cover, so notched
       phones get the mat edge-to-edge and the content padded back in. */
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  /* ==== QoL-8 site chrome — hand-synced VERBATIM on every public/*.html (test/pages.test.js pins the block) ==== */
  /* Solid accent, not the 40%-alpha --ring: the ring blended to ~1.9:1 against
     a white card, under the 3:1 a focus indicator needs (QoL-9 a11y pass). */
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  }
  ::selection { background: rgba(79, 70, 229, .2); }
  /* The header (visual system v2): a full-width bar pinned to the top, its
     content on the container axis, so header, converter card and footer share
     one edge. OPAQUE ON PURPOSE — a backdrop blur re-composites everything
     under the bar on every scroll frame and read as slow scrolling (Michael,
     2026-08-08). A solid bar costs nothing to scroll under. */
  header {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
    min-height: 64px;
    padding: 10px max(var(--sp-5), calc((100% - var(--container)) / 2));
    background: var(--card);
    border-bottom: 1px solid var(--line);
    flex: 0 0 auto;
  }
  .brand {
    display: flex; align-items: center; gap: 10px;
    font-weight: 750; font-size: 17px; letter-spacing: -.02em;
    color: var(--text); text-decoration: none; white-space: nowrap;
  }
  .brand svg { display: block; width: 26px; height: 26px; }
  .brand .sub {
    font-weight: 600; font-size: var(--fs-xs); letter-spacing: 0; color: var(--text-2);
    background: var(--bg-sunken); border-radius: var(--r-full); padding: 3px 9px; margin-left: 2px;
  }
  .actions { display: flex; gap: var(--sp-2); align-items: center; }
  #siteNav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
  #siteNav a {
    color: var(--text-2); text-decoration: none; font-weight: 550; font-size: 14px;
    padding: 8px 12px; border-radius: var(--r-md);
    transition: background var(--t-fast), color var(--t-fast);
  }
  #siteNav a:hover { color: var(--text); background: var(--bg-sunken); }
  /* The content pages' one header action: the road back into the converter —
     ink-dark, not accent, so a page's own call to action keeps the accent. */
  .hdrCta {
    display: inline-flex; align-items: center; white-space: nowrap;
    font-size: 14px; font-weight: 600; text-decoration: none; color: #fff;
    background: var(--text); border-radius: var(--r-md); padding: 9px 16px;
    transition: background var(--t-fast);
  }
  .hdrCta:hover { background: #2a2b3d; }
  #navToggle {
    display: none; align-items: center; justify-content: center;
    width: 40px; height: 40px; padding: 0; border: none; border-radius: var(--r-md); background: transparent;
    color: var(--text-2); position: relative; cursor: pointer; font: inherit;
  }
  #navToggle:hover { color: var(--text); background: var(--bg-sunken); }
  @media (max-width: 1000px) {
    .brand .sub { display: none; }
  }
  @media (max-width: 820px) {
    header { min-height: 58px; padding: 8px var(--sp-4); gap: var(--sp-2); }
    #navToggle { display: inline-flex; margin-left: auto; }
    #siteNav {
      display: none; position: absolute; top: calc(100% + 6px); left: 12px; right: 12px; z-index: 40;
      flex-direction: column; align-items: stretch; gap: 2px; margin-left: 0;
      background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
      padding: 8px; box-shadow: var(--shadow-3);
    }
    #siteNav.open { display: flex; }
    #siteNav a { padding: 12px 14px; font-size: var(--fs-md); border-radius: var(--r-md); }
    .hdrCta { padding: 8px 13px; font-size: 13.5px; }
  }
  footer#siteFooter {
    background: var(--card);
    border-top: 1px solid var(--line);
    padding: 48px max(var(--sp-5), calc((100% - var(--container)) / 2)) 40px;
    flex: 0 0 auto;
  }
  .foot-grid {
    max-width: var(--container); margin: 0 auto;
    display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: var(--sp-8);
  }
  .foot-brand { display: flex; flex-direction: column; gap: 10px; }
  .foot-mark { display: flex; align-items: center; gap: 9px; font-weight: 750; font-size: 16px; letter-spacing: -.02em; }
  .foot-tag { color: var(--muted); font-size: 13.5px; line-height: 1.55; max-width: 300px; }
  .foot-copy { color: var(--muted); font-size: var(--fs-sm); }
  .foot-col { display: flex; flex-direction: column; gap: 10px; }
  /* A styled label, not a heading (S2.1b): after the page's h1/h2 an h4
     here was the heading-order skip on every page; the columns are labelled
     <nav>s, which is the landmark assistive tech uses. */
  .foot-col .foot-h { font-size: var(--fs-sm); color: var(--text); font-weight: 650; margin-bottom: 2px; }
  .foot-col a { color: var(--muted); text-decoration: none; font-size: 14px; width: fit-content; transition: color var(--t-fast); }
  .foot-col a:hover { color: var(--accent); text-decoration: underline; }
  @media (max-width: 820px) {
    footer#siteFooter { padding: 36px var(--sp-5) 32px; }
    .foot-grid { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
    .foot-brand { grid-column: 1 / -1; }
  }
  /* ==== /site chrome ==== */

  button {
    font: inherit;
    font-size: 13.5px;
    font-weight: 550;
    line-height: 1.3;
    border: 1px solid var(--line-strong);
    background: var(--card);
    color: var(--text);
    border-radius: var(--r-md);
    padding: 8px 14px;
    cursor: pointer;
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast),
      box-shadow var(--t-fast), opacity var(--t-fast), transform var(--t-fast);
  }
  button:hover:not(:disabled) { background: #f8f8fb; border-color: #c3c3d1; }
  button:active:not(:disabled) { transform: translateY(1px); }
  button:disabled { opacity: .45; cursor: default; }
  button.primary {
    background: var(--accent); border-color: var(--accent);
    color: var(--on-accent); font-weight: 650;
    box-shadow: var(--shadow-accent), inset 0 1px 0 rgba(255, 255, 255, .16);
  }
  button.primary:hover:not(:disabled) {
    background: var(--accent-strong); border-color: var(--accent-strong);
    box-shadow: 0 1px 2px rgba(67, 56, 202, .3), 0 12px 26px -8px rgba(79, 70, 229, .65), inset 0 1px 0 rgba(255, 255, 255, .16);
  }
  button.small { padding: 5px 10px; font-size: var(--fs-xs); border-radius: var(--r-sm); }
  /* A glyph before a label: the mask is set per button below. */
  .ico::before,
  .pickRow button::before, #resumeChip::before, #newBtn::before,
  #downloadBtn::before, #resultDownloadBtn::before, #dlDownload::before {
    content: ''; flex: none; width: 17px; height: 17px; background: currentColor;
    -webkit-mask: var(--i-download) center / contain no-repeat; mask: var(--i-download) center / contain no-repeat;
  }
  /* The header action's two labels (QoL-9): full text on wide screens, the
     short word under 600px — one row of header at every width. One button
     since S2.6 (Copy SVG moved to the Download screen); it wears .primary
     only while the readout's own Download SVG is out of view — app.js
     toggles the class, so nothing here needs a state. */
  .actions button { display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px; font-size: 14px; }
  .actions .btnTxtShort { display: none; }
  /* Nothing to download before the first result (visual system v2): the
     header's Download arrives with the trace instead of sitting disabled —
     a greyed accent button was the loudest thing on an empty page. */
  #downloadBtn:disabled { display: none; }

  /* The workspace is a framed app (QoL-8.1): main is the mat, #appFrame the
     one elevated container, capped at the chrome's container width so
     header, frame and footer share an axis. */
  main {
    flex: 1; display: flex; min-height: 0; padding: var(--sp-4) var(--sp-5);
  }
  #appFrame {
    flex: 1; display: flex; min-width: 0; min-height: 0;
    width: 100%; max-width: var(--container); margin-inline: auto;
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--r-2xl); box-shadow: var(--shadow-2); overflow: hidden;
  }

  /* ---------- Sidebar ---------- */
  aside {
    width: 292px;
    flex: 0 0 292px;
    background: var(--card);
    border-right: 1px solid var(--line);
    overflow-y: auto;
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    scrollbar-width: thin;
  }
  aside::-webkit-scrollbar, #layerList::-webkit-scrollbar, #dlScreen::-webkit-scrollbar { width: 8px; }
  aside::-webkit-scrollbar-thumb, #layerList::-webkit-scrollbar-thumb, #dlScreen::-webkit-scrollbar-thumb {
    background: var(--line-strong); border-radius: var(--r-full); border: 2px solid transparent; background-clip: content-box;
  }
  aside::-webkit-scrollbar-track, #layerList::-webkit-scrollbar-track, #dlScreen::-webkit-scrollbar-track { background: transparent; }

  #traceControls { display: flex; flex-direction: column; gap: var(--sp-4); }

  details.group { border-bottom: 1px solid var(--line); padding-bottom: var(--sp-4); }
  details.group:last-child { border-bottom: none; padding-bottom: 0; }
  .group summary {
    list-style: none; cursor: pointer; display: flex; align-items: center;
    user-select: none; -webkit-user-select: none;
  }
  .group summary::-webkit-details-marker { display: none; }
  .group summary h3 {
    font-size: var(--fs-xs); letter-spacing: .07em; text-transform: uppercase;
    color: var(--text-2); font-weight: 700;
  }
  .group summary h3 .hint { text-transform: none; letter-spacing: 0; font-weight: 500; margin-left: 4px; }
  .group summary::after {
    content: ''; width: 7px; height: 7px; flex: none;
    /* --muted, not --faint: the disclosure chevron is a state indicator and
       faint reads 2.7:1 on the card (QoL-9 contrast pass; 3:1 is the bar). */
    border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
    transform: rotate(45deg); margin-left: auto; margin-bottom: 2px;
    transition: transform var(--t-fast);
  }
  .group:not([open]) summary::after { transform: rotate(-45deg); margin-bottom: -2px; }
  .groupBody { display: flex; flex-direction: column; gap: var(--sp-3); padding-top: var(--sp-3); }
  details[open] > .groupBody { animation: fadeSlide var(--t-med) var(--ease); }
  @keyframes fadeSlide { from { opacity: 0; transform: translateY(-4px); } }

  .ctl { display: flex; flex-direction: column; gap: 6px; }
  .ctl-top { display: flex; align-items: baseline; gap: 6px; }
  .ctl-top label { font-weight: 550; font-size: 13.5px; }
  .ctl-top output {
    margin-left: auto; color: var(--text-2); font-size: var(--fs-xs); font-weight: 550;
    background: var(--bg); border-radius: var(--r-full); padding: 1px 8px;
    font-variant-numeric: tabular-nums;
  }

  /* On-demand hints (QoL-8): the explanation is a click away, never a wall. */
  .why {
    width: 17px; height: 17px; padding: 0; border: 1px solid var(--line-strong); border-radius: var(--r-full);
    background: transparent; color: var(--muted); font-size: 10.5px; font-weight: 700;
    line-height: 1; display: inline-flex; align-items: center; justify-content: center;
    flex: none; cursor: pointer; position: relative; align-self: center;
    transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
  }
  .why:hover { background: var(--bg-sunken); color: var(--text-2); }
  .why[aria-expanded="true"] { background: var(--accent-weak); color: var(--accent); border-color: var(--accent-line); }
  .why::after { content: ''; position: absolute; inset: -6px; }
  .hint { color: var(--muted); font-size: var(--fs-xs); line-height: 1.55; }
  .hint.ondemand { background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 10px; }

  /* Sliders: filled track + ringed thumb. --fill is set by updateOutputs()
     so the track reads the value at a glance; disabled tracks go flat. */
  input[type=range] {
    -webkit-appearance: none; appearance: none;
    width: 100%; height: 22px; background: transparent; cursor: pointer;
  }
  input[type=range]::-webkit-slider-runnable-track {
    height: 4px; border-radius: var(--r-full);
    background: linear-gradient(to right, var(--accent) var(--fill, 0%), var(--bg-sunken) var(--fill, 0%));
  }
  input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px;
    border-radius: var(--r-full); background: var(--card);
    border: 2px solid var(--accent); box-shadow: 0 1px 3px rgba(14, 15, 26, .18);
    transition: transform var(--t-fast), box-shadow var(--t-fast);
  }
  input[type=range]:hover:not(:disabled)::-webkit-slider-thumb { transform: scale(1.12); }
  input[type=range]:active:not(:disabled)::-webkit-slider-thumb { transform: scale(1.2); box-shadow: 0 0 0 5px var(--accent-weak); }
  input[type=range]::-moz-range-track { height: 4px; border-radius: var(--r-full); background: var(--bg-sunken); }
  input[type=range]::-moz-range-progress { height: 4px; border-radius: var(--r-full); background: var(--accent); }
  input[type=range]::-moz-range-thumb {
    width: 12px; height: 12px; border-radius: var(--r-full); background: var(--card);
    border: 2px solid var(--accent); box-shadow: var(--shadow-1);
  }
  input[type=range]:disabled { opacity: .45; cursor: default; }
  input[type=range]:disabled::-webkit-slider-runnable-track { background: var(--bg-sunken); }
  input[type=range]:disabled::-webkit-slider-thumb { border-color: var(--faint); transform: none; }
  .row { display: flex; align-items: center; gap: var(--sp-2); }
  .row label { font-weight: 550; font-size: 13.5px; }
  input[type=checkbox] { accent-color: var(--accent); width: 16px; height: 16px; }
  input[type=color] {
    width: 34px; height: 28px; padding: 2px 3px;
    border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--card); cursor: pointer;
  }
  select {
    font: inherit; font-size: 13px; padding: 6px 8px; border: 1px solid var(--field-line);
    border-radius: var(--r-sm); background: var(--card); color: var(--text);
  }

  .seg { display: flex; background: var(--bg-sunken); border-radius: 11px; padding: 3px; gap: 2px; }
  .seg button {
    flex: 1; border: none; background: transparent; border-radius: 8px;
    padding: 6px 9px; font-size: 13px; font-weight: 550; color: var(--text-2); box-shadow: none;
  }
  .seg button:hover:not(:disabled) { background: rgba(255, 255, 255, .7); color: var(--text); }
  .seg button.on { background: var(--card); color: var(--text); font-weight: 650; box-shadow: 0 1px 2px rgba(14, 15, 26, .1), 0 0 0 1px rgba(14, 15, 26, .04); }
  #modeSeg button { padding: 8px 6px; font-size: 13.5px; white-space: nowrap; }

  .sidebar-foot { margin-top: auto; color: var(--muted); font-size: var(--fs-xs); line-height: 1.55; padding-top: var(--sp-2); }

  /* ---------- Color-mode layer panel ---------- */
  #layerList {
    display: flex; flex-direction: column; max-height: 340px; overflow-y: auto;
    margin: 0 -5px; padding: 0 5px; scrollbar-width: thin;
  }
  .layer-row { display: flex; align-items: center; gap: 7px; padding: 3px 4px; border-radius: var(--r-sm); }
  .layer-row:hover { background: var(--bg); }
  .layer-row .eye {
    width: 26px; height: 24px; padding: 0; flex: none;
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; line-height: 1; border: none; background: transparent;
    border-radius: var(--r-sm); color: var(--text-2);
  }
  .layer-row .eye:hover:not(:disabled) { background: var(--bg-sunken); }
  .layer-row input[type=color] { width: 26px; height: 22px; flex: none; border-radius: 6px; padding: 1px 2px; }
  .layer-row .lbl {
    flex: 1; min-width: 0; font-size: var(--fs-xs); font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-2);
  }
  .layer-row .lbl code { font-family: var(--font-mono); font-size: 11px; color: var(--text); }
  .layer-row select { max-width: 64px; font-size: 11px; padding: 3px 4px; flex: none; border-radius: 6px; color: var(--muted); }
  .layer-row.off .lbl { opacity: .45; text-decoration: line-through; }

  /* ---------- Stage ---------- */
  #stage {
    flex: 1; min-width: 0; display: flex; flex-direction: column;
    padding: var(--sp-4); gap: var(--sp-3);
    background: #f8f8fb;
  }

  /* The drop zone is the landing moment (QoL-8; cut back 2026-09-24 and
     2026-09-26, its spacing re-set 2026-09-25 at Michael's direction): the
     dashed target holds one message and one action — the icon, the heading,
     Upload image and a quiet line — and since M55 nothing else sits in the
     card. The target sits on a 20 px mat inside the card, so the dashes read
     as a zone and not as a second border (a 12 px inset drew a double
     frame), and its corners take the button's radius — a concentric one
     (24 − 20) would be 4 px, sharper than anything else on the page. Two
     groups inside it, not one flat stack: the icon and the heading (12 px
     apart), then the action and its hint (12 px), 24 px between the groups
     and a 40 px frame around them. */
  #empty {
    flex: 1; display: flex; flex-direction: column; align-items: stretch;
    gap: var(--sp-5); color: var(--muted); text-align: center;
  }
  .dropTarget {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: var(--sp-10) var(--sp-6);
    border: 2px dashed #cbcbe0; border-radius: var(--r-lg); cursor: pointer;
    background: radial-gradient(70% 90% at 50% 0%, rgba(99, 102, 241, .07), rgba(99, 102, 241, 0) 70%), #fdfdff;
    transition: border-color var(--t-med), background-color var(--t-med), box-shadow var(--t-med);
  }
  .dropTarget:hover { border-color: var(--accent); background-color: #f9f9ff; box-shadow: inset 0 0 0 4px rgba(79, 70, 229, .05); }
  .dropIcon {
    display: flex; align-items: center; justify-content: center; flex: none;
    width: 56px; height: 56px; margin-bottom: var(--sp-3); border-radius: var(--r-full);
    color: var(--accent); background: var(--accent-weak);
    transition: transform var(--t-med) var(--ease);
  }
  .dropTarget:hover .dropIcon { transform: translateY(-3px); }
  /* The drop-zone heading is an h2 on every page (S2.5); the h1 selector is
     history — same look. */
  #empty h1, #empty h2 { color: var(--text); font-size: 26px; font-weight: 750; letter-spacing: -.025em; line-height: var(--lh-tight); }
  #empty .dropSub { margin-top: var(--sp-3); font-size: var(--fs-sm); color: var(--muted); }
  /* Two copies of the drop zone's copy, one shown (S2.5): .onPhone under the
     phone breakpoint, .onDesk everywhere else — display:none keeps the hidden
     copy out of the accessibility tree too. */
  #empty .onPhone { display: none; }
  .pickRow { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: var(--sp-6); }
  .pickRow button {
    display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    min-height: 50px; padding: 12px 28px; font-size: 16px; border-radius: var(--r-lg);
  }
  .pickRow button.primary { min-width: 220px; }
  .pickRow button::before { width: 19px; height: 19px; -webkit-mask-image: var(--i-upload); mask-image: var(--i-upload); }
  #cameraBtn::before { -webkit-mask-image: var(--i-camera); mask-image: var(--i-camera); }

  #panes {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr 1.25fr;
    gap: var(--sp-3);
  }
  .pane {
    display: flex; flex-direction: column; min-height: 0;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
    overflow: hidden; box-shadow: var(--shadow-1);
  }
  .pane.result { border-color: var(--accent-line); box-shadow: 0 0 0 3px rgba(79, 70, 229, .07), var(--shadow-2); }
  .paneHead {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    min-height: 42px; padding: 9px 14px; border-bottom: 1px solid var(--line);
    font-weight: 650; font-size: 13px; flex: 0 0 auto;
  }
  .pane:not(.result) .paneHead > span:first-child { color: var(--text-2); font-weight: 550; }
  .pane.result .paneHead > span:first-child { display: inline-flex; align-items: center; gap: 8px; }
  .pane.result .paneHead > span:first-child::before {
    content: ''; width: 8px; height: 8px; border-radius: var(--r-full);
    background: var(--ok); box-shadow: 0 0 0 3px var(--ok-weak); flex: none;
  }
  .paneHead .meta { color: var(--muted); font-weight: 450; font-size: var(--fs-xs); text-align: right; font-variant-numeric: tabular-nums; }

  /* The result's readout (QoL-8): nodes · size · time is the quality argument,
     not metadata — it gets a designed strip under the preview. Since S2.6 the
     strip has two ends: the numbers, and the next step — the plan in small
     type and Download SVG — pushed to the far edge; on a narrow pane the two
     wrap onto their own rows. */
  .paneFoot {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3) var(--sp-5); flex-wrap: wrap;
    padding: 10px 12px 10px 16px; border-top: 1px solid var(--line);
    background: linear-gradient(to bottom, #fafaff, #f6f6fe);
    flex: 0 0 auto;
  }
  .paneFoot .stats { display: flex; align-items: baseline; gap: 4px 16px; flex-wrap: wrap; min-width: 0; }
  /* A narrow result column (≈981–1150 px windows) sets the plan note over the
     button rather than squeezing the button onto two lines. */
  .resultCta { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 6px 12px; margin-left: auto; }
  .resultCta .planNote { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
  #resultDownloadBtn { display: inline-flex; align-items: center; gap: 8px; padding: 10px 18px; font-size: 14.5px; border-radius: 12px; white-space: nowrap; }
  .stat { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; }
  .stat b { font-size: var(--fs-md); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
  .stat span { font-size: var(--fs-xs); color: var(--muted); }
  /* Fresh traces reveal their numbers left to right — one beat, staggered. */
  .paneFoot.fresh .stat { animation: settle .45s var(--ease) backwards; }
  .paneFoot.fresh .stat:nth-child(2) { animation-delay: .05s; }
  .paneFoot.fresh .stat:nth-child(3) { animation-delay: .1s; }
  .paneFoot.fresh .stat:nth-child(4) { animation-delay: .15s; }
  .paneFoot.fresh .stat:nth-child(5) { animation-delay: .2s; }
  @keyframes settle { from { opacity: 0; transform: translateY(5px); } }

  .canvas { flex: 1; position: relative; min-height: 220px; }
  .canvas.checker { background: repeating-conic-gradient(#eeeef3 0% 25%, #fbfbfd 0% 50%) 0 0 / 18px 18px; }
  .canvas.light { background: #ffffff; }
  .canvas.dark { background: #16161d; }
  .fit { position: absolute; inset: 14px; }
  .fit img { width: 100%; height: 100%; display: block; }
  .fit img { object-fit: contain; }

  /* The preview pane holds a server-rendered PNG since S2 (the SVG never
     reaches this page). Two rules keep it from reading softer than the inline
     SVG it replaced: the raster is asked for at pane size × devicePixelRatio,
     and it is displayed at the CSS width the server hands back — which is never
     wider than the raster's own pixels, so nothing is ever upscaled. */
  #previewFit { position: absolute; inset: 14px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
  #previewImg { display: block; max-width: 100%; max-height: 100%; object-fit: contain; transition: opacity .1s; will-change: transform; }
  /* The optimistic ghost: while the server renders the crisp view, the current
     raster is CSS-scaled to roughly where that view will land. Animated for a
     zoom step (a click deserves a beat of motion), instant for a drag. */
  #previewImg.ghost-anim { transition: transform .18s var(--ease), opacity .1s; }
  #previewFit.zoomed { cursor: grab; }
  #previewFit.zoomed.dragging { cursor: grabbing; }
  #previewFit.empty #previewImg { display: none; }

  #zoomBar { display: flex; align-items: center; gap: var(--sp-2); }
  #zoomBar .seg { width: auto; }
  #zoomBar .seg button { padding: 6px 11px; font-size: var(--fs-xs); }
  /* Two ids so the width survives #bottomBar .seg's fixed width below. */
  #bottomBar #viewSeg { width: auto; }
  #viewSeg button { flex: 0 0 auto; }
  #zoomNote { color: var(--muted); font-size: var(--fs-xs); line-height: 1.35; min-width: 0; }

  /* Busy indicators: a bar across the pane's top edge — indeterminate sweep
     while the server works, a real fill while a known-length response body
     downloads — and a pill that says what is actually happening. The pill
     waits 250ms before showing so a warm ~100ms render never flashes it. */
  #busyBar {
    position: absolute; top: 0; left: 0; right: 0; height: 3px; z-index: 6;
    background: rgba(79, 70, 229, .14); overflow: hidden;
  }
  #busyBar .fill { height: 100%; width: 0%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); transition: width .12s linear; }
  #busyBar.indet .fill { width: 34%; animation: busysweep 1.05s ease-in-out infinite; }
  @keyframes busysweep { from { transform: translateX(-110%); } to { transform: translateX(400%); } }
  #busyPill {
    position: absolute; left: 50%; top: 14px; transform: translateX(-50%); z-index: 6;
    display: flex; align-items: center; gap: 9px; max-width: calc(100% - 24px);
    background: rgba(20, 20, 30, .92); color: #fff; padding: 8px 15px; border-radius: var(--r-full);
    font-size: var(--fs-sm); font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    box-shadow: var(--shadow-3); pointer-events: none;
  }
  #busyPill .ring {
    width: 13px; height: 13px; border-radius: 50%; flex: none;
    border: 2px solid rgba(255, 255, 255, .3); border-top-color: #fff;
    animation: spin .7s linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }

  /* The row under the panes wraps rather than clipping: at ≈981–1100 px the
     stage is too narrow for all of it on one line, and New image — the last
     item, pushed right — takes a second row instead of losing its edge. */
  #bottomBar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); flex: 0 0 auto; }
  #bottomBar .seg { width: auto; }
  #bgSeg button { padding: 6px 12px; }
  #bottomBar > .row { margin-left: auto; }
  #newBtn { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
  #newBtn::before { width: 15px; height: 15px; -webkit-mask-image: var(--i-plus); mask-image: var(--i-plus); }

  /* ---------- Overlay, toast ---------- */
  #dropOverlay {
    position: fixed; inset: 0; z-index: 50;
    background: rgba(79, 70, 229, .12);
    backdrop-filter: blur(2px);
    display: flex; align-items: center; justify-content: center;
    pointer-events: none;
  }
  #dropOverlay .box {
    background: var(--card); border: 2px dashed var(--accent); border-radius: var(--r-xl);
    padding: 30px 48px; font-size: var(--fs-xl); font-weight: 700; color: var(--accent);
    box-shadow: var(--shadow-3);
  }
  /* One coherent toast pattern (QoL-8): neutral dark surface, the kind carried
     by the icon — ✓ ok, ! error, i disclosure — errors held longer. Since v2
     it drops in under the header, clear of the preview's zoom row. */
  #toast {
    position: fixed; top: calc(76px + env(safe-area-inset-top, 0px)); left: 50%; transform: translateX(-50%); z-index: 80;
    display: flex; align-items: flex-start; gap: 10px;
    background: #17171f; color: #fff; padding: 11px 16px; border-radius: var(--r-lg);
    font-size: var(--fs-sm); font-weight: 500; line-height: 1.45; box-shadow: var(--shadow-3);
    max-width: min(560px, calc(100vw - 32px));
    animation: toastIn .25s var(--ease);
  }
  @keyframes toastIn { from { opacity: 0; transform: translate(-50%, -8px); } }
  #toast .tIcon {
    flex: none; width: 18px; height: 18px; margin-top: 0; border-radius: var(--r-full);
    display: flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 800; line-height: 1;
  }
  #toast.ok .tIcon { background: rgba(34, 197, 94, .25); color: #7ce8a8; }
  #toast.info .tIcon { background: rgba(129, 140, 248, .3); color: #c3c8ff; }
  #toast.err .tIcon { background: rgba(248, 113, 113, .28); color: #ffb4b4; }

  /* The page-boot veil (QoL-10): visible only under the head script's boot
     flags, above every other surface (#dlScreen 70, #toast 80), so an
     /account arrival — or, since QoL-10.1, a checkout/inbox-link return —
     shows app background, never home content, until its surface is ready.
     Anything that toasts must unveil first: at z-90 the veil would cover the
     toast itself. */
  #bootVeil { display: none; }
  .account-boot #bootVeil, .billing-boot #bootVeil { display: block; position: fixed; inset: 0; z-index: 90; background: var(--bg); }
  /* Only billing boots speak (QoL-10.1) — a silent veil over a paid moment is
     worse than the flash it hides. The /account veil stays silent: its window
     is a parse gap, not a network round trip. */
  #bootVeil p {
    display: none; position: absolute; left: 50%; top: 45%; transform: translate(-50%, -50%);
    color: var(--muted); font-size: var(--fs-md); white-space: nowrap;
    animation: skelPulse 1.2s ease-in-out infinite;
  }
  .billing-boot #bootVeil .vBuy { display: block; }
  .billing-boot.restore-boot #bootVeil .vBuy { display: none; }
  .billing-boot.restore-boot #bootVeil .vRestore { display: block; }
  @media (prefers-reduced-motion: reduce) { #bootVeil p { animation: none; } }

  /* ---------- The Download screen (S3.1, ADR-0051): one paid surface ---------- */
  #dlScreen { position: fixed; inset: 0; z-index: 70; background: var(--bg); overflow-y: auto; }
  #dlPage { max-width: 760px; margin: 0 auto; padding: 22px var(--sp-5) calc(48px + env(safe-area-inset-bottom, 0px)); display: flex; flex-direction: column; gap: var(--sp-4); }
  /* While the card sells the file on screen (the 402 route, the header's
     Download for a visitor the status read says may not download), the file
     and the card stand side by side: the price and the one action are in the
     first screen next to the thing they buy. Wide screens only, and only
     where :has() exists — everywhere else it is the single column it was. */
  @media (min-width: 900px) {
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) {
      max-width: 1120px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(360px, 420px);
      column-gap: 28px; row-gap: var(--sp-4); align-items: start;
    }
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > * { grid-column: 1; }
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlChrome,
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlTop,
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlConfirm,
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlAccount { grid-column: 1 / -1; }
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlSub { grid-column: 2; position: sticky; top: var(--sp-5); }
    /* At the point of purchase the next-image dropzone is an exit (M50's
       reasoning): the window-wide drop still takes a file, and Back to the
       preview has New image. The single column keeps it. */
    #dlPage:has(> #dlSub:not([hidden])):has(> #dlWork:not([hidden])) > #dlDrop { display: none; }
  }
  #dlTop { display: flex; justify-content: flex-start; }
  #dlBack { padding: 7px 14px; font-size: 13.5px; border-radius: var(--r-full); }
  #dlConfirm {
    background: var(--ok-weak); border: 1px solid var(--ok-line); color: var(--ok-deep);
    border-radius: var(--r-lg); padding: 12px 16px; font-size: 13.5px; font-weight: 600;
  }
  #dlWork { display: flex; flex-direction: column; }
  #dlHead {
    display: flex; gap: 16px; align-items: center;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-xl);
    padding: var(--sp-4) 18px; box-shadow: var(--shadow-1);
  }
  #dlThumb {
    width: 72px; height: 72px; object-fit: contain; border-radius: var(--r-md); flex: none;
    background: conic-gradient(#ececf1 25%, #fff 0 50%, #ececf1 0 75%, #fff 0) 0 0/16px 16px;
    border: 1px solid var(--line);
  }
  #dlHead h2 { font-size: 22px; letter-spacing: -.02em; font-weight: 750; }
  #dlMeta { color: var(--muted); font-size: var(--fs-sm); margin-top: 3px; line-height: 1.5; }
  #dlFormats { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .fmtCard {
    text-align: left; padding: 15px 16px; border-radius: var(--r-lg); display: flex; flex-direction: column;
    gap: 5px; position: relative; border: 1px solid var(--line); background: var(--card);
    cursor: pointer;
    transition: background var(--t-fast), border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
  }
  .fmtCard:hover:not(.off):not(.on) { border-color: var(--line-strong); box-shadow: var(--shadow-2); transform: translateY(-1px); }
  .fmtCard strong { font-size: var(--fs-md); font-weight: 700; letter-spacing: -.01em; }
  .fmtCard > span { color: var(--muted); font-size: var(--fs-xs); line-height: 1.45; }
  .fmtCard.on { border-color: var(--accent); background: var(--accent-weak); box-shadow: inset 0 0 0 1px var(--accent); }
  /* --muted on the accent-weak wash is 4.43:1 — a hair under AA (QoL-9). */
  .fmtCard.on > span { color: var(--text-2); }
  .fmtCard.on::after {
    content: ''; position: absolute; top: 12px; right: 12px; width: 18px; height: 18px; border-radius: var(--r-full);
    background: var(--accent) no-repeat center / 11px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  }
  .fmtCard.off { opacity: .5; cursor: not-allowed; }
  .fmtCard.off:hover { background: var(--card); border-color: var(--line); box-shadow: none; }
  #dlSizeRow { display: flex; align-items: center; gap: var(--sp-2); margin-top: var(--sp-3); font-size: var(--fs-sm); color: var(--muted); flex-wrap: wrap; }
  #dlSizeRow .lbl { font-weight: 650; color: var(--text); }
  #dlSizeRow input {
    width: 80px; padding: 7px 9px; font-size: 13px; font: inherit;
    border: 1px solid var(--field-line); border-radius: var(--r-sm);
    font-variant-numeric: tabular-nums;
  }
  #dlSizeRow #dlDpi { width: 60px; }
  #dlSizeRow select { padding: 6px 7px; font-size: 13px; }
  #dlScaleSeg { display: flex; gap: 6px; margin-top: 4px; }
  #dlScaleSeg button { padding: 4px 11px; font-size: var(--fs-xs); border-radius: var(--r-sm); }
  #dlScaleSeg button.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
  #dlRecap { display: flex; justify-content: space-between; align-items: center; gap: 10px; color: var(--muted); font-size: var(--fs-sm); }
  /* Two actions, one primary (S2.6): Download takes the width, Copy SVG sits
     beside it as a plain button; under 640 they stack, each full width. */
  #dlAction { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
  #dlDownload { flex: 1 1 240px; display: inline-flex; align-items: center; justify-content: center; gap: 9px; padding: 14px; font-size: var(--fs-md); border-radius: var(--r-lg); }
  #dlCopy { flex: 0 1 auto; padding: 14px 18px; font-size: var(--fs-md); border-radius: var(--r-lg); }
  #dlSub {
    position: relative; overflow: hidden;
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-xl);
    padding: 26px 28px 24px; box-shadow: var(--shadow-3);
    scroll-margin-top: var(--sp-4); /* the 402 route scrolls the card into view (S2.3) */
  }
  #dlSub::before {
    content: ''; position: absolute; left: 0; right: 0; top: 0; height: 4px;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
  }
  #dlSub h2 { font-size: 22px; line-height: 1.25; letter-spacing: -.02em; font-weight: 750; margin-bottom: 8px; }
  #dlSub .lede { color: var(--muted); font-size: 14px; line-height: 1.55; }
  #dlSub ul { list-style: none; margin: var(--sp-4) 0 0; display: grid; gap: 10px; }
  #dlSub li { font-size: 14px; line-height: 1.45; padding-left: 28px; position: relative; color: var(--text-2); }
  #dlSub li::before {
    content: ''; position: absolute; left: 0; top: 0; width: 19px; height: 19px; border-radius: var(--r-full);
    background: var(--accent-weak) no-repeat center / 11px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f46e5' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  }
  #dlNoImage {
    background: var(--accent-weak); border: 1px solid var(--accent-line); border-radius: var(--r-lg);
    padding: 15px 18px; font-size: 13.5px; color: #3b3480; line-height: 1.55;
  }
  #dlDrop {
    border: 2px dashed var(--line-strong); border-radius: var(--r-xl); padding: var(--sp-8); text-align: center;
    color: var(--muted); cursor: pointer; font-size: 14px; background: var(--card);
    transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
  }
  #dlDrop:hover { border-color: var(--accent); color: var(--text); background: #fbfbff; }
  #resumeChip {
    margin-top: var(--sp-3); display: inline-flex; align-items: center; gap: 7px;
    padding: 7px 14px; font-size: var(--fs-sm); font-weight: 600; border-radius: var(--r-full);
    color: var(--accent-strong); background: var(--accent-weak); border-color: var(--accent-line);
  }
  #resumeChip:hover:not(:disabled) { background: #e5e7ff; border-color: var(--accent); }
  #resumeChip::before { width: 15px; height: 15px; -webkit-mask-image: var(--i-resume); mask-image: var(--i-resume); }
  @media (max-width: 640px) {
    #dlFormats { grid-template-columns: 1fr; }
    #dlHead { flex-wrap: wrap; }
    #dlCopy { flex: 1 1 100%; }
  }
  #payPrice {
    display: flex; align-items: baseline; gap: 8px; margin: 20px 0 2px;
  }
  #payPrice .amount { font-size: 44px; font-weight: 800; letter-spacing: -.035em; line-height: 1; font-variant-numeric: tabular-nums; }
  #payPrice .per { color: var(--muted); font-size: 14px; font-weight: 500; }
  #payStatus {
    margin-top: 18px; background: var(--accent-weak); border: 1px solid var(--accent-line);
    border-radius: var(--r-md); padding: 11px 13px; font-size: var(--fs-sm); line-height: 1.5; color: #3b3480;
  }
  /* The card's third state (QoL-10): health hasn't answered yet, so neither
     the CTA nor the till-closed note — a skeleton where the answer will
     land. Sized to #payAction's footprint so the settle doesn't jump. */
  #payPending { margin-top: var(--sp-5); display: grid; gap: 8px; }
  #payPending .skel {
    height: 13px; border-radius: var(--r-sm); background: var(--bg-sunken);
    animation: skelPulse 1.2s ease-in-out infinite;
  }
  #payPending .skel-btn { height: 52px; width: 100%; max-width: 380px; border-radius: var(--r-lg); margin-bottom: 4px; }
  @keyframes skelPulse { 50% { opacity: .55; } }
  @media (prefers-reduced-motion: reduce) { #payPending .skel { animation: none; } }
  /* One primary action (S2.3, 2026-09-13): the CTA stands alone under the
     value and the price — full width on phones — and the trust line under it
     answers what is charged, how to cancel, the refund and "do I need an
     account": /terms' own promises, pinned to that page by test. */
  #payAction { margin-top: var(--sp-5); }
  #paySubscribe { width: 100%; max-width: 380px; padding: 15px 18px; font-size: var(--fs-lg); border-radius: var(--r-lg); }
  #payTrust { margin-top: 14px; color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; max-width: 560px; display: grid; gap: 6px; }
  #payTrust span { display: flex; align-items: flex-start; gap: 8px; }
  #payTrust span::before {
    content: ''; flex: none; width: 14px; height: 14px; margin-top: 1px; background: var(--ok);
    -webkit-mask: var(--i-check) center / contain no-repeat; mask: var(--i-check) center / contain no-repeat;
  }
  #payTrust span:first-child::before { background: var(--text-2); -webkit-mask-image: var(--i-lock); mask-image: var(--i-lock); }
  /* Existing subscribers, behind a disclosure: restore access and manage
     billing share the email field and the inbox round-trip (ADR-0037). */
  #payRestore { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--line); font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
  #payRestore summary { cursor: pointer; font-weight: 600; color: var(--text-2); width: fit-content; border-radius: var(--r-sm); }
  #payRestore summary:hover { color: var(--accent); }
  #payRestore .body { margin-top: var(--sp-3); }
  #payRestore label { display: block; font-size: var(--fs-xs); font-weight: 650; color: var(--text-2); margin-bottom: 5px; }
  #payRestore input {
    display: block; width: 100%; max-width: 380px; border: 1px solid var(--field-line); border-radius: var(--r-md);
    padding: 9px 11px; font: inherit; font-size: 13.5px; background: var(--card); color: var(--text);
  }
  #payRestore .row { display: flex; gap: var(--sp-2); margin-top: var(--sp-2); flex-wrap: wrap; }
  #payRestore .hint { margin-top: var(--sp-2); font-size: var(--fs-xs); }
  @media (max-width: 640px) {
    #paySubscribe, #payPending .skel-btn, #payRestore input { max-width: none; }
    #payRestore .row button { flex: 1 1 140px; }
  }
  /* The /account page's own chrome (S2.4 §3): on a page boot the real header
     sits inert under the overlay, so the screen carries a brand row — the
     header's .brand (chrome block above) and the site links — shown only
     then; every in-app open of the screen hides it. */
  #dlChrome {
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
    flex-wrap: wrap; padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line);
  }
  #dlChrome nav { display: flex; align-items: center; gap: 2px; }
  #dlChrome nav a {
    color: var(--text-2); text-decoration: none; font-weight: 550; font-size: 14px;
    padding: 8px 12px; border-radius: var(--r-md);
    transition: background var(--t-fast), color var(--t-fast);
  }
  #dlChrome nav a:hover { color: var(--text); background: var(--bg-sunken); }
  /* The account panel (S2.4, ADR-0058): the heading, then the pending
     skeleton until the status read and health have both answered, then the
     state the server named — never a guess. The subscriber's lines, the
     failed-payment lead (warn ink, Manage billing promoted to the one
     action), or the ended plan's date above the card. */
  #dlAccount {
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-xl);
    padding: var(--sp-6) 28px; box-shadow: var(--shadow-1);
  }
  #dlAccount h2 { font-size: 24px; letter-spacing: -.02em; font-weight: 750; }
  #acctPending { display: grid; gap: 8px; margin-top: var(--sp-3); }
  #acctPending .skel {
    height: 13px; border-radius: var(--r-sm); background: var(--bg-sunken);
    animation: skelPulse 1.2s ease-in-out infinite;
  }
  @media (prefers-reduced-motion: reduce) { #acctPending .skel { animation: none; } }
  #acctError {
    display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-3);
    color: var(--text-2); font-size: 13.5px; line-height: 1.5;
  }
  #acctStatus { margin-top: var(--sp-2); font-size: 14px; line-height: 1.55; }
  #acctWho { color: var(--text-2); }
  #acctPlan { margin-top: 6px; font-weight: 650; }
  #acctPlan.due { color: var(--warn); }
  #acctNote { margin-top: 6px; color: var(--muted); }
  #acctActions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-4); }
  #acctManageHint { color: var(--muted); font-size: var(--fs-xs); line-height: 1.5; max-width: 440px; }
  @media (max-width: 640px) {
    #dlAccount { padding: var(--sp-5) var(--sp-4); }
    #acctManage { width: 100%; }
  }
  #fixChip {
    position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%);
    display: flex; align-items: center; gap: 8px; max-width: calc(100% - 24px);
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
    padding: 7px 8px 7px 13px; font-size: var(--fs-sm); white-space: nowrap;
    box-shadow: var(--shadow-3);
  }
  #fixChip::before { content: ''; width: 8px; height: 8px; border-radius: var(--r-full); background: var(--warn); box-shadow: 0 0 0 3px var(--warn-weak); flex: none; }
  #fixChip span { color: var(--text); overflow: hidden; text-overflow: ellipsis; font-weight: 550; }

  /* ---------- Small screens (QoL-9, B-010): the payoff leads ---------- */
  /* ≤980 the frame stacks and the STAGE comes first — the DOM itself reorders
     (placeSidebar() moves the aside below the stage) so keyboard tab order
     matches what the eye sees, not just the CSS. One page scroll, no nested
     scroll areas: on touch, an inner overflow pane is where flings go to die. */
  @media (max-width: 980px) {
    #shell { height: auto; min-height: 100dvh; }
    main { padding: var(--sp-2); }
    #appFrame { flex-direction: column; border-radius: var(--r-xl); }
    aside {
      width: auto; flex: 0 0 auto; overflow: visible;
      border-right: none; border-top: 1px solid var(--line);
    }
    /* The visual order is fixed here, not only by placeSidebar()'s DOM move:
       app.js is deferred, so the first frame can paint the aside above the
       stage and the move then reads as a layout shift — R3 measured it as
       a 0.28 CLS on #stage, in 2 of 3 runs. With the stage first from the
       first paint the DOM move (kept, for tab order) changes nothing on
       screen (S2.1b). */
    #stage { order: 1; }
    aside { order: 2; }
    .canvas { min-height: 300px; }
  }
  /* Tablet (~601–980): both panes stay up; the control sheet reads as cards,
     two abreast. */
  @media (min-width: 601px) and (max-width: 980px) {
    #traceControls { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); align-items: start; }
    #traceControls details.group {
      border: 1px solid var(--line); border-radius: var(--r-md);
      padding: 10px 12px;
    }
  }
  /* Phone (≤600): ONE pane at a time behind the Original/Preview toggle, the
     canvas capped so head + picture + readout + zoom row all land inside the
     first viewport — that cap is what closes B-010's "preview in-viewport
     with zero scrolling" bar at 375×812. */
  @media (max-width: 600px) {
    #stage { padding: var(--sp-3); gap: var(--sp-3); }
    #paneTabs:not([hidden]) { display: flex; }
    #panes { grid-template-columns: 1fr; }
    #panes.show-orig .pane.result { display: none; }
    #panes:not(.show-orig) .pane:not(.result) { display: none; }
    /* flex-basis auto, or the pane's flex column sizes the canvas from its
       220px min and ignores the height — the clamp is the whole point here. */
    .canvas { flex: 0 0 auto; min-height: 0; height: clamp(220px, 46vh, 560px); height: clamp(220px, 46svh, 560px); }
    /* The phone's drop zone (S2.5): the phone copy and the camera control
       show, the desktop copy goes, and the zone tightens — icon, buttons —
       so the whole target sits inside the first screen under a landing hero
       (measured at 375×812 in the roadmap log). The two buttons are two
       full-width rows, not a pair (Michael, 2026-09-25) — the row they cost
       is paid back by the target's frame and the buttons' height. */
    #empty { gap: var(--sp-3); }
    .dropTarget { padding: var(--sp-4) var(--sp-3); }
    #empty .onDesk { display: none; }
    #empty .onPhone { display: inline; }
    #empty button.onPhone { display: inline-flex; }
    #empty h2 { font-size: 22px; }
    .dropIcon { width: 46px; height: 46px; margin-bottom: var(--sp-3); }
    .dropIcon svg { width: 22px; height: 22px; }
    #empty .dropSub { margin-top: 10px; }
    #resumeChip { margin-top: 10px; padding: 5px 12px; }
    .pickRow { flex-direction: column; margin-top: 12px; gap: 8px; width: 100%; max-width: 360px; margin-inline: auto; }
    .pickRow button { flex: none; width: 100%; min-height: 46px; padding: 11px 14px; gap: 8px; font-size: var(--fs-md); white-space: nowrap; }
    .pickRow button.primary { min-width: 0; }
    .paneFoot, .paneFoot .stats { gap: var(--sp-2) var(--sp-4); }
    .paneFoot { padding: 9px 12px 10px; }
    /* One CTA per screen (S2.6): the sticky header reads "Download" on a
       phone, so the readout's own button does not show here. */
    .resultCta { display: none; }
    #bottomBar { flex-wrap: wrap; row-gap: var(--sp-2); }
    #bottomBar .seg { width: auto; }
    #zoomBar { order: -1; flex: 1 1 100%; flex-wrap: wrap; }
    #bottomBar > .row { margin-left: auto; }
    header .brand .sub { display: none; }
    #downloadBtn .btnTxt { display: none; }
    .actions .btnTxtShort { display: inline; }
    .actions { gap: 6px; }
    .actions button { padding: 8px 14px; }
    /* A phone's toast is a snackbar: bottom of the screen, clear of the
       pane toggle and the header's Download. */
    #toast { top: auto; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); animation-name: toastUp; }
    @keyframes toastUp { from { opacity: 0; transform: translate(-50%, 8px); } }
  }
  /* Touch (any width): the named targets — zoom segment, layer eyes, merge
     menus — reach 44px, sliders get a thumb a finger can land on, and text
     fields hold 16px so iOS Safari stops zooming the page into them. */
  @media (pointer: coarse) {
    .actions button { min-height: 44px; }
    .seg button { min-height: 44px; min-width: 44px; }
    input[type=range] { height: 44px; }
    input[type=range]::-webkit-slider-thumb { width: 22px; height: 22px; margin-top: -9px; }
    input[type=range]::-moz-range-thumb { width: 18px; height: 18px; }
    input[type=checkbox] { width: 20px; height: 20px; }
    .layer-row { min-height: 48px; }
    .layer-row .eye { width: 44px; height: 44px; }
    .layer-row input[type=color] { width: 38px; height: 32px; }
    /* 16px in every text-entry and select, or iOS Safari zooms the page into
       the field on focus — selectors match the base rules' specificity. */
    select, .layer-row select { min-height: 44px; font-size: 16px; }
    .layer-row select { max-width: 108px; }
    #payRestore input, #dlSizeRow input { font-size: 16px; min-height: 42px; }
    .why { width: 22px; height: 22px; font-size: var(--fs-xs); }
    .why::after { inset: -11px; }
    button.small { min-height: 36px; }
    #navToggle { height: 40px; }
  }
  /* The pane toggle strip (phone only; hidden alongside #panes). */
  #paneTabs { display: none; flex: 0 0 auto; }
  #paneTabs .seg { flex: 1; }
  /* Announcements for assistive tech (QoL-9): visually hidden, never display:
     none — a live region must stay in the accessibility tree to speak. */
  .srOnly {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  /* App-page extension, not a chrome-block edit: the home indicator's inset. */
  footer#siteFooter { padding-bottom: calc(40px + env(safe-area-inset-bottom, 0px)); }

  /* ---------- App shell ---------- */
  /* The app keeps the whole viewport: #shell is the 100dvh app frame and the
     footer sits below the fold, reached by scroll — the preview pane never
     shrinks for chrome. */
  #shell { height: 100vh; height: 100dvh; display: flex; flex-direction: column; flex: 0 0 auto; }
  /* The mount div is a marker, not a box (ADR-0054): it pins the static
     skeleton and tells app.js the page carries the converter; display:
     contents keeps #appFrame a direct flex child of main exactly as before. */
  #unpixel-app { display: contents; }

  /* ---------- Before the first image: the upload card (visual system v2) ---------- */
  /* app.js sets body.has-image the moment an image is in (setWorkspaceState)
     and drops it on New image; the first paint — and a page whose script
     never ran — is this state. The frame is a centred card sized by its
     content, the drop zone and nothing else: the sidebar holds only the
     control stack, which means nothing before an image (QoL-8), so it is
     not drawn at all (M55 — the steps' strip it carried left the card).
     800 px wide (Michael, 2026-09-25: at 1040 the target was a strip,
     3.5 : 1 — now 2.6 : 1, a box). */
  body:not(.has-image) #appFrame { flex-direction: column; max-width: 800px; }
  body:not(.has-image) #stage { order: 1; background: var(--card); padding: var(--sp-5); }
  body:not(.has-image) aside { display: none; }
  /* Phones keep the tight card: the 12 px inset and the zone's own phone
     rhythm (above) are what S2.5's 812 px bar was measured on. */
  @media (max-width: 600px) {
    body:not(.has-image) #stage { padding: var(--sp-3) var(--sp-3) var(--sp-4); }
  }

  /* ---------- Embedded converter pages (S2.1b, ADR-0057) ---------- */
  /* A landing page carries the converter mid-document: the same skeleton,
     this same stylesheet, and this block — everything a page needs to host
     #appFrame below a hero and above its own copy. Hooked on <body class="lp">
     (since S2.5 the home page too, as lp lp-home). The element rules above
     (main, button, aside, select, the inputs) are lived with rather than
     scoped: a landing page writes no <aside>, <select> or range input of its
     own, and every class it introduces is lp-*. */
  body.lp { height: auto; min-height: 100%; }
  /* body's background is painted on the canvas and sized by the root's box —
     the viewport while html is 100% tall — so the fade below tiled every
     screen height and the grid came back in bands down a long page (the
     landing pages since S2.7; the home page's sections showed it, M57). A
     page carrying the converter is long, so the root takes the document's
     height and the fade is drawn once, under the hero. */
  html:has(> body.lp) { height: auto; }
  /* The page behind the hero: an indigo glow and the pixel grid the product
     undoes, both fading out into the mat. One background, so no decorative
     box can widen the page (a phone's layout viewport grows to fit any
     overflow, and the sticky header goes with it). */
  body.lp {
    background:
      radial-gradient(1000px 560px at 50% 0, rgba(246, 246, 249, 0) 20%, var(--bg) 76%),
      linear-gradient(rgba(79, 70, 229, .06) 1px, transparent 1px) center top / 28px 28px,
      linear-gradient(90deg, rgba(79, 70, 229, .06) 1px, transparent 1px) center top / 28px 28px,
      radial-gradient(1100px 520px at 50% -200px, rgba(99, 102, 241, .17), rgba(99, 102, 241, 0) 72%) no-repeat,
      var(--bg);
  }
  .lp #shell { height: auto; min-height: 0; }
  .lp main { display: block; padding: 0; background: none; min-height: 0; flex: 1 0 auto; }
  .lp-app { padding: 0 var(--sp-5); }
  /* With an image in, the frame is the workspace: the viewport under the
     sticky header, which is where handleFile() scrolls it (ADR-0057 §5) — the
     sidebar and the panes scroll inside it, the page does not. */
  .lp #appFrame { height: clamp(560px, calc(100vh - 96px), 980px); scroll-margin-top: 80px; }
  .lp-home #appFrame { height: clamp(560px, calc(100vh - 96px), 980px); }
  /* Before an image exists the frame is content-sized (S2.5, v2): the card is
     as tall as the drop zone and its strip, so nothing can overflow it and
     the hero above keeps its line. */
  .lp:not(.has-image) #appFrame { height: auto; }
  .lp-hero { max-width: 900px; margin: 0 auto; padding: 36px var(--sp-5) 26px; text-align: center; }
  .lp-kicker {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: var(--fs-sm); font-weight: 700; letter-spacing: .01em; color: var(--accent-strong);
    background: var(--card); border: 1px solid var(--accent-line); border-radius: var(--r-full);
    padding: 5px 13px; margin-bottom: 18px; box-shadow: var(--shadow-1);
  }
  .lp-hero h1 {
    font-size: clamp(32px, 3.6vw, 48px); line-height: 1.08; letter-spacing: -.035em; font-weight: 800;
    color: var(--text); text-wrap: balance;
  }
  .lp-accent {
    background: linear-gradient(95deg, var(--accent) 10%, var(--accent-2) 90%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
  .lp-hero h1 .lp-accent { display: block; }
  .lp-hero p { margin: 14px auto 0; max-width: 660px; font-size: var(--fs-lg); line-height: 1.6; color: var(--text-2); text-wrap: pretty; }
  /* The home page's hero (S2.5, M51 → (a)): a line above the app, not a page —
     the h1 and one sentence, the upload card under it inside the first
     screen at 1280×720. */
  .lp-home .lp-hero { padding: 32px var(--sp-5) 24px; }
  /* The home page's first screen ended with the footer where the screen
     could hold it (Michael, 2026-09-25: "bottom of the people's screen" —
     and the same four-column footer as every other page, not a bar); since
     M57 (2026-09-26) sections follow the before/after and the footer
     follows them. #shell — header and main; the footer is its sibling —
     still takes any slack, which only a screen taller than the whole page
     would leave. */
  .lp-home #shell { flex: 1 0 auto; }
  .lp-home .lp-app { padding-bottom: var(--sp-6); }
  /* The first screen is 64 header · 228 hero · 331 card · 20 · 271
     before/after = 914 px at the numbers above, and since M57 sections follow
     it: the first one's head — its eyebrow and h2 — ends at 1049, so under
     1049 px of viewport the first screen tightens — the hero's and the
     target's frames, the rhythm's shape kept (icon · 12 · heading, 20,
     button · 12 · hint), the before/after's frame at 640 px — to
     64 · 204 · 295 · 16 · 243 = 822, the head ending at 957: a 982 px screen
     (a 14-inch laptop) shows the before/after and "How it works" whole, at
     900 the head's eyebrow meets the fold, and a 13-inch laptop's ~790 cuts
     the before/after's caption — what is left to cut there is content, the
     h1's size, the before/after. (Until M57 the footer ended the first
     screen and set these numbers: M52, M56.) */
  @media (min-width: 981px) and (max-height: 1048px) {
    .lp-home .lp-hero { padding: 20px var(--sp-5) 16px; }
    .lp-home .lp-hero p { margin-top: 10px; }
    body.lp-home:not(.has-image) #stage { padding: var(--sp-4); }
    .lp-home #empty { gap: var(--sp-4); }
    .lp-home .dropTarget { padding: 28px var(--sp-6); }
    .lp-home .pickRow { margin-top: var(--sp-5); }
    .lp-home .lp-demo { max-width: 640px; margin-top: var(--sp-4); }
  }
  /* The before/after under the home page's card (M55, 2026-09-26; M56 the
     same day): one wordmark zoomed in, whole — the PNG's own pixels, enlarged
     by the browser with nothing smoothed, and over them the SVG the engine
     traced from it with its anchor points drawn, clipped at the divider. The
     stage is the picture's box (17 : 6, the crop's own shape) and the frame's
     padding its side margins — the W starts five source pixels after the H,
     so the air cannot come from the crop. --split is the divider's place in
     the stage: app.js moves it with a drag or a tap, and the visually hidden
     range input with the arrow keys. Before an image only, like the card;
     the page's scroll stays the page's (touch-action: pan-y), so only a
     sideways drag moves it. */
  .lp-demo { position: relative; width: 90%; max-width: 720px; margin: var(--sp-5) auto 0; }
  body.has-image .lp-demo { display: none; }
  .lp-demo-frame {
    --split: 50%;
    position: relative; padding: 0 20px; overflow: hidden;
    background: #fff; border: 1px solid var(--line); border-radius: var(--r-xl);
    box-shadow: var(--shadow-2); cursor: ew-resize; touch-action: pan-y;
    -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent;
  }
  .lp-demo-stage { position: relative; aspect-ratio: 17 / 6; }
  .lp-demo-stage img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    pointer-events: none; -webkit-user-drag: none;
  }
  .lp-demo-png { image-rendering: -webkit-optimize-contrast; image-rendering: pixelated; }
  .lp-demo-svg { clip-path: inset(0 0 0 var(--split)); }
  .lp-demo-handle {
    position: absolute; top: 0; bottom: 0; left: var(--split); width: 2px; margin-left: -1px;
    background: var(--accent); pointer-events: none;
  }
  .lp-demo-handle::after {
    content: ''; position: absolute; top: 50%; left: 50%; width: 34px; height: 34px;
    transform: translate(-50%, -50%); border-radius: var(--r-full);
    border: 2px solid var(--accent); box-shadow: var(--shadow-2);
    background: #fff no-repeat center / 18px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f46e5' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 7l-5 5 5 5M15 7l5 5-5 5'/%3E%3C/svg%3E");
  }
  .lp-demo-range {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; opacity: 0;
  }
  .lp-demo-range:focus-visible + .lp-demo-handle::after { box-shadow: 0 0 0 4px var(--accent-line), var(--shadow-2); }
  /* The legend: each half's name in its top corner, the line under the
     frame. The names sit in the figcaption and are lifted into the frame
     (.lp-demo is their box), clear of the compass mark down to a 600 px
     stage; a phone's stage is too short for that, and there they stay in
     the caption's row, under their halves. */
  .lp-demo figcaption {
    display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
    margin-top: 10px; font-size: var(--fs-sm); color: var(--muted);
  }
  .lp-demo-cap { text-align: center; text-wrap: balance; }
  .lp-demo-tag {
    position: absolute; top: 12px; left: 12px; z-index: 1; pointer-events: none;
    flex: none; padding: 4px 9px; border-radius: var(--r-full);
    font-size: var(--fs-2xs); font-weight: 750; letter-spacing: .08em; line-height: 1;
    color: var(--text-2); background: var(--card); border: 1px solid var(--line-strong);
  }
  .lp-demo-tag-svg { left: auto; right: 12px; color: var(--accent-strong); background: var(--accent-weak); border-color: var(--accent-line); }
  /* A phone's frame is the card's width, the whole word at a little under
     its own size — the anchor points shrink with it and the pixels still
     show at a phone's density. Wider, it is nine tenths of the card: an
     exhibit under it, never a second card beside it. */
  @media (max-width: 600px) {
    .lp-demo { width: auto; margin-top: var(--sp-4); }
    .lp-demo-frame { padding: 0 12px; border-radius: var(--r-lg); }
    .lp-demo figcaption { justify-content: space-between; font-size: var(--fs-xs); }
    .lp-demo-tag { position: static; }
  }
  .lp-hero a, .lp-body a { color: var(--accent); }
  .lp-body { max-width: 1060px; margin: 0 auto; padding: 72px var(--sp-5) 80px; }
  .lp-section { padding: 48px 0; border-top: 1px solid var(--line); }
  .lp-section:first-child { border-top: none; padding-top: 0; }
  .lp-section h2 { font-size: clamp(24px, 2.6vw, 31px); line-height: 1.2; letter-spacing: -.025em; font-weight: 800; margin-bottom: 14px; max-width: 780px; text-wrap: balance; }
  .lp-section p { font-size: 16px; line-height: 1.7; color: var(--text-2); margin-bottom: 14px; max-width: 780px; }
  .lp-section p:last-child { margin-bottom: 0; }
  .lp-fine { color: var(--muted) !important; font-size: var(--fs-sm) !important; line-height: 1.55 !important; }
  .lp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 26px; }
  .lp-offer { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 26px; align-items: start; }
  .lp-card {
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-xl);
    padding: 22px 22px 20px; box-shadow: var(--shadow-1);
  }
  .lp-card h3 { font-size: 16.5px; font-weight: 700; margin-bottom: 8px; letter-spacing: -.015em; }
  .lp-card p { font-size: 14.5px; color: var(--text-2); margin: 0; line-height: 1.6; }
  .lp-card .lp-fine { margin-top: 12px; }
  /* The plan's card beside the free one: the one the offer is about. */
  .lp-offer .lp-card:last-child { border-color: var(--accent-line); box-shadow: 0 0 0 4px var(--accent-weak), var(--shadow-2); }
  .lp-facts { list-style: none; display: grid; gap: 10px; margin: 0; }
  .lp-facts li { position: relative; padding-left: 28px; font-size: 15px; line-height: 1.55; color: var(--text-2); }
  .lp-card .lp-facts li { font-size: 14px; }
  .lp-facts li::before {
    content: ''; position: absolute; left: 0; top: 1px; width: 19px; height: 19px; border-radius: var(--r-full);
    background: var(--accent-weak) no-repeat center / 11px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234f46e5' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  }
  .lp-facts strong, .lp-card strong { color: var(--text); font-weight: 650; }
  .lp-card .lp-price { font-size: 34px; font-weight: 800; letter-spacing: -.03em; line-height: 1.1; font-variant-numeric: tabular-nums; margin: 2px 0 16px; color: var(--text); }
  .lp-price .lp-per { font-size: 14px; font-weight: 500; color: var(--muted); letter-spacing: 0; margin-left: 4px; }
  .lp-faq { margin-top: 8px; }
  .lp-faq details { border-bottom: 1px solid var(--line); }
  .lp-faq details:first-child { border-top: 1px solid var(--line); }
  .lp-faq summary {
    list-style: none; cursor: pointer; display: flex; align-items: center; gap: 14px;
    padding: 17px 2px; user-select: none; -webkit-user-select: none;
  }
  .lp-faq summary::-webkit-details-marker { display: none; }
  .lp-faq summary h3 { font-size: 16.5px; font-weight: 650; letter-spacing: -.01em; flex: 1; }
  .lp-faq summary::after {
    content: ''; width: 24px; height: 24px; flex: none; border-radius: var(--r-full);
    background: var(--bg-sunken) no-repeat center / 12px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233e3f51' stroke-width='2.6' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
    transition: transform var(--t-med) var(--ease), background-color var(--t-fast);
  }
  .lp-faq details[open] summary::after { transform: rotate(45deg); }
  .lp-faq summary:hover h3 { color: var(--accent); }
  .lp-faq summary:hover::after { background-color: var(--accent-weak); }
  .lp-answer { padding: 0 44px 18px 2px; }
  .lp-answer p { font-size: 15.5px; line-height: 1.7; color: var(--text-2); margin-bottom: 10px; }
  .lp-answer p:last-child { margin-bottom: 0; }
  /* The closer: the one dark-accent block on the page, the last thing read. */
  .lp-closer {
    margin-top: 56px; padding: 48px 28px; text-align: center; color: #fff;
    border-radius: var(--r-2xl); position: relative; overflow: hidden; isolation: isolate;
    background: radial-gradient(80% 120% at 50% 0%, rgba(255, 255, 255, .16), rgba(255, 255, 255, 0) 60%), linear-gradient(135deg, #4f46e5, #6d28d9);
    box-shadow: 0 30px 60px -24px rgba(79, 70, 229, .55);
  }
  .lp-closer h2 { font-size: clamp(24px, 2.8vw, 32px); font-weight: 800; letter-spacing: -.025em; margin-bottom: 8px; color: #fff; }
  .lp-closer p { color: rgba(255, 255, 255, .86); font-size: 16px; }
  .lp-cta-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 22px; }
  .lp-btn {
    display: inline-flex; align-items: center; gap: 8px; text-decoration: none;
    font-weight: 650; font-size: 15px; padding: 12px 22px; border-radius: var(--r-lg);
    border: 1px solid var(--line-strong); background: var(--card); color: var(--text) !important;
    transition: background var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
  }
  .lp-btn:hover { background: #f8f8fb; transform: translateY(-1px); box-shadow: var(--shadow-1); }
  .lp-btn.lp-primary {
    background: var(--accent); border-color: var(--accent);
    color: var(--on-accent) !important; box-shadow: var(--shadow-accent);
  }
  .lp-btn.lp-primary:hover { background: var(--accent-strong); }
  .lp-closer .lp-btn.lp-primary { background: #fff; border-color: #fff; color: var(--accent-strong) !important; box-shadow: 0 8px 20px -8px rgba(14, 15, 26, .45); }
  .lp-closer .lp-btn.lp-primary:hover { background: #f1f1ff; }
  .lp-closer .lp-btn:not(.lp-primary) { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .45); color: #fff !important; }
  .lp-closer .lp-btn:not(.lp-primary):hover { background: rgba(255, 255, 255, .16); }
  .lp-links { margin-top: 20px; font-size: 14px; color: var(--muted); }
  .lp-closer .lp-links { color: rgba(255, 255, 255, .78); }
  .lp-body .lp-closer a:not(.lp-btn) { color: #fff; }
  .lp-list { padding-left: 22px; display: grid; gap: 10px; margin: 0; max-width: 780px; }
  .lp-list li { font-size: 16px; line-height: 1.65; color: var(--text-2); }
  .lp-list li::marker { color: var(--accent); }
  .lp-list strong { color: var(--text); }
  .lp-grid.lp-4 { grid-template-columns: repeat(4, 1fr); }
  /* The home page's sections under the first screen (M57, 2026-09-26): the
     landing pages' lp-body with a centred head over each section — an eyebrow,
     the h2, one line — the three steps in a numbered row, what the file is in
     a two-by-two of icon cards, the FAQ in the reading column, the closer.
     Glyphs are the app's masks (--i-* on body); no images, so nothing here
     joins the first screen's requests. */
  .lp-home .lp-body { padding-top: 48px; }
  .lp-home .lp-section { border-top: none; padding: 40px 0; }
  .lp-home .lp-section:first-child { padding-top: 0; }
  .lp-head { max-width: 720px; margin: 0 auto 26px; text-align: center; }
  .lp-head h2, .lp-head p { margin-inline: auto; }
  .lp-head p { margin-bottom: 0; text-wrap: balance; }
  .lp-eyebrow {
    display: block; margin-bottom: 10px; font-size: var(--fs-2xs); font-weight: 750;
    letter-spacing: .12em; text-transform: uppercase; color: var(--accent);
  }
  .lp-ico {
    --ico: var(--i-upload);
    position: relative; display: grid; place-items: center; flex: none;
    width: 44px; height: 44px; border-radius: 12px;
    color: var(--accent); background: var(--accent-weak); box-shadow: inset 0 0 0 1px var(--accent-line);
  }
  .lp-ico::before {
    content: ''; width: 22px; height: 22px; background: currentColor;
    -webkit-mask: var(--ico) center / contain no-repeat; mask: var(--ico) center / contain no-repeat;
  }
  .lp-ico-eye { --ico: var(--i-eye); }
  .lp-ico-download { --ico: var(--i-download); }
  .lp-ico-curve { --ico: var(--i-curve); }
  .lp-ico-layers { --ico: var(--i-layers); }
  .lp-ico-file { --ico: var(--i-file); }
  .lp-ico-lock { --ico: var(--i-lock); }
  .lp-steps {
    list-style: none; margin: 0; padding: 0; counter-reset: lp-step;
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
  }
  .lp-steps > li { position: relative; counter-increment: lp-step; }
  .lp-steps .lp-ico { margin-bottom: 16px; }
  /* The step's number rides on its glyph; the list itself says the order. */
  .lp-steps .lp-ico::after {
    content: counter(lp-step); position: absolute; top: -7px; right: -7px;
    display: grid; place-items: center; width: 20px; height: 20px; border-radius: var(--r-full);
    font-size: 11px; font-weight: 800; line-height: 1; color: var(--on-accent);
    background: var(--accent); box-shadow: 0 0 0 2px var(--card);
  }
  .lp-feats { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .lp-feats .lp-card { display: flex; gap: 16px; align-items: flex-start; }
  .lp-more { margin-top: 22px; text-align: center; font-size: 15px; font-weight: 650; }
  .lp-more a { color: var(--accent); text-decoration: none; }
  .lp-more a::after { content: ' →'; }
  .lp-more a:hover { text-decoration: underline; }
  .lp-home .lp-faq { max-width: 780px; margin: 0 auto; }
  .lp-home .lp-closer { margin-top: 32px; }
  @media (max-width: 980px) {
    .lp #appFrame { height: auto; min-height: 62vh; }
    .lp:not(.has-image) #appFrame { min-height: 0; }
    .lp-grid.lp-4 { grid-template-columns: 1fr 1fr; }
  }
  @media (max-width: 820px) {
    .lp-grid, .lp-offer, .lp-steps, .lp-feats { grid-template-columns: 1fr; }
    .lp-home .lp-section { padding: 32px 0; }
  }
  @media (max-width: 600px) {
    .lp-grid.lp-4 { grid-template-columns: 1fr; }
  }
  @media (max-width: 600px) {
    .lp-hero { padding: 18px var(--sp-4) 14px; }
    .lp-home .lp-hero { padding: 18px var(--sp-4) 14px; }
    .lp-kicker { margin-bottom: 8px; font-size: var(--fs-xs); padding: 3px 10px; }
    .lp-hero h1 { font-size: 27px; line-height: 1.1; }
    .lp-hero p { font-size: 14.5px; line-height: 1.55; margin-top: 8px; }
    .lp-app { padding: 0 var(--sp-2); }
    .lp #appFrame { scroll-margin-top: 66px; }
    .lp-body { padding: 44px var(--sp-4) 48px; }
    .lp-section { padding: 36px 0; }
    .lp-section h2 { font-size: 23px; }
    .lp-section p, .lp-list li { font-size: 15.5px; }
    .lp-closer { padding: 36px 20px; margin-top: 40px; }
    /* A phone gives the cards' copy the full width: the glyph over the title,
       as the steps have it. */
    .lp-feats .lp-card { display: block; }
    .lp-feats .lp-ico { margin-bottom: 14px; }
    .lp-home .lp-body { padding-top: 32px; }
  }
