/* ============================================================================
   ARIA — design system

   Near-black and pure white. One accent. The contrast does the work, so the
   page stays quiet until something matters — then it is unmissable.

   Type: Geist for interface and display, JetBrains Mono for anything the
   machine produced (keys, IDs, timings, confidence). Weight and size carry
   hierarchy; colour is reserved for meaning.
   ========================================================================= */

:root {
  --black:     #08090a;
  --near:      #0d0f11;
  --panel:     #121417;
  --panel-2:   #171a1e;
  --line:      #1e2226;
  --line-2:    #2a2f36;

  --white:     #ffffff;
  --text:      #f4f5f6;
  --dim:       #9ba1a8;
  --mute:      #6b7280;
  --faint:     #454b53;

  --accent:    #3b82f6;
  --accent-hi: #60a5fa;
  --scan:      #22d3ee;
  --ok:        #34d399;
  --warn:      #fbbf24;
  --bad:       #f87171;

  --wrap: 1400px;
  --r:    10px;
  --r-lg: 16px;

  --ui:   "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace;

  --ease: cubic-bezier(.16,1,.3,1);
  --fast: cubic-bezier(.4,0,.2,1);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--black);
  color: var(--text);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.6;
  letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

::selection { background: var(--white); color: var(--black); }

/* ============================================================================
   SECTION INVERSION

   The page alternates between dark and light bands. Every component reads its
   colours from these four variables, so a section flips its entire palette by
   setting `class="light"` — no component needs a light-mode variant of its own.
   ========================================================================= */

body, .dark {
  --bg:      var(--black);
  --fg:      var(--white);
  --fg-2:    var(--text);
  --fg-dim:  var(--dim);
  --fg-mute: var(--mute);
  --fg-faint:var(--faint);
  --edge:    var(--line);
  --edge-2:  var(--line-2);
  --raise:   var(--near);
  --raise-2: var(--panel);
}

.light {
  --bg:      #ffffff;
  --fg:      #08090a;
  --fg-2:    #16181b;
  --fg-dim:  #4a5157;
  --fg-mute: #6b7280;
  --fg-faint:#9aa1a9;
  --edge:    #e6e8eb;
  --edge-2:  #d3d7dc;
  --raise:   #fafbfc;
  --raise-2: #f3f5f7;
  background: var(--bg);
  color: var(--fg-2);
}
.light ::selection { background: var(--black); color: var(--white); }

/* A soft off-white band, for when two light sections would otherwise collide */
.light.tint { --bg: #f7f8f9; --raise: #ffffff; --raise-2: #eef0f2; }

section.light, div.light { background: var(--bg); color: var(--fg-2); }

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 40px; }
/* Long-form reading (legal pages, prose sections) stays narrow on purpose —
   a 1400px line length is unreadable. Only layout uses the full width. */
.narrow { max-width: 820px; }

a { color: inherit; text-decoration: none; }

h1,h2,h3,h4 { margin: 0 0 16px; font-weight: 600; line-height: 1.05;
              letter-spacing: -.035em; color: var(--fg); }
h1 { font-size: clamp(2.6rem, 4.4vw, 4.4rem); font-weight: 600;
     letter-spacing: -.045em; }
h2 { font-size: clamp(2.1rem, 3.4vw, 3.4rem); letter-spacing: -.04em; }
h3 { font-size: 1.16rem; letter-spacing: -.022em; line-height: 1.35; }
p  { margin: 0 0 18px; }

.lead { font-size: clamp(1.08rem, 1.3vw, 1.3rem); color: var(--fg-dim);
        max-width: 60ch; line-height: 1.62; letter-spacing: -.014em; }
.center .lead { margin-left: auto; margin-right: auto; }

/* machine voice — anything the software generated */
.eyebrow {
  font-family: var(--mono); font-size: .69rem; font-weight: 500;
  letter-spacing: .18em; text-transform: uppercase; color: var(--fg-mute);
  display: inline-flex; align-items: center; gap: 10px; margin-bottom: 22px;
}
.eyebrow::before { content: ""; width: 5px; height: 5px; border-radius: 50%;
                   background: var(--scan); flex: none;
                   box-shadow: 0 0 10px var(--scan); }
.eyebrow.plain::before { display: none; }

/* ------------------------------------------------------------------ header */

/* The header is always dark: it is the constant frame the page scrolls under,
   and a header that flipped colour mid-scroll would read as a glitch. */
header.site {
  position: sticky; top: 0; z-index: 90;
  background: rgba(8,9,10,.78);
  backdrop-filter: blur(20px) saturate(1.6);
  border-bottom: 1px solid var(--line);
  --bg: var(--black); --fg: var(--white); --fg-2: var(--text);
  --fg-dim: var(--dim); --fg-mute: var(--mute); --fg-faint: var(--faint);
  --edge: var(--line); --edge-2: var(--line-2);
  --raise: var(--near); --raise-2: var(--panel);
  color: var(--text);
}
header.site .inner { display: flex; align-items: center; gap: 34px; height: 64px; }

.brand { display: flex; align-items: center; gap: 10px; }
.brand .mark { width: 27px; height: 27px; flex: none; }
.brand .name { font-size: 1.06rem; font-weight: 600; letter-spacing: -.03em;
               color: var(--fg); }
.brand .name b { color: var(--scan); font-weight: 600; }
.brand .name .product { font-weight: 400; color: var(--fg-mute); margin-left: 7px;
                        letter-spacing: -.01em; }

nav.site { margin-left: auto; display: flex; align-items: center; gap: 8px; }
nav.site a { color: var(--fg-dim); font-size: .89rem; font-weight: 450;
             padding: 7px 12px; border-radius: 7px; white-space: nowrap;
             transition: color .15s var(--fast), background .15s var(--fast); }
nav.site a:hover { color: var(--fg); background: var(--raise-2); }
nav.site a.on { color: var(--fg); }

/* Products dropdown. Click-driven (see mountHeader) so it works on touch and
   by keyboard — a hover-only menu is unreachable on a phone. */
.navmenu { position: relative; }
.navtrig { display: inline-flex; align-items: center; gap: 6px;
           background: none; border: 0; cursor: pointer; font: inherit;
           color: var(--fg-dim); font-size: .89rem; font-weight: 450;
           padding: 7px 12px; border-radius: 7px;
           transition: color .15s var(--fast), background .15s var(--fast); }
.navtrig:hover, .navmenu.open .navtrig { color: var(--fg);
           background: var(--raise-2); }
.navtrig svg { transition: transform .18s var(--ease); opacity: .7; }
.navmenu.open .navtrig svg { transform: rotate(180deg); }

.navdrop { position: absolute; top: calc(100% + 10px); left: 0; z-index: 60;
           min-width: 310px; padding: 8px; border-radius: 14px;
           background: var(--raise); border: 1px solid var(--edge);
           box-shadow: 0 24px 60px rgba(0,0,0,.5);
           opacity: 0; visibility: hidden; transform: translateY(-6px);
           transition: opacity .18s var(--fast), transform .18s var(--ease),
                       visibility .18s; }
.navmenu.open .navdrop { opacity: 1; visibility: visible; transform: none; }
.navdrop a { display: flex; align-items: flex-start; gap: 11px;
             padding: 11px 13px; border-radius: 9px; }
.navdrop a img { border-radius: 8px; flex: 0 0 auto; margin-top: 1px; }
.navdrop .txt { min-width: 0; }
.navdrop .pn { display: flex; align-items: center; gap: 8px;
               color: var(--fg); font-weight: 500; font-size: .93rem; }
.navdrop .pt { display: block; margin-top: 3px; font-size: .8rem;
               color: var(--fg-mute); white-space: normal; line-height: 1.45; }
.navdrop .pill { font-family: var(--mono); font-size: .58rem; font-weight: 600;
                 letter-spacing: .1em; text-transform: uppercase;
                 padding: 2px 7px; border-radius: 999px;
                 color: var(--accent);
                 border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); }
@media (max-width: 700px) {
  .navdrop { min-width: 250px; left: auto; right: 0; }
}

/* --------------------------------------------------------------------- faq */
/* Native <details> — works with no JavaScript, is keyboard accessible, and
   search engines read the answers even while collapsed. */
.faq details { border-bottom: 1px solid var(--edge); }
.faq details:first-child { border-top: 1px solid var(--edge); }
.faq summary { list-style: none; cursor: pointer; padding: 20px 40px 20px 0;
               position: relative; font-size: 1.02rem; font-weight: 500;
               color: var(--fg); letter-spacing: -.015em;
               transition: color .15s var(--fast); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { color: var(--accent); }
.faq summary::after { content: ""; position: absolute; right: 8px; top: 50%;
               width: 9px; height: 9px; margin-top: -6px;
               border-right: 2px solid var(--fg-mute);
               border-bottom: 2px solid var(--fg-mute);
               transform: rotate(45deg);
               transition: transform .2s var(--ease); }
.faq details[open] summary::after { transform: rotate(-135deg); margin-top: -2px; }
.faq details p { margin: 0 0 22px; padding-right: 40px; color: var(--fg-dim);
               font-size: .94rem; line-height: 1.75; }

/* ---------------------------------------------------------- product cards */
/* Homepage catalogue. auto-fit so a third and fourth product reflow without
   anyone touching this file. */
/* max 420px per track: with two products auto-fit alone stretched each card to
   650px on a wide screen, which reads as a half-empty layout rather than a
   catalogue. Centred, so three or four products fill the row naturally. */
/* Four products → a tidy 2×2 block on desktop (was auto-fit, which left the
   4th card alone on a second row). Collapses to one column on narrow screens. */
.prodgrid { display: grid; gap: 20px; justify-content: center;
            grid-template-columns: repeat(2, minmax(300px, 420px)); }
@media (max-width: 680px) { .prodgrid { grid-template-columns: minmax(0, 420px); } }
.prodcard { display: flex; flex-direction: column;
            background: var(--raise); border: 1px solid var(--edge);
            border-radius: var(--r-lg); padding: 28px 26px 24px;
            transition: border-color .25s var(--fast),
                        transform .25s var(--ease); }
.prodcard:hover { border-color: var(--edge-2); transform: translateY(-3px); }
.prodcard .head { display: flex; align-items: center; gap: 12px;
                  margin-bottom: 14px; flex-wrap: wrap; }
.prodcard .picon { border-radius: 10px; flex: 0 0 auto; }
.prodcard h3 { margin: 0; font-size: 1.16rem; letter-spacing: -.02em;
               color: var(--fg); }
.prodcard .state { margin-left: auto; font-family: var(--mono);
                   font-size: .6rem; font-weight: 600; letter-spacing: .1em;
                   text-transform: uppercase; padding: 4px 9px;
                   border-radius: 999px; white-space: nowrap; }
.prodcard .state.live { color: var(--ok);
                border: 1px solid color-mix(in srgb, var(--ok) 40%, transparent); }
.prodcard .state.soon { color: var(--fg-mute); border: 1px solid var(--edge-2); }
.prodcard .tag { margin: 0 0 16px; color: var(--fg-dim); font-size: .95rem;
                 line-height: 1.6; }
.prodcard ul { list-style: none; padding: 0; margin: 0 0 20px; }
.prodcard ul li { position: relative; padding-left: 20px; margin-bottom: 8px;
                  font-size: .89rem; color: var(--fg-dim); line-height: 1.55; }
.prodcard ul li::before { content: "✓"; position: absolute; left: 0;
                          color: var(--accent); font-weight: 600; }
.prodcard .foot { margin-top: auto; padding-top: 16px;
                  border-top: 1px solid var(--edge); display: flex;
                  align-items: baseline; gap: 8px; flex-wrap: wrap; }
.prodcard .foot b { font-size: 1.32rem; font-weight: 600; color: var(--fg); }
.prodcard .foot span { font-size: .82rem; color: var(--fg-mute); }

/* ----------------------------------------------------------------- buttons */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 20px; border-radius: 9px; cursor: pointer; border: 1px solid;
  font-family: var(--ui); font-size: .92rem; font-weight: 500;
  letter-spacing: -.012em; transition: all .18s var(--fast);
  white-space: nowrap;
}
/* primary is WHITE — the highest-contrast thing on the page */
.btn.primary { background: var(--fg); border-color: var(--fg);
               color: var(--bg); font-weight: 550; }
.btn.primary:hover { opacity: .88; transform: translateY(-1px); }
.btn.ghost { background: transparent; border-color: var(--edge-2);
             color: var(--fg-2); }
.btn.ghost:hover { border-color: var(--fg-faint); background: var(--raise-2);
                   transform: translateY(-1px); }
.btn.solid { background: var(--raise-2); border-color: var(--edge-2);
             color: var(--fg-2); }
.btn.solid:hover { border-color: var(--fg-faint); filter: brightness(1.15); }
.btn.big { padding: 14px 26px; font-size: .98rem; border-radius: 10px; }
.btn.wide { width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; }
.btn .arr { transition: transform .22s var(--ease); }
.btn:hover .arr { transform: translateX(3px); }

/* ---------------------------------------------------------------- sections */

/* MEASURED on the live pdf page: at 92px top and bottom, section padding was
   1,616px - 1.7 screens, 15% of an 11-screen page - purely empty space. Two
   adjacent sections put 184px between their content, which reads as drift
   rather than rhythm. 64px still separates them clearly and gives back most
   of a screen. */
section { padding: 64px 0; }
section.tight { padding: 48px 0; }
section.flush { padding: 0; }
.center { text-align: center; }
.section-head { margin-bottom: 60px; max-width: 780px; }
.center .section-head { margin-left: auto; margin-right: auto; }
.hr { height: 1px; background: var(--edge); border: 0; margin: 0; }

/* -------------------------------------------------------------------- hero */

/* clip: the ::before glow below is 1200px wide and centred with a negative
   translate, so on any viewport under ~1200px it hangs past both edges. On a
   phone that is ~400px of invisible page either side — the document scrolls
   sideways and nothing in the DOM explains why, because a pseudo-element does
   not appear in the element tree. */
.hero { padding: 108px 0 92px; position: relative; overflow: hidden; }
/* one precise light source, top centre */
.hero::before {
  content: ""; position: absolute; top: -420px; left: 50%; width: 1200px;
  height: 760px; transform: translateX(-50%); pointer-events: none;
  background: radial-gradient(ellipse 46% 50% at 50% 50%,
              rgba(59,130,246,.13), transparent 70%);
}
/* engineered grid, barely there */
.hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background-image:
    linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.022) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(ellipse 68% 62% at 50% 32%, #000, transparent 76%);
}
.hero-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 88px;
             align-items: center; position: relative; }
/* The slider is the exception: centring it leaves a different amount of dead
   space under each slide, so the layout jumps as they rotate. It stretches to
   the row and its slides fill it. */
.hero-grid > .heroslider { align-self: stretch; }
.hero h1 { margin-bottom: 24px; }
.hero-cta { display: flex; gap: 11px; flex-wrap: wrap; margin-top: 34px; }
.center .hero-cta { justify-content: center; }
.hero-note { margin-top: 22px; font-family: var(--mono); font-size: .74rem;
             color: var(--fg-faint); letter-spacing: .04em;
             display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.hero-note .sep { width: 1px; height: 11px; background: var(--edge-2); }

/* announcement chip */
.chip {
  display: inline-flex; align-items: center; gap: 10px; margin-bottom: 26px;
  padding: 7px 15px; border: 1px solid var(--edge-2);
  border-radius: 100px; background: var(--raise-2); font-size: .82rem;
  color: var(--fg-dim); transition: border-color .2s var(--fast),
  background .2s var(--fast);
}
.chip:hover { background: var(--raise); }
.chip:hover { border-color: var(--fg-faint); }
.chip b { background: var(--fg); color: var(--bg); font-size: .64rem;
          font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
          padding: 3px 8px; border-radius: 100px; font-family: var(--mono); }

/* entrance — the hero's own staged arrival, once, on load */
.rise { opacity: 0; transform: translateY(14px);
        animation: rise .8s var(--ease) forwards; }
.rise.d1{animation-delay:.04s} .rise.d2{animation-delay:.11s}
.rise.d3{animation-delay:.18s} .rise.d4{animation-delay:.26s}
.rise.d5{animation-delay:.34s} .rise.d6{animation-delay:.42s}
@keyframes rise { to { opacity: 1; transform: none; } }

/* reveal-on-scroll — BOTH DIRECTIONS, so scrolling back up is alive too.
   A TRANSITION, not an animation: an animation plays once and cannot reverse,
   which is why the old version went dead after one pass. */
[data-reveal] { opacity: 0; transform: translateY(18px);
                transition: opacity .6s var(--ease), transform .6s var(--ease); }
[data-reveal].shown { opacity: 1; transform: none; }
/* Already on screen when the page painted: appear with no motion at all.
   Fading in something the reader is already looking at reads as a stutter. */
[data-reveal].instant { transition: none; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
  .rise { opacity: 1; transform: none; animation: none; }
}

/* --------------------------------------------------------- the price table
   SHARED, because every product page needs one. It was page-local to pdf.html,
   which meant ocr.html - the product that actually earns money - showed the
   same comparison in a plainer table.

   A price table is the one claim a sceptical buyer can check in ten seconds,
   so it is laid out to be read, not skimmed past. */
.cost-table { border: 1px solid var(--edge); border-radius: var(--r-lg);
              overflow: hidden; }
.tbl { width: 100%; border-collapse: collapse; font-size: .95rem; }
.tbl th { text-align: left; padding: 13px 18px; font-family: var(--mono);
          font-size: .62rem; letter-spacing: .11em; text-transform: uppercase;
          color: var(--fg-mute); font-weight: 500;
          border-bottom: 1px solid var(--edge); background: var(--raise); }
.tbl td { padding: 15px 18px; border-bottom: 1px solid var(--edge);
          color: var(--fg-dim); vertical-align: top; line-height: 1.55; }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl .n { text-align: right; white-space: nowrap;
          font-variant-numeric: tabular-nums; }
.tbl th.n { text-align: right; }
/* Our row is the reference point, so it is the only one at full contrast. */
.tbl tr.us td { background: var(--raise-2); color: var(--fg); }
.tbl tr.us b { color: var(--accent); }
.tbl-note { margin: 0; padding: 16px 18px; background: var(--raise);
            border-top: 1px solid var(--edge); font-size: .86rem;
            color: var(--fg-mute); line-height: 1.7; }
@media (max-width: 640px) {
  .tbl { font-size: .86rem; }
  .tbl th:last-child, .tbl td:last-child { display: none; }
}

/* ------------------------------------------------- fit a section to a screen
   Every screenshot was width:100% / height:auto, so its height came from the
   aspect ratio and nothing stopped a section growing past the viewport. On a
   laptop that meant a section could not be taken in at once, and the page read
   as an endless scroll rather than a sequence of ideas.

   Capping by viewport height rather than pixel width means the SAME rule works
   on a 13-inch laptop and a 27-inch monitor, and every future product page
   inherits it without thinking about it.

   `dvh` follows the mobile address bar as it hides; `vh` is the fallback for
   anything that does not support it. */
.stage img, .shot img, .app img, .ex-img img {
  max-height: 52vh;
  max-height: 52dvh;
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin-inline: auto;
}
/* The hero shot may take a little more - it is the first thing seen, and it is
   the whole product in one picture. */
.heroshot .app img { max-height: 58vh; max-height: 58dvh; }

/* ------------------------------------------------------- THE HERO SLIDER
   One slide per product, cross-fading. The homepage of a growing line cannot
   lead with one product's artwork - this pane used to be the ARIA OCR scanner
   alone, which made a three-product company read as a one-product company.

   Slides are stacked, so the pane keeps the height of the tallest and nothing
   jumps as they rotate. */
/* The TEXT pane rotates in step with the picture. Same stacking trick: one
   grid cell, so the column keeps the height of the tallest slide and the CTAs
   do not jump as the copy changes length. */
/* ⚠ THE CTAs MUST NOT MOVE BETWEEN SLIDES. Measured: a two-line headline is
   142px against a three-line 213px, so the lead, the proof bar and BOTH
   BUTTONS jumped 71px each time that slide came round - the buy button
   sliding out from under the pointer. The copy is written to three lines on
   every slide, and these floors stop a future edit reintroducing it. */
.herotexts { display: grid; }
.herotexts > .tslide > h1 { min-height: 3.1em; }
.herotexts > .tslide > .lead { min-height: 4.8em; }
.herotexts > .tslide { grid-area: 1 / 1; opacity: 0; visibility: hidden;
                       transform: translateY(6px);
                       transition: opacity .55s var(--ease),
                                   transform .55s var(--ease), visibility .55s;
                       pointer-events: none; }
.herotexts > .tslide.on { opacity: 1; visibility: visible;
                          transform: none; pointer-events: auto; }

/* ⚠ EACH SLIDE KEEPS ITS OWN SHAPE. Three earlier attempts forced one frame
   onto three different images - cropping filenames off with `cover`,
   letterboxing with `contain`, capping by viewport height - and each looked
   wrong in its own way.
   The right split: the CONTAINER is a fixed box so the layout never jumps,
   and each slide sizes ITSELF inside it, centred. A screenshot is shown at
   its natural aspect, never stretched and never cropped. */
.heroslider { position: relative; display: grid; align-items: center;
              min-height: 0; height: 62vh; height: 62dvh;
              max-height: 660px; }
/* ⚠ THE OUTGOING SLIDE MUST LEAVE BEFORE THE NEXT ARRIVES. A symmetrical
   cross-fade shows BOTH at once for half a second, and a screenshot fading
   through a scanned deed reads as a rendering fault, not a transition -
   caught by screenshotting mid-rotation. The incoming slide is delayed past
   the outgoing one's fade, so the overlap is never visible. */
.heroslider > .slide { align-self: center; justify-self: stretch;
                       max-height: 100%; min-height: 0;
                       display: flex; flex-direction: column;
                       transition: opacity .34s var(--ease),
                                   visibility 0s linear .34s; }
.heroslider > .slide.on { transition: opacity .42s var(--ease) .30s,
                                      visibility 0s linear .30s; }
/* The image area gives back whatever it does not need, so a short landscape
   shot makes a short slide rather than a padded one. */
.heroslider .shotslide .shotwrap { flex: 0 1 auto; min-height: 0; }
.heroslider .shotslide .shotwrap img { width: 100%; height: auto;
                                       max-height: 100%; object-fit: contain; }

/* ⚠ THE OCR SLIDE KEEPS ITS OWN SHAPE - different products, different
   placeholder sizes. It is a scanned DEED, and the stamp paper is what makes
   it read as a legal document at a glance. An earlier crop to landscape cut
   the stamp off entirely and destroyed the very thing it was showing; the
   image is now cropped to keep the stamp AND every highlighted line, which
   leaves it slightly portrait (0.83). So it sits narrower and centred while
   the two landscape screenshots run full width. */
.heroslider > .slide.scanner { justify-self: center; width: min(78%, 400px); }
.heroslider .page.real { flex: 1 1 auto; min-height: 0; overflow: hidden; }
.heroslider .page.real img { width: 100%; height: 100%;
                             object-fit: contain; object-position: top center; }
@media (max-height: 780px) {
  .heroslider { height: 56vh; height: 56dvh; }
}
/* ⚠ ALL slides share ONE grid cell, so they stack and the pane keeps the
   height of the tallest. An earlier version left the first slide in normal
   flow and absolutely positioned the rest - so slide 1 was ALWAYS visible
   underneath whichever slide was "on", and the OCR scanner showed through the
   ARIA REPAIR slide. Caught by looking at the page, not by reading the state. */
.heroslider > .slide { grid-area: 1 / 1; opacity: 0; visibility: hidden;
                       transition: opacity .7s var(--ease), visibility .7s;
                       pointer-events: none; }
.heroslider > .slide.on { opacity: 1; visibility: visible;
                          pointer-events: auto; }

.heroslider .dots { position: absolute; left: 0; right: 0; bottom: -30px;
                    display: flex; gap: 8px; justify-content: center; }
.heroslider .dots button { width: 7px; height: 7px; padding: 0; border: 0;
                    border-radius: 50%; background: var(--line-2);
                    cursor: pointer; transition: background .3s, width .3s; }
.heroslider .dots button[aria-selected="true"] { width: 20px; border-radius: 4px;
                    background: var(--accent); }

/* A screenshot-based slide, sharing the scanner's frame so all three read as
   one family. */
.shotslide,
.shieldslide { border-radius: var(--r-lg); overflow: hidden;
             border: 1px solid var(--edge); background: #0a0c0e;
             box-shadow: 0 40px 80px -32px rgba(0,0,0,.75); }
.shotslide .shotwrap { position: relative; overflow: hidden; }
.shotslide .shotwrap img { display: block; width: 100%; height: auto; }

/* ARIA PDF - a compression sweep passing down the shot. */
.shotslide .sweep { position: absolute; left: 0; right: 0; height: 34%;
    background: linear-gradient(180deg, transparent,
                rgba(34,211,238,.13) 45%, rgba(34,211,238,.30), transparent);
    border-bottom: 1px solid rgba(34,211,238,.5);
    animation: sweepDown 6s var(--ease) infinite; }
@keyframes sweepDown {
  0%       { top: -34%; opacity: 0; }
  12%      { opacity: 1; }
  78%      { opacity: 1; }
  88%,100% { top: 100%; opacity: 0; }
}

/* ARIA REPAIR - each verdict resolving in turn, the way the app fills them in.
   The bands sit over the five result rows in img/app-repair.png. */
.shotslide .row { position: absolute; left: 2.3%; right: 2.3%; height: 11.4%;
    border-radius: 6px; opacity: 0;
    animation: verdict 7s var(--ease) infinite; }
.shotslide .row.r1 { top: 22.5%; box-shadow: inset 0 0 0 1px rgba(245,166,35,.55);
                     background: rgba(245,166,35,.10); animation-delay: .1s; }
.shotslide .row.r2 { top: 35.0%; box-shadow: inset 0 0 0 1px rgba(245,166,35,.55);
                     background: rgba(245,166,35,.10); animation-delay: .5s; }
.shotslide .row.r3 { top: 47.6%; box-shadow: inset 0 0 0 1px rgba(255,90,86,.65);
                     background: rgba(255,90,86,.13); animation-delay: .9s; }
.shotslide .row.r4 { top: 60.2%; box-shadow: inset 0 0 0 1px rgba(61,149,245,.55);
                     background: rgba(61,149,245,.10); animation-delay: 1.3s; }
.shotslide .row.r5 { top: 72.8%; box-shadow: inset 0 0 0 1px rgba(61,149,245,.55);
                     background: rgba(61,149,245,.10); animation-delay: 1.7s; }
@keyframes verdict {
  0%, 6%    { opacity: 0; }
  16%, 78%  { opacity: 1; }
  90%, 100% { opacity: 0; }
}

/* ⚠ Reduced motion must leave the slides VISIBLE, not merely unanimated - a
   page that renders empty for these users is worse than one that does not
   move. The slider still advances; only the decoration stops. */
@media (prefers-reduced-motion: reduce) {
  .shotslide .sweep { animation: none; opacity: 0; }
  .shotslide .row { animation: none; opacity: 1; }
}

/* ---------------------------------------------------------- THE SPLIT HERO
   ⚠ SHARED, AND IT HAS TO BE. Every product page runs the same split hero
   (rulebook §2.1), and these rules used to live only inside pdf.html's local
   <style>. The ARIA REPAIR page therefore rendered with `display:block`: the
   screenshot fell BELOW the text and the hero ran a screen and a half before
   the product was visible at all. A page-local copy is a copy that drifts. */
.herosplit { display: grid;
             grid-template-columns: minmax(0, 1.02fr) minmax(0, 1.18fr);
             gap: 44px; align-items: center; }
.herotext { max-width: 37rem; }
.heroshot { position: relative; }
.heroshot .app { box-shadow: 0 30px 70px -28px rgba(0,0,0,.85),
                             0 0 0 1px var(--edge); border-radius: 12px; }
.shotcap { margin: 14px 2px 0; font-size: .86rem; line-height: 1.6;
           color: var(--fg-mute); }
@media (max-width: 980px) {
  .herosplit { grid-template-columns: 1fr; gap: 34px; }
  .herotext { max-width: none; }
}

/* The window chrome drawn around a screenshot, so it reads as an application
   and not as a stray picture. Shared for the same reason as the hero.
   ⚠ pdf.html deliberately OVERRIDES .app locally: its screenshots sit flush on
   a stage, so they drop the bottom border and square off the bottom corners.
   That override is correct and must keep winning - do not "unify" it. */
.app { border: 1px solid rgba(255,255,255,.1);
       background: var(--panel); border-radius: 12px; overflow: hidden; }
.app-bar { display: flex; align-items: center; gap: 8px; padding: 11px 15px;
           background: var(--panel-2); border-bottom: 1px solid var(--line); }
.app-bar i { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto;
             background: var(--line-2); }
.app-bar i:first-child  { background: #ff5f57; }
.app-bar i:nth-child(2) { background: #febc2e; }
.app-bar i:nth-child(3) { background: #28c840; }
.app-bar span { margin-left: 8px; font-family: var(--mono); font-size: .66rem;
                letter-spacing: .05em; color: var(--faint); }
.app img { display: block; width: 100%; height: auto; }
/* Before/after sliders must keep BOTH layers the same size or the comparison
   stops being a comparison, so they are capped as a unit, not per image. */
.ba { max-height: 46vh; max-height: 46dvh; overflow: hidden; }
.ba img { max-height: none; }

/* Short viewports - a laptop in a browser with tabs and a dock - need the caps
   tighter still, or a "section" is again taller than the screen. */
@media (max-height: 780px) {
  .stage img, .shot img, .app img, .ex-img img { max-height: 46vh; max-height: 46dvh; }
  .heroshot .app img { max-height: 50vh; max-height: 50dvh; }
  .ba { max-height: 40vh; max-height: 40dvh; }
  section { padding: 52px 0; }
}

/* ------------------------------------------------------------ back to top */
.totop {
  position: fixed; right: 22px; bottom: 22px; z-index: 60;
  width: 42px; height: 42px; display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  background: var(--raise-2); color: var(--fg);
  border: 1px solid var(--edge-2);
  box-shadow: 0 8px 24px -10px rgba(0,0,0,.6);
  opacity: 0; visibility: hidden; transform: translateY(10px) scale(.94);
  transition: opacity .28s var(--ease), transform .28s var(--ease),
              visibility .28s, border-color .18s var(--fast),
              background .18s var(--fast);
}
.totop.on { opacity: 1; visibility: visible; transform: none; }
.totop:hover { border-color: var(--fg-faint); background: var(--raise); }
.totop:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.totop svg { width: 17px; height: 17px; }
@media (max-width: 640px) { .totop { right: 14px; bottom: 14px; } }
@media (prefers-reduced-motion: reduce) {
  .totop { transition: opacity .2s linear, visibility .2s; transform: none; }
  .totop.on { transform: none; }
}

/* ------------------------------------------------------- the scanner panel */

/* The scanner is a picture of the application, so it stays dark wherever it
   sits — on a light band it reads as a device on a desk. */
.scanner {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  /* A portrait page is tall. Narrow the panel and centre it in its column so
     the full document shows without out-scaling the headline beside it. */
  max-width: 380px; margin-left: auto; margin-right: 0;
  background: #0d0f11; border: 1px solid #1e2226;
  box-shadow: 0 50px 100px -40px rgba(0,0,0,.55),
              0 0 0 1px rgba(255,255,255,.02) inset;
}
.light .scanner { box-shadow: 0 40px 80px -32px rgba(8,9,10,.35),
                              0 2px 8px rgba(8,9,10,.08); }
/* Deliberately NOT browser chrome. Traffic-light dots would say "web app",
   which is the opposite of what this product is: an offline desktop
   application. This reads as a document viewer — file name, page, and the
   confidence the engine actually reports. */
/* ⚠ SCOPED TO BOTH SLIDE TYPES. These rules named only .scanner, so on the
   ARIA PDF and ARIA REPAIR slides the little document icon rendered at its
   natural SVG size - a huge glyph filling the top of the frame - and the
   filename and badge fell back to unstyled body text. The window furniture is
   shared by every slide, so the selectors must be too. */
.scanner .chrome,
.shotslide .chrome,
.shieldslide .chrome {
  display: flex; align-items: center; gap: 10px; padding: 12px 15px;
  border-bottom: 1px solid #1e2226; background: #121417;
}
.scanner .chrome .doc,
.shotslide .chrome .doc,
.shieldslide .chrome .doc { width: 15px; height: 15px; flex: none;
                          color: #6b7280; }
.scanner .chrome .file,
.shotslide .chrome .file,
.shieldslide .chrome .file { font-family: var(--mono); font-size: .7rem;
                         color: #9ba1a8; overflow: hidden;
                         text-overflow: ellipsis; white-space: nowrap; }
.scanner .chrome .conf,
.shotslide .chrome .conf,
.shieldslide .chrome .conf { margin-left: auto; font-family: var(--mono);
                         font-size: .67rem; color: var(--ok); flex: none;
                         display: inline-flex; align-items: center; gap: 6px;
                         padding: 3px 9px; border-radius: 100px;
                         border: 1px solid rgba(52,211,153,.28);
                         background: rgba(52,211,153,.07); }
.scanner .chrome .conf::before,
.shotslide .chrome .conf::before,
.shieldslide .chrome .conf::before { content: ""; width: 5px; height: 5px;
                                 border-radius: 50%; background: var(--ok); }
/* The two non-green states, so a badge reading "would not open" is not drawn
   in the success colour. */
.shotslide .chrome .conf.warn { color: var(--warn, #f5a623);
    border-color: rgba(245,166,35,.30); background: rgba(245,166,35,.08); }
.shotslide .chrome .conf.warn::before { background: var(--warn, #f5a623); }

.page { position: relative; padding: 26px 24px; overflow: hidden;
        min-height: 292px; background:
        linear-gradient(180deg, #0d0f11, #0a0c0e); }

/* Real scanned page behind the recognition overlay. A genuine document sells
   this far better than drawn placeholder bars: the foxing, the torn edge and
   the typewriter ink are exactly the conditions the software has to cope with.
   Names and signatures are irreversibly blurred — see img/deed-sample.jpg. */
/* The whole page is shown — the stamp block, the typed body and the torn edge
   together are what make it read as a real document. The panel itself is
   narrowed instead (see .scanner max-width) so a full portrait page does not
   tower over the hero copy. */
.page.real { padding: 0; min-height: 0; background: #0a0c0e; }
.page.real img { display: block; width: 100%; height: auto;
                 /* the scan is warm cream; cool and dim it slightly so it sits
                    inside a dark UI without glaring */
                 filter: brightness(.92) saturate(.88) contrast(1.04); }

/* Words the engine has recognised, drawn over the scan */
.page.real .hit { position: absolute; border-radius: 2px;
                  background: rgba(34,211,238,.16);
                  box-shadow: inset 0 0 0 1px rgba(34,211,238,.55);
                  animation: hit 7s var(--ease) infinite; opacity: 0; }
.page.real .hit.low { background: rgba(248,113,113,.18);
                      box-shadow: inset 0 0 0 1px rgba(248,113,113,.7); }
@keyframes hit {
  0%, 12%   { opacity: 0; }
  22%, 76%  { opacity: 1; }
  88%, 100% { opacity: 0; }
}
.page .stamp { display: flex; justify-content: space-between;
               align-items: flex-start; padding-bottom: 16px; margin-bottom: 18px;
               border-bottom: 1px solid #1e2226; }
.page .stamp .seal { width: 44px; height: 44px; border-radius: 50%; flex: none;
                     border: 1px solid #2a2f36; color: #454b53;
                     display: grid; place-items: center; font-family: var(--mono);
                     font-size: .5rem; line-height: 1.2; text-align: center;
                     transform: rotate(-9deg); }
.page .stamp .meta { text-align: right; font-family: var(--mono);
                     font-size: .59rem; color: #454b53; line-height: 2; }

.ln { height: 7px; border-radius: 2px; margin-bottom: 12px; background: #1c2126;
      animation: resolve 7s var(--ease) infinite; }
.ln.t { height: 10px; background: #262c33; }
.ln.w70{width:70%} .ln.w85{width:85%} .ln.w60{width:60%}
.ln.w95{width:95%} .ln.w45{width:45%} .ln.w78{width:78%}
.ln:nth-child(2){animation-delay:.14s} .ln:nth-child(3){animation-delay:.28s}
.ln:nth-child(4){animation-delay:.42s} .ln:nth-child(5){animation-delay:.56s}
.ln:nth-child(6){animation-delay:.7s}  .ln:nth-child(7){animation-delay:.84s}
.ln:nth-child(8){animation-delay:.98s} .ln:nth-child(9){animation-delay:1.12s}
.ln:nth-child(10){animation-delay:1.26s}
@keyframes resolve {
  0%,7%    { background: #1c2126; filter: blur(1.4px); }
  17%,74%  { background: #dfe3e7; filter: blur(0); }
  86%,100% { background: #1c2126; filter: blur(1.4px); }
}
.ln.low { animation-name: resolveLow; }
@keyframes resolveLow {
  0%,7%    { background: #1c2126; filter: blur(1.4px); }
  17%,74%  { background: var(--bad); filter: blur(0); }
  86%,100% { background: #1c2126; filter: blur(1.4px); }
}

.scanline { position: absolute; left: 0; right: 0; height: 64px;
            pointer-events: none;
            background: linear-gradient(180deg, transparent,
                        rgba(34,211,238,.07) 70%, rgba(34,211,238,.16));
            border-bottom: 1px solid var(--scan);
            box-shadow: 0 0 24px rgba(34,211,238,.4);
            animation: sweep 7s var(--ease) infinite; }
@keyframes sweep {
  0%   { top: -70px; opacity: 0; }
  7%   { opacity: 1; }
  72%  { top: 100%; opacity: 1; }
  80%,100% { top: 100%; opacity: 0; }
}

/* ⚠ .shotslide TOO, not just .scanner. These were scoped to .scanner alone,
   so the ARIA PDF and ARIA REPAIR slides rendered their readout as unstyled
   body text - "OPENED 10 / 12ORIGINAL UNTOUCHED OFFLINE" in 16px, running out
   of the frame. Every slide shares the same window furniture. */
.scanner .readout,
.shotslide .readout { display: flex; gap: 20px; padding: 13px 16px;
                    border-top: 1px solid #1e2226; background: #121417;
                    font-family: var(--mono); font-size: .68rem;
                    color: #454b53; flex-wrap: wrap; }
.scanner .readout b,
.shotslide .readout b,
.shieldslide .readout b { color: #f4f5f6; font-weight: 500; }
.shieldslide .readout { display: flex; gap: 20px; padding: 13px 16px;
                    border-top: 1px solid #1e2226; background: #121417;
                    font-family: var(--mono); font-size: .68rem;
                    color: #454b53; flex-wrap: wrap; }

/* ARIA SHIELD hero slide — a CSS terminal running `shield build`, with a magenta
   protection scan-line sweeping the code on a loop. No screenshot; the animation
   is the product. Mirrors the /shield page hero terminal. */
.shieldslide .shieldterm { position: relative; flex: 1 1 auto; min-height: 0;
   overflow: hidden; white-space: pre; font-family: var(--mono);
   font-size: .74rem; line-height: 1.85; color: #7d858f;
   padding: 18px 20px; background: linear-gradient(180deg,#0d1017,#0a0c11); }
.shieldslide .shieldterm .sp { color: #d946ef; }
.shieldslide .shieldterm .sk { color: #8fb8ff; }
.shieldslide .shieldterm .ss { color: #5ad1c8; }
.shieldslide .shieldterm .sw { color: #f5a623; }
.shieldslide .shieldterm .sg { color: #2bc46a; }
.shieldslide .shieldscanbar { position: absolute; left: 0; right: 0; top: 0;
   height: 56px; pointer-events: none; z-index: 2;
   background: linear-gradient(180deg, rgba(217,70,239,0) 0%,
     rgba(217,70,239,.10) 45%, rgba(217,70,239,.22) 50%,
     rgba(217,70,239,.10) 55%, rgba(217,70,239,0) 100%);
   animation: shieldheroscan 5.5s cubic-bezier(.5,0,.5,1) infinite; }
@keyframes shieldheroscan {
  0%   { transform: translateY(6px);   opacity: 0; }
  8%   { opacity: 1; }
  50%  { transform: translateY(240px); opacity: 1; }
  62%  { opacity: 0; }
  100% { transform: translateY(240px); opacity: 0; }
}
.shieldslide .sdone { animation: shieldheroglow 5.5s ease-in-out infinite; }
@keyframes shieldheroglow {
  0%,55%   { text-shadow: none; }
  66%      { text-shadow: 0 0 12px rgba(43,196,106,.9); }
  80%,100% { text-shadow: 0 0 5px rgba(43,196,106,.45); }
}
.shieldslide .scaret { display: inline-block; width: 7px; height: 13px;
   margin-left: 2px; background: #d946ef; vertical-align: -2px;
   animation: shieldherocaret 1.05s step-end infinite; }
@keyframes shieldherocaret { 0%,50%{opacity:1;} 51%,100%{opacity:0;} }
@media (prefers-reduced-motion: reduce) {
  .shieldslide .shieldscanbar, .shieldslide .sdone, .shieldslide .scaret {
    animation: none; }
  .shieldslide .shieldscanbar { display: none; }
}

/* ------------------------------------------------------------------- cards */

.grid { display: grid; gap: 1px; background: var(--edge); border: 1px solid
        var(--edge); border-radius: var(--r-lg); overflow: hidden; }
.grid.c2 { grid-template-columns: repeat(2,1fr); }
.grid.c3 { grid-template-columns: repeat(3,1fr); }
/* bordered grid: cells share hairlines, like a spec sheet */
.grid > .cell { background: var(--bg); padding: 40px 36px;
                transition: background .25s var(--fast); }
.grid > .cell:hover { background: var(--raise); }
.cell h3 { margin-bottom: 10px; }
.cell p { color: var(--fg-dim); margin: 0; font-size: .93rem; line-height: 1.65; }
.cell .ico { width: 32px; height: 32px; margin-bottom: 20px; color: var(--fg);
             display: grid; place-items: center; }
.cell .ico svg { width: 19px; height: 19px; }

/* standalone card, used where a grid is wrong */
.card { background: var(--raise); border: 1px solid var(--edge);
        border-radius: var(--r-lg); padding: 36px; }
.card h3 { margin-bottom: 10px; }
.card p { color: var(--fg-dim); font-size: .94rem; }
.card p:last-child { margin-bottom: 0; }

/* -------------------------------------------------------------- bento grid
   Uneven cells: one wide feature, several supporting. Reads as a considered
   layout rather than three identical boxes. */

.bento { display: grid; grid-template-columns: repeat(3, 1fr);
         gap: 1px; background: var(--edge); border: 1px solid var(--edge);
         border-radius: var(--r-lg); overflow: hidden; }
.bento > * { background: var(--bg); padding: 38px 34px;
             transition: background .25s var(--fast); }
.bento > *:hover { background: var(--raise); }
.bento .span2 { grid-column: span 2; }
.bento h3 { margin-bottom: 10px; }
.bento p { color: var(--fg-dim); margin: 0; font-size: .93rem; line-height: 1.65; }
.bento .ico { width: 30px; height: 30px; margin-bottom: 18px; color: var(--fg);
              display: grid; place-items: center; }
.bento .ico svg { width: 18px; height: 18px; }

/* a metric shown inside a bento cell */
.bento .metric { font-size: 2.4rem; font-weight: 600; letter-spacing: -.045em;
                 color: var(--fg); line-height: 1; margin-bottom: 6px; }
.bento .metric small { font-size: 1rem; color: var(--fg-mute); font-weight: 400; }

/* ------------------------------------------------------------ code / terminal
   Used to show real output. Always dark, wherever it sits. */

.term { border-radius: var(--r); overflow: hidden;
        border: 1px solid #1e2226; background: #0d0f11;
        font-family: var(--mono); font-size: .8rem; line-height: 1.9; }
.light .term { box-shadow: 0 24px 50px -22px rgba(8,9,10,.4),
                           0 2px 6px rgba(8,9,10,.07); }
.term .bar { display: flex; align-items: center; gap: 7px; padding: 10px 14px;
             background: #121417; border-bottom: 1px solid #1e2226; }
.term .bar i { width: 7px; height: 7px; border-radius: 50%; background: #2a2f36; }
.term .bar span { margin-left: 8px; font-size: .68rem; color: #6b7280;
                  letter-spacing: .04em; }
.term .body { padding: 16px 18px; color: #9ba1a8; overflow-x: auto; }
.term .body .c { color: var(--faint); }
.term .body .g { color: var(--ok); }
.term .body .w { color: var(--white); }
.term .body .a { color: var(--scan); }
.term .body .r { color: var(--bad); }

/* ------------------------------------------------------------ comparison row
   Two columns divided by a hairline: what it replaces vs what you get. */

.versus { display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
          background: var(--edge); border: 1px solid var(--edge);
          border-radius: var(--r-lg); overflow: hidden; }
.versus > div { background: var(--bg); padding: 30px; }
.versus .head { font-family: var(--mono); font-size: .68rem; letter-spacing: .16em;
                text-transform: uppercase; color: var(--fg-mute);
                margin-bottom: 20px; display: flex; align-items: center; gap: 9px; }
.versus ul { list-style: none; padding: 0; margin: 0; }
.versus li { font-size: .92rem; color: var(--fg-dim); padding: 9px 0 9px 26px;
             position: relative; border-top: 1px solid var(--edge); }
.versus li:first-child { border-top: none; }
.versus li::before { position: absolute; left: 0; top: 9px; font-size: .95rem; }
.versus .no li::before  { content: "×"; color: var(--bad); font-weight: 600; }
.versus .yes li::before { content: "✓"; color: var(--ok); font-weight: 600; }

/* ----------------------------------------------------------------- pricing */

/* auto-fit, not repeat(3,1fr): the Suite shows only TWO cards, and a fixed
   three-column grid leaves a dead gap where the third would be. */
.plans { display: grid; gap: 20px; align-items: start;
         grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

/* Product switcher — segmented control above the cards. */
.prodswitch { display: flex; justify-content: center; gap: 6px; margin: 0 auto 30px;
              padding: 5px; border: 1px solid var(--edge); border-radius: 999px;
              background: var(--raise); width: fit-content; max-width: 100%;
              flex-wrap: wrap; }
.prodswitch button { appearance: none; border: 0; background: transparent;
              color: var(--fg-mute); font: inherit; font-size: .88rem;
              font-weight: 500; padding: 9px 20px; border-radius: 999px;
              cursor: pointer; white-space: nowrap;
              transition: background .2s var(--fast), color .2s var(--fast); }
.prodswitch button:hover { color: var(--fg); }
.prodswitch button.on { background: var(--fg); color: var(--bg); }

/* Not-yet-on-sale card: readable, clearly not purchasable. The label must stay
   LEGIBLE — dimming the button to .5 made "Coming shortly" almost invisible on
   the dark card, which reads as a broken button rather than a deliberate one. */
.plan.soon { opacity: .9; }
.plan.soon .btn[disabled] { cursor: not-allowed; pointer-events: none;
                            background: transparent; color: var(--fg-mute);
                            border: 1px dashed var(--edge-2); opacity: 1; }
@media (max-width: 560px) {
  .prodswitch { width: 100%; }
  .prodswitch button { flex: 1 1 auto; padding: 9px 12px; font-size: .82rem; }
}
.plan { background: var(--raise); border: 1px solid var(--edge);
        border-radius: var(--r-lg); padding: 36px 34px; position: relative;
        transition: border-color .25s var(--fast), transform .25s var(--ease); }
.plan:hover { border-color: var(--edge-2); transform: translateY(-2px); }
.plan.featured { border-color: var(--fg); background: var(--raise-2); }
.plan .tag { position: absolute; top: -11px; left: 28px; background: var(--fg);
             color: var(--bg); font-family: var(--mono); font-size: .61rem;
             font-weight: 600; letter-spacing: .12em; padding: 4px 11px;
             border-radius: 100px; text-transform: uppercase; }
.plan .term { font-family: var(--mono); font-size: .7rem; letter-spacing: .16em;
              text-transform: uppercase; color: var(--fg-mute); }
.plan .price { font-size: 3.4rem; font-weight: 600; margin: 12px 0 0;
               letter-spacing: -.05em; line-height: 1; color: var(--fg); }
.plan .per { font-size: .84rem; color: var(--fg-mute); margin-top: 9px;
             font-family: var(--mono); letter-spacing: .02em; }
.plan .save { display: inline-block; margin-top: 14px; font-size: .72rem;
              font-family: var(--mono); letter-spacing: .06em; color: var(--ok);
              border: 1px solid rgba(52,211,153,.28); border-radius: 100px;
              padding: 3px 11px; }
.plan ul { list-style: none; padding: 0; margin: 24px 0 26px; }
.plan li { font-size: .9rem; color: var(--fg-dim); padding: 7px 0 7px 24px;
           position: relative; }
.plan li::before { content: ""; position: absolute; left: 2px; top: 14px;
                   width: 11px; height: 5px; border-left: 1.5px solid var(--scan);
                   border-bottom: 1.5px solid var(--scan);
                   transform: rotate(-45deg); }

/* ============================================================== persuasion
   Components that carry the emotional argument rather than the factual one.
   The rest of the site establishes credibility; these make the reader feel
   the cost of doing nothing. */

/* The one comparison that matters: an afternoon of retyping vs under a minute */
.stakes { display: grid; grid-template-columns: 1fr auto 1fr; gap: 0;
          border: 1px solid var(--edge); border-radius: var(--r-lg);
          overflow: hidden; align-items: stretch; }
.stakes > div { padding: 40px 36px; background: var(--bg); }
.stakes .before { background: var(--raise); }
.stakes .arrow { display: grid; place-items: center; padding: 0 26px;
                 background: var(--raise-2); border-left: 1px solid var(--edge);
                 border-right: 1px solid var(--edge); color: var(--fg-faint); }
.stakes .lab { font-family: var(--mono); font-size: .67rem; letter-spacing: .17em;
               text-transform: uppercase; color: var(--fg-mute);
               margin-bottom: 18px; }
.stakes .fig { font-size: clamp(2.6rem, 4.6vw, 4rem); font-weight: 600;
               letter-spacing: -.045em; line-height: .95; color: var(--fg); }
.stakes .fig small { font-size: .3em; font-weight: 450; letter-spacing: -.01em;
                     color: var(--fg-mute); display: block; margin-top: 12px;
                     line-height: 1.5; }
.stakes .before .fig { color: var(--bad); }
.stakes .after  .fig { color: var(--ok); }
.stakes .sub { margin: 18px 0 0; font-size: .92rem; color: var(--fg-dim); }

/* Payment trust marks */
.trust { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;
         margin-top: 26px; }
.trust span { display: inline-flex; align-items: center; gap: 8px;
              font-family: var(--mono); font-size: .68rem; letter-spacing: .09em;
              text-transform: uppercase; color: var(--fg-mute);
              border: 1px solid var(--edge); border-radius: 100px;
              padding: 7px 14px; }
.trust svg { width: 13px; height: 13px; color: var(--ok); }

/* ------------------------------------------------------------ colour accents
   Colour is used sparingly and always to mean something: cyan = the software
   reading, green = confirmed, amber = a caveat, red = flagged. Decoration is
   limited to the gradient hairlines that separate the bands, which give the
   page life without competing with the content. */

/* A luminous hairline at a band boundary — brightest in the middle, fading out */
.seam { height: 1px; border: 0; margin: 0;
        background: linear-gradient(90deg, transparent,
                    rgba(34,211,238,.55) 32%, rgba(59,130,246,.55) 68%,
                    transparent); }

/* Numbers in the stat strip get a coloured underline that draws on hover */
.stat { position: relative; }
.stat::after { content: ""; position: absolute; left: 0; bottom: -1px;
               width: 0; height: 2px; background: var(--scan);
               transition: width .55s var(--ease); }
.stat:hover::after { width: 100%; }
.stat:nth-child(2)::after { background: var(--ok); }
.stat:nth-child(3)::after { background: var(--accent); }
.stat:nth-child(4)::after { background: var(--warn); }

/* Icon tiles pick up a tinted background and border */
.cell .ico, .bento .ico {
  border-radius: 9px; border: 1px solid;
  background: color-mix(in srgb, var(--scan) 9%, transparent);
  border-color: color-mix(in srgb, var(--scan) 22%, transparent);
  color: var(--scan); width: 38px; height: 38px;
}
/* rotate the accent so a grid of cells is not monotone */
.cell:nth-child(2) .ico, .bento > *:nth-child(2) .ico {
  background: color-mix(in srgb, var(--ok) 9%, transparent);
  border-color: color-mix(in srgb, var(--ok) 22%, transparent);
  color: var(--ok); }
.cell:nth-child(3) .ico, .bento > *:nth-child(3) .ico {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent) 26%, transparent);
  color: var(--accent-hi); }
.bento > *:nth-child(4) .ico {
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  border-color: color-mix(in srgb, var(--warn) 24%, transparent);
  color: var(--warn); }
.light .cell:nth-child(3) .ico, .light .bento > *:nth-child(3) .ico {
  color: var(--accent); }

/* The chip's dot pulses — the only motion in the header area */
.chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok);
             flex: none; box-shadow: 0 0 0 0 rgba(52,211,153,.6);
             animation: pulse 2.4s ease-out infinite; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(52,211,153,.55); }
  70%  { box-shadow: 0 0 0 7px rgba(52,211,153,0); }
  100% { box-shadow: 0 0 0 0 rgba(52,211,153,0); }
}

/* Metric numerals in the bento take the accent */
.bento .metric { color: var(--scan); }
.light .bento .metric { color: var(--accent); }

/* Featured plan gets a coloured glow rather than only a white border */
.plan.featured::before {
  content: ""; position: absolute; inset: -1px; border-radius: var(--r-lg);
  padding: 1px; pointer-events: none;
  background: linear-gradient(160deg, var(--scan), var(--accent) 55%,
              transparent);
  -webkit-mask: linear-gradient(#000 0 0) content-box,
                linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}

/* -------------------------------------------------------------------- misc */

.note { background: var(--raise); border: 1px solid var(--edge);
        border-radius: var(--r); padding: 18px 22px; font-size: .93rem;
        color: var(--fg-dim); display: flex; gap: 14px; align-items: flex-start; }
.note::before { content: ""; width: 3px; align-self: stretch; flex: none;
                background: var(--scan); border-radius: 2px; }
.note strong { color: var(--fg); font-weight: 550; }
.note.warn::before { background: var(--warn); }

table.plain { width: 100%; border-collapse: collapse; font-size: .93rem; }
table.plain th, table.plain td { text-align: left; padding: 15px 16px;
                                 border-bottom: 1px solid var(--edge);
                                 vertical-align: top; }
table.plain th { font-family: var(--mono); color: var(--fg-mute); font-size: .68rem;
                 letter-spacing: .14em; font-weight: 500; text-transform: uppercase;
                 white-space: nowrap; }
table.plain td { color: var(--fg-dim); }
table.plain tr:last-child th, table.plain tr:last-child td { border-bottom: none; }
table.plain a { color: var(--accent-hi); }
table.plain a:hover { text-decoration: underline; }

code, .mono { font-family: var(--mono); font-size: .9em; }
.key { font-family: var(--mono); background: var(--raise-2);
       border: 1px solid var(--edge-2); border-radius: 8px; padding: 9px 14px;
       display: inline-block; color: var(--fg); letter-spacing: .08em;
       font-size: .86rem; }

.pill { display: inline-block; font-family: var(--mono); font-size: .64rem;
        font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
        padding: 4px 10px; border-radius: 100px; border: 1px solid; }
.pill.ok      { color: var(--ok);   border-color: rgba(52,211,153,.3); }
.pill.warn    { color: var(--warn); border-color: rgba(251,191,36,.3); }
.pill.danger  { color: var(--bad);  border-color: rgba(248,113,113,.3); }
.pill.neutral { color: var(--fg-mute); border-color: var(--edge-2); }

/* stat strip */
.stats { display: grid; grid-template-columns: repeat(4,1fr); gap: 1px;
         background: var(--edge); border-bottom: 1px solid var(--edge);
         max-width: var(--wrap); margin: 0 auto;
         border-left: 1px solid var(--edge); border-right: 1px solid var(--edge); }
.stat { background: var(--bg); padding: 42px 34px; }
.stat .n { font-size: 3rem; font-weight: 600; letter-spacing: -.045em;
           color: var(--fg); line-height: 1; margin-bottom: 9px; }
.stat .n small { font-size: 1.1rem; color: var(--fg-mute); font-weight: 400;
                 letter-spacing: -.02em; }
.stat .l { font-family: var(--mono); font-size: .68rem; letter-spacing: .13em;
           text-transform: uppercase; color: var(--fg-mute); }

/* Published method. Folded by default so it never competes with the numbers,
   but present - every competitor states an accuracy claim with no method at
   all, so showing the working is the differentiator. */
.method { max-width: 74ch; margin: 30px auto 0; }
.method > summary { cursor: pointer; font-family: var(--mono);
                    font-size: .68rem; letter-spacing: .13em;
                    text-transform: uppercase; color: var(--fg-mute);
                    padding: 11px 0; list-style: none;
                    display: flex; align-items: center; gap: 9px;
                    transition: color .18s var(--fast); }
.method > summary::-webkit-details-marker { display: none; }
.method > summary::before { content: "+"; font-size: .95rem; line-height: 1;
                            color: var(--accent); }
.method[open] > summary::before { content: "–"; }
.method > summary:hover { color: var(--fg); }
.method-body { padding: 4px 0 8px 20px; border-left: 2px solid var(--edge); }
.method-body p { font-size: .92rem; line-height: 1.75; color: var(--fg-dim);
                 margin: 0 0 13px; }
.method-body p:last-child { margin-bottom: 0; }
.method-body b { color: var(--fg); font-weight: 600; }

/* --------------------------------------------------------------- proof bar
   Measured numbers, directly under the headline, above the fold.

   PDFgear is the only competitor page in the studied set that puts evidence
   that high - it uses a 4.9-star badge. We have no rating and will not invent
   one, so this slot carries figures nobody else in the category publishes at
   all. SHARED, not page-local: every product page gets the same block. */
.proofbar { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 20px 0 4px;
            padding: 13px 0; border-top: 1px solid var(--edge);
            border-bottom: 1px solid var(--edge); }
.proofbar span { font-family: var(--mono); font-size: .66rem; letter-spacing: .1em;
                 text-transform: uppercase; color: var(--fg-mute);
                 display: flex; align-items: baseline; gap: 7px; }
.proofbar b { font-family: inherit; font-size: 1.18rem; font-weight: 600;
              letter-spacing: -.02em; color: var(--fg); }

/* ------------------------------------------------------------------- forms */

input[type=text], input[type=email], input[type=tel], textarea, select {
  width: 100%; padding: 12px 15px; font-size: .95rem; font-family: inherit;
  background: var(--raise-2); color: var(--fg); border: 1px solid var(--edge-2);
  border-radius: 9px; transition: border-color .18s var(--fast),
  box-shadow .18s var(--fast); letter-spacing: -.01em;
}
input:focus, textarea:focus, select:focus { outline: none;
  border-color: var(--fg-faint); box-shadow: 0 0 0 3px rgba(255,255,255,.06); }
input::placeholder { color: var(--fg-faint); }
label { display: block; font-family: var(--mono); font-size: .66rem;
        font-weight: 500; letter-spacing: .15em; text-transform: uppercase;
        color: var(--fg-mute); margin-bottom: 9px; }
.field { margin-bottom: 18px; }
.msg { font-size: .9rem; padding: 12px 16px; border-radius: 9px; margin: 16px 0;
       display: none; border: 1px solid; }
.msg.show { display: block; }
.msg.err  { color: #fca5a5; border-color: rgba(248,113,113,.3);
            background: rgba(248,113,113,.06); }
.msg.ok   { color: #6ee7b7; border-color: rgba(52,211,153,.3);
            background: rgba(52,211,153,.06); }
.msg.info { color: #93c5fd; border-color: rgba(59,130,246,.3);
            background: rgba(59,130,246,.06); }

/* ------------------------------------------------------------------- legal */

.legal { max-width: 740px; }
.legal h1 { font-size: clamp(2.1rem,4.4vw,3rem); }
.legal h2 { margin-top: 52px; font-size: 1.32rem; letter-spacing: -.025em; }
.legal h2:first-of-type { margin-top: 0; }
.legal h3 { margin-top: 30px; font-size: .99rem; color: var(--fg-2); }
.legal p, .legal li { color: var(--fg-dim); font-size: .96rem; line-height: 1.72; }
.legal ul { padding-left: 22px; }
.legal li { margin-bottom: 10px; }
.legal a { color: var(--accent-hi); }
.legal a:hover { text-decoration: underline; }
.updated { font-family: var(--mono); color: var(--fg-faint); font-size: .76rem;
           letter-spacing: .1em; text-transform: uppercase; margin-bottom: 44px;
           padding-bottom: 26px; border-bottom: 1px solid var(--edge); }

/* ------------------------------------------------------------------ footer */

/* Footer is always dark — it closes the page the way the header opens it. */
footer.site { border-top: 1px solid var(--line); padding: 68px 0 40px;
              background: var(--black); color: var(--text);
              --bg: var(--black); --fg: var(--white); --fg-2: var(--text);
              --fg-dim: var(--dim); --fg-mute: var(--mute);
              --fg-faint: var(--faint);
              --edge: var(--line); --edge-2: var(--line-2); }
footer.site .cols { display: grid; grid-template-columns: 2.4fr 1fr 1fr 1fr;
                    gap: 44px; margin-bottom: 52px; }
footer.site h4 { font-family: var(--mono); font-size: .66rem; letter-spacing: .16em;
                 text-transform: uppercase; color: var(--fg-mute); margin: 0 0 18px;
                 font-weight: 500; }
footer.site a { display: block; color: var(--fg-dim); margin-bottom: 11px;
                font-size: .9rem; transition: color .15s var(--fast); }
footer.site a:hover { color: var(--fg); }
/* The brand lockup is a flex row, not a stacked footer link — without this the
   generic rule above turns it into a block and the mark drops off the
   wordmark's baseline. */
footer.site .brand { display: inline-flex; margin-bottom: 0; }
footer.site .brand:hover { color: inherit; }
footer.site .about { color: var(--fg-mute); max-width: 300px; font-size: .89rem;
                     line-height: 1.7; margin-top: 18px; }
footer.site .base { border-top: 1px solid var(--edge); padding-top: 26px;
                    display: flex; justify-content: space-between; gap: 18px;
                    flex-wrap: wrap; color: var(--fg-faint); font-size: .82rem; }
footer.site .base .legalname { font-family: var(--mono); font-size: .74rem;
                               letter-spacing: .03em; }

/* ------------------------------------------------------------------ mobile */

/* A grid child's default min-width is auto, which means "as wide as my content"
   — so a long word or a wide table pushes the whole grid past the viewport and
   nothing below it lines up. Allowing children to shrink is what actually stops
   the sideways scroll; the breakpoints below only rearrange things. */
.hero-grid > *, .grid > *, .bento > *, .versus > *,
.split > *, .stakes > *, .plans > * { min-width: 0; }

@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; gap: 52px; }
  .grid.c3, .grid.c2, .plans { grid-template-columns: 1fr; }
  .bento { grid-template-columns: 1fr; }
  .bento .span2 { grid-column: span 1; }
  .versus { grid-template-columns: 1fr; }
  .split { grid-template-columns: 1fr !important; gap: 40px !important; }
  .stats { grid-template-columns: repeat(2,1fr); }
  footer.site .cols { grid-template-columns: 1fr 1fr; }
  nav.site a.hide-sm { display: none; }
  section { padding: 76px 0; }
  .hero { padding: 66px 0 60px; }

  /* Was 1fr auto 1fr with 36px side padding per cell — three columns plus
     216px of padding cannot fit a phone, and it was never collapsed. The
     arrow points down instead of across once the cells stack. */
  .stakes { grid-template-columns: 1fr; }
  .stakes > div { padding: 30px 24px; }
  .stakes .arrow { padding: 14px 0; border-left: none; border-right: none;
                   border-top: 1px solid var(--edge);
                   border-bottom: 1px solid var(--edge); }
  .stakes .arrow svg, .stakes .arrow span { transform: rotate(90deg); }
}

@media (max-width: 560px) {
  footer.site .cols { grid-template-columns: 1fr; }
  .stats { grid-template-columns: 1fr; }
  .wrap { padding: 0 20px; }
  h1 { font-size: 2.6rem; }

  /* Tables are the one thing that genuinely cannot reflow: a 3-column legal
     table needs more width than a phone has. The wrapper gets the scroll strip
     — putting overflow on the table itself leaves tbody/tr free to push past
     the edge, which is exactly what was still happening. body has
     overflow-x:hidden, so without this the last column is silently CLIPPED
     rather than scrollable. These are the pages a payment reviewer reads. */
  .table-scroll { display: block; width: 100%; max-width: 100%;
                  overflow-x: auto; -webkit-overflow-scrolling: touch;
                  border-radius: var(--r-sm); }
  .table-scroll > table.plain { min-width: 460px; margin: 0; }
  table.plain th, table.plain td { padding: 12px 13px; }
  table.plain td { overflow-wrap: anywhere; }

  /* THE header was the real overflow on every page: nav.site is a nowrap flex
     row, so "Your licences" sat past the right edge and widened the document
     by ~70px sitewide. Hiding .hide-sm links was not enough. Let the header
     wrap and shrink the gap instead. */
  header.site .inner { flex-wrap: wrap; gap: 10px; height: auto;
                       padding-top: 10px; padding-bottom: 10px; }
  nav.site { margin-left: auto; gap: 4px; flex-wrap: wrap;
             justify-content: flex-end; }
  nav.site a { padding: 7px 9px; font-size: .85rem; }
  .brand .name .product { display: none; }

  /* Touch targets. 44px is the accepted minimum; nav links and buttons were
     sized for a mouse pointer. */
  nav.site a, .btn { min-height: 44px; display: inline-flex;
                     align-items: center; }
  .btn.big { padding: 14px 22px; }

  /* Long unbroken strings — licence keys, URLs, email addresses — are a
     common source of overflow on narrow screens. */
  .mono, code, .term .body { overflow-wrap: anywhere; }

  h2 { font-size: 1.9rem; }
  section { padding: 56px 0; }
  .hero { padding: 48px 0 44px; }

  /* --- alignment, not just fit -------------------------------------------
     Things that are correct on a desktop read as broken on a phone: buttons
     that sit side by side at 40% width each, cards with desktop padding
     eating half the screen, right-aligned blocks stranded mid-column. */

  /* Button rows: full width and stacked, so neither is a cramped half.
     (hero-cta is the real container class — verified against the markup.) */
  .hero-cta { display: flex; flex-direction: column; align-items: stretch;
              gap: 10px; }
  .hero-cta .btn { width: 100%; }

  /* Cards and panels were padded for a 1200px canvas. */
  .card, .cell, .plan, .note, .term, .step { padding: 22px 20px; }

  /* .scanner is pinned right on desktop (max-width:380px; margin-left:auto).
     Once the hero is one column that leaves it stranded — square it up. */
  .scanner { max-width: 100%; margin-left: 0; margin-right: 0; }

  /* Media keeps its shape instead of stretching or overflowing. */
  img, svg, video, canvas { max-width: 100%; height: auto; }

  /* Headings were tuned with desktop tracking; at phone sizes tight negative
     letter-spacing makes them collide. */
  h1 { letter-spacing: -.03em; line-height: 1.08; }
  h2 { letter-spacing: -.02em; line-height: 1.18; }

  /* .center is used 17 times for desktop-centred copy. Centred text over a
     narrow column is hard to read — left-align it and let it breathe. */
  .center { text-align: left; }
  .section-head { margin-bottom: 38px; }

  /* Footer stacks; keep it left-aligned and let the blurb use full width. */
  footer.site .cols > * { text-align: left; }
  footer.site .about { max-width: none; }
}

/* Tablets and small laptops: the 980px rules collapse everything to one
   column, which wastes a 768–820px screen. Two columns fit comfortably. */
@media (min-width: 700px) and (max-width: 980px) {
  .grid.c3 { grid-template-columns: repeat(2, 1fr); }
  .bento { grid-template-columns: repeat(2, 1fr); }
  .plans { grid-template-columns: repeat(2, 1fr); }
  footer.site .cols { grid-template-columns: repeat(3, 1fr); }
  .scanner { max-width: 100%; margin-left: 0; }
}

/* Touch targets, on every device that is driven by a finger rather than a
   mouse — this is a pointer question, not a width question, so it must sit
   outside the width breakpoints. A tablet at 820px has the same fingers as a
   phone at 360px. 44px is the accepted minimum; these links were sized for a
   cursor. Padding is used rather than height so the text stays centred. */
@media (pointer: coarse) {
  nav.site a, footer.site a, .btn {
    min-height: 44px; display: inline-flex; align-items: center;
  }
  footer.site .cols a { padding: 6px 0; }
  /* Space between adjacent tap targets, so a fingertip cannot hit two. */
  footer.site .cols li + li, footer.site .cols a + a { margin-top: 2px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important;
    animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .rise { opacity: 1; transform: none; }
  /* Without this the blanket transition-duration override above would leave
     every reveal element stuck at opacity 0 - the page would look empty. */
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ARIA LOCK — green accents for the homepage lock slide (added 2026-08-23).
   The element carries both .shieldslide (for layout) and .lockslide (accent). */
.lockslide .shieldscanbar { background: linear-gradient(90deg,
    transparent, rgba(47,191,110,.55), transparent); }
.lockslide .shieldterm .sp { color: #2fbf6e; }
.lockslide .shieldterm .ss { color: #2fbf6e; }
.lockslide .chrome .conf.ok::before { background: #2fbf6e; }
