/* detectanyai: "the instrument"
 *
 * The concept, because every rule below follows from it: this is a measuring
 * device that reports its own uncertainty. Not a marketing page with a widget
 * on it. That's GOAL §6's position ("the detector that tells you how confident
 * it actually is") made visual, and it decides everything:
 *
 * 1. THE ACCENT IS AMBER, and that IS the argument. Not green, not red. A
 *    green/red readout is a binary verdict rendered in colour, and rendering
 *    one is precisely what COMPLIANCE §2 forbids. The dial is the same amber at
 *    4% as at 96%; only the number moves. A palette that cannot turn red cannot
 *    accuse anyone. It also looks like nothing else in a category of blue SaaS.
 * 2. ONE SUPERFAMILY, TWO ROLES. Chivo for voice, Chivo Mono for every measured
 *    thing: scores, prices, dates, the descriptor. A dial and its label should
 *    be the same typeface.
 * 3. SURFACES ARE RANKED, not uniform. Three depths (page / panel / instrument)
 *    with different borders and radii. The old version gave everything the same
 *    rounded rectangle, which is why it read as a form rather than a product.
 */

/* Self-hosted, subsetted, preloaded: see fonts/README.md for why and how. */
/* unicode-range must describe what the file ACTUALLY contains. Declare a range
   wider than the subset and the browser downloads the face for a character it
   doesn't have, then renders tofu instead of falling back to a system font that
   would have drawn it. These match static/fonts/README.md's subset commands. */
@font-face {
  font-family: 'Chivo';
  src: url('/static/fonts/chivo-latin.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
  /* Wide: this face renders the visitor's pasted text, not just our copy. */
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
                 U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122;
}
@font-face {
  font-family: 'Chivo Mono';
  src: url('/static/fonts/chivomono-latin.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
  /* Tight: this face only ever renders our own labels, scores and prices. */
  unicode-range: U+0020-007E, U+00A0, U+00A3, U+00A9, U+00B7, U+00B1, U+00D7,
                 U+20AC, U+2013, U+2018-2019, U+201C-201D, U+2022, U+2026;
}

:root {
  /* Ranked surfaces, not one panel colour repeated. */
  --bg:        #08090c;
  --surface-1: #0e1015;   /* panels */
  --surface-2: #14171e;   /* raised: the instrument */
  --surface-3: #1b1f28;   /* insets, chips */

  --ink:       #eceef2;
  --ink-2:     #a8b0be;   /* secondary prose: >=7.5:1 on every surface */
  /* Labels, axis, fine print. Chosen by computing the ratio against ALL FOUR
     surfaces, not by eye and not by what Lighthouse sampled: at #6f7889 this hit
     4.47:1 on --bg (which the audit caught) but 3.71:1 on --surface-3 (which it
     never checked, so it would have shipped). Now >=5:1 everywhere.
     This is not a score chase: `.fine` is the class carrying the billing fine
     print, and a disclosure nobody can read is not a disclosure. */
  --ink-3:     #858ea0;

  --line:      #1c2029;
  --line-2:    #262c38;
  --line-3:    #343c4c;

  /* The readout. See note 1. This is the colour of uncertainty, on purpose. */
  --amber:     #f2a93b;
  --amber-lo:  #7d5514;
  --on-amber:  #17120a;

  /* UI states only. Never used to characterise a score. */
  --warn: #ff6b6b;
  --ok:   #3ecf8e;

  /* Two widths, because one was the whole desktop problem. --maxw is a reading
     measure for prose and must stay near 680px (a 1100px paragraph is unreadable
     at any screen size). --wide is the composition width the hero and the grids
     use, so a 27" monitor gets a laid-out page instead of a phone column floating
     in the middle of it. */
  --maxw: 680px;
  --wide: 1120px;
  --r-sm: 6px;
  --r:    10px;
  --r-lg: 16px;

  --sans: 'Chivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --mono: 'Chivo Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Chivo's figures are already equal-width, so a column of percentages doesn't
     jitter as the digits change. This asks for it explicitly anyway: it costs
     nothing and it's the safety net if the face is ever swapped for one whose
     defaults are proportional. tests/test_landing.py asserts the property (equal
     advance widths) rather than this flag, because the flag is not the point. */
  --fig: 'tnum' 1;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 16px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Instrument texture: a hairline grid fading out under the fold. Pure gradient, no image request, no CLS, nothing on the LCP path. */
body::before {
  content: "";
  position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(var(--line) 1px, transparent 1px),
    linear-gradient(90deg, var(--line) 1px, transparent 1px);
  background-size: 56px 56px;
  opacity: .5;
  mask-image: radial-gradient(ellipse 120% 55% at 50% -5%, #000 0%, transparent 72%);
}
body > * { position: relative; z-index: 1; }

a { color: var(--amber); text-decoration: underline;
    text-underline-offset: 2px; text-decoration-thickness: 1px;
    text-decoration-color: color-mix(in srgb, var(--amber) 45%, transparent); }
a:hover { text-decoration-color: var(--amber); }
:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; border-radius: 2px; }

main { max-width: var(--maxw); margin: 0 auto; padding: 4px 20px 8px; }

/* Every measured thing. */
.mono, .seg-pct, .price-fig, .eyebrow, code, .gauge-val, .axis, .meta,
.counter, .rangebar-label { font-family: var(--mono); font-feature-settings: var(--fig); }
/* A code chip never breaks across lines. MY-AI-CHECK is the string a cardholder
   compares against their statement, character for character, and one split over
   two lines is one they can't match (COMPLIANCE §4). The same goes for the "---"
   separator on /batch: a rule someone has to retype must survive being read. */
code { white-space: nowrap; }

/* Section label: mono, tracked, with a rule running off to the right. */
.eyebrow {
  font: 500 10.5px/1 var(--mono);
  letter-spacing: .18em; text-transform: uppercase; color: var(--amber);
  margin: 0 0 12px; display: flex; align-items: center; gap: 10px;
}
.eyebrow::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--line-3), transparent);
}

/* ---------- header ---------- */
header.site {
  max-width: var(--wide); margin: 0 auto;
  padding: 18px 24px; display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  /* Above <main>, or the language menu can't escape. `body > *` gives header and
     main the same z-index, main paints later, and the readout panel (its own
     stacking context, courtesy of backdrop-filter) drew straight over the open
     dropdown. The menu's own z-index can't fix that: it's trapped inside this
     element's stacking context, so the lift has to happen here. */
  position: relative; z-index: 40;
}
/* One image, mark and wordmark together (static/logo.svg). The lockup can no
   longer drift, because there is nothing left to drift: the wordmark is outlines,
   not text in the body font.
   The trade, stated plainly: an <img> cannot inherit currentColor, so the ink is
   baked at #eceef2 and the logo is dark-background only. That is fine here (the
   site is dark, and print hides the header) and it is the thing to remember if a
   light surface ever needs it: regenerate, don't recolour with a filter. */
.brand { display: inline-flex; align-items: center; text-decoration: none; }
.brand:hover { text-decoration: none; }
/* Height drives it and width follows, so the aspect ratio comes from the file
   rather than from two numbers that can disagree with it. */
.brand .logo { display: block; height: 24px; width: auto; }
header nav { display: flex; gap: 14px; font-size: 13px; align-items: center; }

/* ---------- language control ---------- */
.langpick { position: relative; }
.langpick summary {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  list-style: none; color: var(--ink-2);
  font: 500 12px/1 var(--mono); letter-spacing: .06em;
  padding: 7px 9px; border: 1px solid var(--line-2); border-radius: 8px;
  transition: border-color .15s ease, color .15s ease;
}
.langpick summary::-webkit-details-marker { display: none; }
.langpick summary:hover { color: var(--ink); border-color: var(--line-3); }
.langpick[open] summary { color: var(--ink); border-color: var(--line-3); }
.langmenu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 30;
  min-width: 210px; padding: 8px;
  border: 1px solid var(--line-3); border-radius: var(--r);
  /* Near-opaque, not glass. The readout panel behind this one ALSO has a
     backdrop-filter, and nested backdrop-filters don't compose: the dial showed
     straight through the menu and the language names were unreadable. Glass is
     for the surfaces you look at, not the ones you read a list in. The blur is
     kept for the edge treatment where it lands over plain page. */
  background: color-mix(in srgb, var(--surface-2) 98%, transparent);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
  box-shadow: 0 24px 50px -12px rgba(0,0,0,.9), 0 0 0 1px rgba(0,0,0,.4);
}
.langmenu-head, .langmenu-foot {
  font: 500 10px/1.5 var(--mono); letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3); margin: 4px 8px 8px;
}
.langmenu-foot {
  text-transform: none; letter-spacing: 0; font-size: 11px;
  margin: 8px 8px 4px; padding-top: 8px; border-top: 1px solid var(--line);
}
.langopt {
  width: 100%; margin: 0; padding: 8px 9px; border: 0; border-radius: 6px;
  background: transparent; color: var(--ink); cursor: pointer;
  font: 400 13.5px/1 var(--sans); text-align: left;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.langopt:hover { background: var(--surface-3); filter: none; }
.langopt.is-current { color: var(--ink); }
.langopt .tick { color: var(--amber); flex: none; }
header nav a { color: var(--ink-2); text-decoration: none; }
header nav a:hover { color: var(--ink); text-decoration: underline; }
/* Cancel is prominent in the header by rule, not by taste (COMPLIANCE §1.4).
   nowrap because a pill that breaks into two lines stops looking like a control
   and starts looking like an accident, and this is the control the whole
   easy-cancel promise rests on. */
header nav a.cancel {
  color: var(--ink); border: 1px solid var(--line-3);
  padding: 6px 11px; border-radius: 999px; font-size: 12.5px;
  white-space: nowrap;
}
header nav a.cancel:hover { border-color: var(--amber); text-decoration: none; }

/* ---------- hero ---------- */
/* Mobile first: one column, copy then readout. The desktop composition is at the
   bottom of this file. */
.hero { padding: 14px 0 0; display: grid; gap: 20px; }
/* One line at 320-340px is a hard rule (GOAL §6), which is why the H1 is short
   enough to look blunt. It's also the exact-match keyword, which buys the
   ad-relevance half of Quality Score. Both constraints want the same words. */
.hero h1 {
  font-size: clamp(34px, 10.5vw, 46px);
  line-height: 1.02; letter-spacing: -.045em; font-weight: 900;
  margin: 0 0 14px; text-wrap: balance;
}
.hero .sub {
  color: var(--ink-2); margin: 0 0 24px; font-size: 16.5px; max-width: 44ch;
}
.hero .sub strong { color: var(--ink); font-weight: 600; }

/* ---------- the specimen (input) ---------- */
/* A bay you place something in, not a form field: a labelled rail, a live count,
   and the action inside the panel. Glass rather than a flat box, with the blur
   picking up the instrument grid behind it so the panel sits IN the page instead
   of on top of it. backdrop-filter degrades to the flat background where it isn't
   supported, which is a cosmetic loss and nothing more. */
.specimen {
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--surface-2) 70%, transparent),
    color-mix(in srgb, var(--surface-1) 82%, transparent));
  backdrop-filter: blur(18px) saturate(1.25);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset,
              0 14px 40px -22px rgba(0,0,0,.9);
  overflow: hidden;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.specimen:focus-within {
  border-color: var(--line-3);
  box-shadow: 0 1px 0 rgba(255,255,255,.06) inset,
              0 0 0 3px color-mix(in srgb, var(--amber) 12%, transparent),
              0 14px 40px -22px rgba(0,0,0,.9);
}
.specimen-rail, .specimen-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 12px 9px 14px;
}
.specimen-rail { border-bottom: 1px solid var(--line); }
.specimen-foot { border-top: 1px solid var(--line); }
.specimen-rail .label {
  font: 500 10px/1 var(--mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.counter { font-size: 11.5px; color: var(--ink-3); transition: color .15s ease; }
.counter.ready { color: var(--amber); }
textarea {
  display: block; width: 100%; background: transparent; color: var(--ink);
  border: 0; padding: 15px 14px; font: 400 15.5px/1.65 var(--sans);
  resize: vertical; min-height: 168px;
}
textarea:focus { outline: none; }
textarea::placeholder { color: #626c80; }

/* The inline action. A real <button> for keyboard and screen readers, styled as
   a control rather than a slab. It only fills in once the specimen is long
   enough to read, so the panel reports its own state instead of waiting to
   reject you. */
button.inline-cta {
  width: auto; margin: 0; flex: none;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 12px; border-radius: 8px;
  background: transparent; color: var(--ink-3); border: 1px solid transparent;
  font: 600 13.5px/1 var(--sans);
  transition: background .18s ease, color .18s ease, border-color .18s ease;
}
button.inline-cta svg { transition: transform .18s ease; }
button.inline-cta:hover { filter: none; }
.specimen.ready button.inline-cta {
  background: var(--amber); color: var(--on-amber);
}
.specimen.ready button.inline-cta:hover svg { transform: translateX(2px); }
.specimen:not(.ready) button.inline-cta { cursor: default; }
#specimen-hint { min-width: 0; }

input[type=email], input[type=text] {
  width: 100%; background: var(--surface-1); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r);
  padding: 13px 14px; font: inherit;
}
label { display: block; margin-top: 14px; font-size: 14px; color: var(--ink-2); }

/* ---------- buttons ---------- */
button {
  width: 100%; margin-top: 12px; padding: 15px 18px;
  font: 700 15.5px/1 var(--sans); letter-spacing: -.01em;
  color: var(--on-amber); background: var(--amber); border: 0;
  border-radius: var(--r); cursor: pointer;
  transition: filter .12s ease, transform .08s ease;
}
button:hover { filter: brightness(1.07); }
button:active { transform: translateY(1px); }
button.ghost {
  background: transparent; color: var(--ink); border: 1px solid var(--line-3);
}
button.danger { background: transparent; color: var(--warn); border: 1px solid var(--warn); }
.err { color: var(--warn); margin-top: 10px; font-size: 14.5px; }
.cf-turnstile { margin-top: 12px; }

/* ---------- generic panels (report/account/legal/cancel) ---------- */
.report, .account, .legal, .cancel-page {
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 22px; margin-top: 20px;
}
.legal h1, .report h1, .account h1, .cancel-page h1 {
  font-size: 27px; line-height: 1.15; letter-spacing: -.035em;
  margin: 0 0 10px; font-weight: 800; text-wrap: balance;
}
.legal .sub, .report .sub, .cancel-page .sub { color: var(--ink-2); margin: 0 0 18px; }

/* ---------- the readout (teaser) ---------- */
/* The raised surface: this is the instrument, and it should sit above the page
   rather than beside it. */
.teaser {
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--surface-2) 80%, transparent),
    color-mix(in srgb, var(--surface-1) 90%, transparent));
  backdrop-filter: blur(18px) saturate(1.25);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset, 0 18px 48px -22px rgba(0,0,0,.9);
  overflow: hidden;
}
/* Only the FILLED readout animates in. The idle one is there from first paint,
   so animating it would be motion for its own sake and a CLS risk. */
.teaser:not(.is-idle) { animation: rise .3s cubic-bezier(.2,.7,.3,1); }
@keyframes rise { from { opacity: .6; transform: translateY(6px); } }

/* ---------- the idle readout ---------- */
/* Visible before anything is submitted, so a cold visitor can see what they're
   about to get instead of a form and a wall of text. It is EMPTY, never fake:
   every value is a dash, the dial reads "··" rather than 0, and nothing here is
   a number we did not produce. A greyed-out placeholder score would still be a
   fabricated result. */
.teaser.is-idle { opacity: .92; }
.teaser.is-idle .dial { opacity: .5; }
.teaser.is-idle .gauge-val { fill: var(--ink-3); }
.idle-note {
  margin: 0; padding: 0 16px 18px; text-align: center;
  font-size: 14px; color: var(--ink-3); line-height: 1.55; text-wrap: balance;
}
.teaser:not(.is-idle) .idle-note { display: none; }
.segments li.skeleton .seg-bar { background: var(--line); }
.segments li.skeleton .seg-pct,
.segments li.skeleton .seg-i { color: var(--line-3); }
.segments li.skeleton .seg-text {
  height: 8px; border-radius: 3px; background: var(--line);
  align-self: center; opacity: .8;
}
.segments li.skeleton:nth-child(2) .seg-text { width: 76%; }
.segments li.skeleton:nth-child(3) .seg-text { width: 54%; }
.teaser:not(.is-idle) .segments li.skeleton { display: none; }

.readout-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
  background: rgba(0,0,0,.25);
}
.readout-head .label {
  font: 500 10px/1 var(--mono); letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.meta { font-size: 11px; color: var(--ink-3); }

.dial-wrap { padding: 18px 14px 4px; display: grid; place-items: center; }
.dial { width: 100%; max-width: 260px; height: auto; display: block; }
/* The arc is one hue at every score. A dial that greens at 5% and reds at 95%
   has delivered a verdict without writing one. */
.dial .track { stroke: var(--line-2); }
.dial .value { stroke: var(--amber); transition: stroke-dashoffset .5s cubic-bezier(.2,.7,.3,1); }
.dial .tick  { stroke: var(--line-3); }
.dial .tick.major { stroke: var(--ink-3); }
.dial .axis  { fill: var(--ink-3); font-size: 9px; letter-spacing: .06em; }
.gauge-val {
  fill: var(--ink); font-size: 38px; font-weight: 700; letter-spacing: -.04em;
}
.gauge-pct { fill: var(--ink-3); font-size: 15px; font-weight: 500; }
.gauge-cap {
  fill: var(--ink-3); font-size: 8px; letter-spacing: .16em; font-weight: 500;
}

.verdict {
  font-size: 17px; font-weight: 600; text-align: center; margin: 2px 0 4px;
  letter-spacing: -.02em; line-height: 1.35; text-wrap: balance;
  padding: 0 14px;
}
.hook { color: var(--ink-2); text-align: center; margin: 0; font-size: 14.5px;
        padding: 0 14px 16px; }

/* Real spread, from the real sentence distribution, never an invented ±. */
.rangebar {
  margin: 0 14px 16px; padding: 11px 12px; border-radius: var(--r-sm);
  background: var(--surface-3); border: 1px solid var(--line);
}
.rangebar-label {
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 8px; display: flex;
  justify-content: space-between; gap: 8px;
}
.rangebar-track {
  position: relative; height: 4px; border-radius: 2px; background: var(--line-2);
}
.rangebar-fill {
  position: absolute; top: 0; height: 4px; border-radius: 2px;
  background: linear-gradient(90deg, var(--amber-lo), var(--amber));
}

/* ---------- per-sentence chart ---------- */
/* A chart, not a list of cards: aligned index, bar, number, text. The eye should
   be able to run down the bars and find the signal without reading. */
.segments { list-style: none; padding: 0 14px 6px; margin: 0; }
.segments li {
  display: grid; grid-template-columns: 20px 46px 40px 1fr;
  align-items: baseline; gap: 10px;
  padding: 9px 0; border-top: 1px solid var(--line);
}
.seg-i { font: 400 10.5px/1.6 var(--mono); color: var(--ink-3);
         font-feature-settings: var(--fig); }
.seg-bar { height: 5px; border-radius: 1px; background: var(--line-2);
           position: relative; top: -2px; overflow: hidden; }
.seg-bar i { display: block; height: 100%; background: var(--amber-lo); }
.segments li.flag .seg-bar i { background: var(--amber); }
.seg-pct { font-size: 12.5px; font-weight: 500; color: var(--ink-2); }
.segments li.flag .seg-pct { color: var(--amber); }
.seg-text { color: var(--ink-2); font-size: 14px; line-height: 1.5; }
.segments li.flag .seg-text { color: var(--ink); }
/* There are no locked rows any more. Every row is a real, scored row: the free
   result is the whole result, and the gate meters how much and how often
   (GOAL §3). What survives is the meter note, which says how much of a long
   submission was actually read. */
.meter-note {
  font: 400 12.5px/1.5 var(--mono); color: var(--ink-3);
  padding: 10px 14px 0; margin: 0; border-top: 1px solid var(--line);
  display: flex; align-items: baseline; gap: 8px;
}
.meter-note::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--amber); flex: none; position: relative; top: -3px;
}
/* Why the pay block is on screen. Sits ABOVE the disclosure, because "why am I
   being asked to pay" comes before "here are the terms". */
.upgrade-why {
  margin: 0 0 12px; font-size: 14.5px; font-weight: 600; color: var(--ink);
}

/* ---------- pay block ---------- */
.pay { margin: 14px; padding: 16px; border-radius: var(--r);
       background: var(--bg); border: 1px solid var(--line-2); }
.disclose { font-size: 14.5px; line-height: 1.5; margin: 0 0 10px; }
code, .disclose code {
  font-size: 12.5px; background: var(--surface-3); padding: 2px 6px;
  border-radius: 4px; border: 1px solid var(--line-2); color: var(--ink);
  letter-spacing: .02em;
}
/* The billing disclosure is legally load-bearing, never shrink it below the
   body text around it, and never grey it out (COMPLIANCE §1.1 "conspicuous"). */
.disclose-list { font-size: 14px; line-height: 1.65; margin: 0; padding-left: 18px;
                 color: var(--ink-2); }
.disclose-list li { margin-bottom: 5px; }
.disclose-list strong, .disclose strong { color: var(--ink); }
.pay input[type=email] { margin-top: 14px; }
.pay .fine { margin: 10px 0 0; }
.fine { font-size: 12.5px; color: var(--ink-3); line-height: 1.55; }

/* ---------- report ---------- */
.report .segments { padding-left: 0; padding-right: 0; }
.report-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.report-actions button, .btn-link { width: auto; display: inline-block; margin-top: 0; }
.btn-link { padding: 13px 18px; border: 1px solid var(--line-3);
            border-radius: var(--r); color: var(--ink); text-decoration: none; }
.btn-link:hover { border-color: var(--amber); text-decoration: none; }

/* ---------- landing bands ---------- */
.band { margin-top: 52px; }
/* Clamped, because the one-line rule (GOAL §6) binds section headings too and
   26px x ~22 chars does not fit 280px of usable width at 320px. The copy is kept
   inside ~22 chars as well. The type and the words have to agree, and relying on
   only one of them is how a heading silently wraps on the cheapest phone. */
.band h2 {
  font-size: clamp(22px, 6.6vw, 26px); line-height: 1.12; letter-spacing: -.035em;
  margin: 0 0 10px; font-weight: 800; text-wrap: balance;
}
.band > p { color: var(--ink-2); margin: 0 0 18px; }

/* benefit grid: hairline-ruled cells, not floating cards */
.grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--line); border: 1px solid var(--line);
  border-radius: var(--r); overflow: hidden;
}
.card { background: var(--surface-1); padding: 16px; }
.card h3 {
  font: 500 10px/1 var(--mono); letter-spacing: .14em; text-transform: uppercase;
  color: var(--amber); margin: 0 0 9px;
}
.card p { margin: 0; font-size: 14px; color: var(--ink-2); line-height: 1.55; }
.card p strong { color: var(--ink); font-weight: 600; }

/* how it works */
.steps { list-style: none; padding: 0; margin: 0; counter-reset: step; }
.steps li {
  display: flex; gap: 14px; padding: 15px 0; border-top: 1px solid var(--line);
  counter-increment: step;
}
.steps li::before {
  content: counter(step, decimal-leading-zero);
  font: 500 11px/1.7 var(--mono); color: var(--amber); flex: none;
  font-feature-settings: var(--fig);
}
.steps p { margin: 0; font-size: 14.5px; color: var(--ink-2); }
.steps strong { color: var(--ink); font-weight: 600; display: block; margin-bottom: 2px; }

/* methodology. The honesty band, deliberately the plainest thing here */
.honest {
  border: 1px solid var(--line-2); border-left: 2px solid var(--amber);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--surface-1); padding: 18px 18px 18px 20px;
}
.honest > p:first-child { margin-top: 0; color: var(--ink-2); }
.honest ul { margin: 12px 0 0; padding-left: 18px; }
.honest li { font-size: 14.5px; color: var(--ink-2); margin-bottom: 8px; line-height: 1.55; }
.honest li strong { color: var(--ink); font-weight: 600; }

/* pricing */
.prices { display: grid; gap: 1px; background: var(--line);
          border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.price-card {
  background: var(--surface-1); padding: 16px;
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
}
.price-card.now { background: var(--surface-2); }
.price-fig {
  font-size: 20px; font-weight: 700; letter-spacing: -.03em;
  flex: none; min-width: 70px; color: var(--ink-2);
}
.price-card.now .price-fig { color: var(--amber); }
.price-body { flex: 1 1 190px; }
.price-body h3 { margin: 0 0 3px; font-size: 14.5px; font-weight: 700; }
.price-body p { margin: 0; font-size: 13.5px; color: var(--ink-2); line-height: 1.5; }

/* FAQ: <details>, so it works with JS off and costs nothing to render */
.faq { border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  cursor: pointer; padding: 15px 0; font-weight: 600; font-size: 15px;
  list-style: none; display: flex; justify-content: space-between; gap: 12px;
  letter-spacing: -.01em;
}
.faq summary::-webkit-details-marker { display: none; }
/* A rotated "+" rather than a minus glyph: U+2212 is not in Chivo, so it
   rendered as tofu. This needs no glyph that might not exist and animates. */
.faq summary::after {
  content: "+"; color: var(--amber); font-family: var(--mono); font-weight: 400;
  flex: none; transition: transform .18s ease; transform-origin: 50% 50%;
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq details p { margin: 0 0 15px; font-size: 14.5px; color: var(--ink-2);
                 max-width: 60ch; line-height: 1.6; }

/* ---------- cancel / account / legal ---------- */
.cancel-page h2 { font-size: 16px; margin: 24px 0 6px; }
.status { font-size: 17px; font-weight: 700; font-family: var(--mono);
          font-feature-settings: var(--fig); }
.status.active { color: var(--ok); }
.status.canceled { color: var(--ink-3); }
.legal h2 { font-size: 16.5px; margin: 24px 0 6px; letter-spacing: -.01em; }
.legal p, .legal li { color: var(--ink-2); }
.legal ul { padding-left: 20px; }

/* ---------- footer ---------- */
footer.site {
  max-width: var(--maxw); margin: 56px auto 0; padding: 24px 20px 44px;
  text-align: center; color: var(--ink-3); border-top: 1px solid var(--line);
}
footer nav { display: flex; gap: 8px 18px; justify-content: center;
             flex-wrap: wrap; margin-bottom: 14px; font-size: 13.5px; }
footer nav a { color: var(--ink-2); text-decoration: none; }
footer nav a:hover { color: var(--ink); text-decoration: underline; }

@media (max-width: 420px) {
  .grid { grid-template-columns: 1fr; }
  .segments li { grid-template-columns: 16px 34px 36px 1fr; gap: 8px; }
  header.site { padding: 14px 16px; }
  /* The whole lockup shrinks together now, rather than the wordmark alone
     changing size next to a fixed mark. Measured, not guessed: at 24px the logo
     is 170px wide, and logo + language + Cancel does not fit 320 once "Cancel
     anytime" is forbidden from wrapping (the nav ran to 340px on a 320 viewport
     and the page scrolled sideways). */
  .brand .logo { height: 19px; }
  header nav { gap: 8px; }
  /* Leaves "Cancel". See base.html: the control stays, the adverb goes. */
  .at-wide { display: none; }
  /* The hint and the button share a row, which leaves the hint about 150px at
     320px: no sentence worth writing fits that, so it wrapped. Give it the full
     width and let the button drop below, rather than shortening the sentence
     into uselessness. */
  .specimen-foot { flex-wrap: wrap; }
  .specimen-foot .fine { flex: 1 0 100%; }
  .specimen-foot .inline-cta { margin-left: auto; }
  /* Brand + language + Account + Cancel does not fit 320px: the nav ran to
     357px and the page scrolled sideways. Cancel is the one that cannot go
     (COMPLIANCE §1.4 wants it prominent in the header), and Account is the one
     that can: it's in the footer, and anyone with a subscription reaches it from
     their report and from every email we send. */
  header nav a.acct { display: none; }
  .langmenu { min-width: 0; width: max-content; max-width: calc(100vw - 28px); }
}

/* ---------- desktop ----------
   Everything above is the phone. Without this block the site was a 680px column
   centred in a 1440px window, which is what "feels like a mobile site on a
   desktop" actually means: no composition, just a narrow strip of leftovers.

   The hero becomes two columns and the readout moves up beside the input, so the
   instrument is visible next to the thing that feeds it and the fold does real
   work. Prose bands keep the 680px reading measure regardless of window width,
   because a 1100px line length is unreadable at any screen size. */
@media (min-width: 900px) {
  main { max-width: var(--wide); padding: 12px 24px 8px; }

  .hero {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 40px; align-items: start; padding: 32px 0 8px;
  }
  .hero-copy { padding-top: 8px; }
  .hero h1 { font-size: clamp(46px, 4.2vw, 58px); }
  .hero .sub { font-size: 17.5px; margin-bottom: 28px; }
  /* The readout follows down the page rather than scrolling away from the input
     it belongs to. */
  .hero-readout { position: sticky; top: 24px; }
  .dial { max-width: 300px; }
  textarea { min-height: 210px; }

  /* Prose keeps its measure; the page does not. */
  .band { max-width: var(--maxw); margin-left: auto; margin-right: auto; }
  .band.full { max-width: var(--wide); }
  .band.full .grid { grid-template-columns: repeat(4, 1fr); }
  .band.full .prices { grid-template-columns: repeat(3, 1fr); }
  .band.full .steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
  .band.full .steps li { border-top: 1px solid var(--line-3); padding-top: 14px; }

  footer.site { max-width: var(--wide); }
  header.site { padding: 22px 24px; }
}

/* The reveal is decorative. Honor the OS setting. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

@media print {
  header.site, footer.site, .report-actions, .pay, body::before { display: none; }
  body { background: #fff; color: #000; }
  .report { border: 0; background: #fff; }
  .seg-text, .segments li, .seg-pct { color: #000; }
}

/* Arrow links. The arrows are inline SVG rather than characters: U+2192/U+2190
   are not in Chivo at any weight, so they rendered as tofu. */
.arrow-link a { display: inline-flex; align-items: center; gap: 6px; }
.arrow-link a svg { flex: none; transition: transform .18s ease; }
.arrow-link a:hover svg { transform: translateX(2px); }
.arrow-link.back a:hover svg { transform: translateX(-2px); }

/* Dial: idle vs live centre. Swapped as whole groups so the idle state needs no
   placeholder number and no stand-in glyph. */
.teaser.is-idle .dial .on-live { display: none; }
.teaser:not(.is-idle) .dial .on-idle { display: none; }
.gauge-idle {
  fill: var(--ink-3); font-family: var(--mono); font-size: 10px;
  font-weight: 500; letter-spacing: .18em;
}

/* The line under the pay button. Sits BELOW it, never between the disclosure
   and the button: nothing may come between the terms and the control that
   consents to them (COMPLIANCE §1.1). */
.pay-note { margin: 10px 0 0; text-align: center; }

/* ---------- batch: the sorted table (ROADMAP §3.6a) ----------
   Not a grid of dials. The dial answers "how confident on this one", which is
   the wrong question thirty times over; the question here is "which do I read
   first", and the only honest instrument for that is a ranked list you can scan
   without moving your eyes off a column.

   Note what has NO colour ramp: the confidence column is the same amber at 4% as
   at 96%, exactly like the dial (see note 1 at the top of this file). A table
   that turned rows red at the top would be a binary verdict rendered as a
   heatmap, and rendering one is what COMPLIANCE §2 forbids. Only the number and
   the bar length move. */
.batch, .batch-new {
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 22px; margin-top: 20px;
}
.batch h1, .batch-new h1 {
  font-size: 27px; line-height: 1.15; letter-spacing: -.035em;
  margin: 0 0 10px; font-weight: 800; text-wrap: balance;
}
.batch .sub, .batch-new .sub { color: var(--ink-2); margin: 0 0 18px; }
.batch-new code {
  font-family: var(--mono); background: var(--surface-3); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: 4px; padding: 1px 6px;
  font-size: .9em;
}

/* The warning. Amber-bordered and full-width because it is not an aside in the
   editorial sense: it is the condition on which the table below it may be used.
   It sits ABOVE the table for the same reason the billing disclosure sits above
   the pay button (COMPLIANCE §1.1): after the fact is not disclosure. */
.warn {
  background: var(--surface-2); border: 1px solid var(--amber-lo);
  border-left: 3px solid var(--amber); border-radius: var(--r-md);
  padding: 16px 18px; margin: 0 0 22px;
}
.warn h2 {
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--amber);
  margin: 0 0 8px;
}
.warn p { color: var(--ink-2); font-size: 14.5px; line-height: 1.6; margin: 0 0 8px; }
.warn p:last-child { margin-bottom: 0; }
.warn strong { color: var(--ink); }

/* A table on a phone is the hard case, and the honest answer is that it should
   not be a table there. At 320px a five-column table gives you two bad options:
   scroll it sideways, in which case the names scroll away from the scores (a
   ranked list of anonymous numbers, which is worse than no table), or crush every
   column to two characters. So, phone-first like the rest of this file: each
   document is a CARD that needs no scrolling and keeps the name and its score in
   the same eyeline, which is the only pairing on this screen that matters. The
   table reappears at 760px, where it fits and is genuinely better to scan.

   The <table> MARKUP stays at every width. The data is tabular, a screen reader
   should hear it announced that way, and `display` doesn't change that. This is
   a layout choice, not a semantic one. */
/* `position: relative` is load-bearing, not habit: the .sr-only span inside this
   table is absolutely positioned, and without a positioned ancestor it resolves
   against the initial containing block, escapes this box, and drags the whole
   PAGE out to the table's full width. Measured: 605px of body on a 320px
   viewport. overflow-x is the safety net for the table layout at >=760px; the
   phone layout is cards and has nothing to scroll. */
.table-wrap { position: relative; overflow-x: auto; }
.batch-table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.batch-table thead { display: none; }
.batch-table tbody, .batch-table tr, .batch-table td, .batch-table th { display: block; }
.batch-table tbody tr {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "seq label open" "seq score flag";
  gap: 8px 10px; align-items: center;
  padding: 14px 2px; border-bottom: 1px solid var(--line);
}
.batch-table tbody tr:last-child { border-bottom: 0; }
.batch-table th, .batch-table td { text-align: left; }
.col-seq   { grid-area: seq; }
.col-label { grid-area: label; }
.col-score { grid-area: score; }
.col-flag  { grid-area: flag; }
.col-open  { grid-area: open; }
/* The column header is hidden in this layout, so the number has to say what it
   counts. Without it "1 / 3" next to a percentage is just a second mystery score. */
.col-flag::after { content: " flagged"; }

.col-seq {
  font-family: var(--mono); color: var(--ink-3); font-size: 12.5px;
  white-space: nowrap;
}
.col-label { font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.col-score { display: flex; align-items: center; gap: 10px; }
.col-score .pct {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--amber); font-size: 13.5px;
}
.minibar {
  flex: 1; min-width: 60px; height: 5px; border-radius: 1px;
  background: var(--line-2); overflow: hidden;
}
.minibar i { display: block; height: 100%; background: var(--amber-lo); }
.col-flag {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--ink-2); font-size: 13px; white-space: nowrap;
}
/* Nothing flagged: recede it. The eye should be pulled by the rows that have
   something in them, and never by colour standing in for a verdict. */
.col-flag.is-zero { color: var(--ink-3); }

/* Wide enough for the real thing. Five columns, scannable down each one, which
   is the point of a table and the reason it comes back the moment it fits. */
@media (min-width: 760px) {
  .batch-table thead { display: table-header-group; }
  .batch-table tbody { display: table-row-group; }
  .batch-table td, .batch-table th { display: table-cell; }
  /* The header row needs restoring too, and forgetting it is worse than it
     sounds: table-cells inside a display:block row get wrapped in an ANONYMOUS
     table of their own, so the headers silently size to their own columns and
     stop lining up with the data underneath. A table whose headings point at the
     wrong columns is a mislabelled score. */
  .batch-table thead tr { display: table-row; }
  /* Selector matched to the phone rule character for character, on purpose. A
     media query adds NO specificity, so a tidier `.batch-table tr` here (0,1,1)
     silently loses to `.batch-table tbody tr` above (0,1,2): the rows stayed grid
     at every width and the cells rendered stacked on top of one another. And the
     areas must be cleared explicitly, since display:table-row does not undo them. */
  .batch-table tbody tr {
    display: table-row; grid-template-areas: none; grid-template-columns: none;
    gap: 0; padding: 0; border-bottom: 0;
  }
  .batch-table th, .batch-table td {
    padding: 12px 10px; border-bottom: 1px solid var(--line); vertical-align: middle;
  }
  .batch-table tbody tr:last-child th, .batch-table tbody tr:last-child td {
    border-bottom: 0;
  }
  .batch-table thead th {
    font-family: var(--mono); font-size: 11px; font-weight: 500;
    letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3);
    border-bottom: 1px solid var(--line-3); white-space: nowrap;
  }
  .batch-table tbody tr:hover { background: var(--surface-2); }
  .col-flag::after { content: none; }
  .col-seq { width: 1%; }
  .col-label { min-width: 150px; max-width: 340px; }
  .col-score { width: 34%; min-width: 150px; display: table-cell; }
  /* Back to a table cell, so the flex sizing above stops applying and the bar
     can be a plain inline box next to its number again. */
  .col-score .minibar { display: inline-block; width: calc(100% - 52px); }
  .col-score .pct { margin-left: 10px; }
}

.batch-new .upsell {
  background: var(--surface-2); border: 1px solid var(--line-2);
  border-radius: var(--r-md); padding: 18px;
}
.batch-new .upsell p { color: var(--ink-2); margin: 0 0 12px; }
.batch-new .upsell .billing-line {
  font-family: var(--mono); font-size: 13px; color: var(--ink);
}
.batch-new .upsell p:last-child { margin-bottom: 0; }

/* Present for screen readers, absent for everyone else. The Open column's header
   would otherwise be an empty th, which a screen reader announces as nothing. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media print {
  .warn { border-color: #000; background: #fff; }
  .warn h2, .warn p, .warn strong { color: #000; }
  .batch { border: 0; background: #fff; }
  .batch-table th, .batch-table td, .col-seq, .col-flag, .col-score .pct { color: #000; }
}

/* ---------- upload (§3.6b) ----------
   A <label> wrapping a hidden <input type=file>, which is what makes the whole
   panel clickable while the control stays a real file input: keyboard reachable,
   announced properly, and working with JS off. A <div onclick> would look the
   same and be none of those things. */
.dropzone {
  display: block; text-align: center; cursor: pointer;
  background: var(--surface-2); border: 1px dashed var(--line-3);
  border-radius: var(--r); padding: 22px 18px; margin: 0 0 14px;
  transition: border-color .15s ease, background .15s ease;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--amber); background: var(--surface-3); }
.dropzone.has-files { border-style: solid; border-color: var(--amber-lo); }
/* The input is hidden, so the ring has to be drawn by its label or the control
   is invisible to anyone tabbing through the page. */
.dropzone:focus-within { outline: 2px solid var(--amber); outline-offset: 2px; }
.dz-main { display: block; font-size: 15px; color: var(--ink); }
.dz-main u { text-decoration-color: var(--amber); text-underline-offset: 3px; }
.dz-sub {
  /* 11.5px, not 12.5: at 320px this box has about 200px of room, and the line
     has to hold on one. Still above the 11px the section labels use. */
  display: block; margin-top: 5px; font-size: 11.5px; color: var(--ink-3);
  font-family: var(--mono);
}
.dz-picked {
  display: block; margin-top: 10px; font-size: 12.5px; color: var(--amber);
  font-family: var(--mono); overflow-wrap: anywhere;
}
/* The "or" divider, with rules either side. */
.or {
  display: flex; align-items: center; gap: 12px; margin: 0 0 14px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3);
}
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line-2); }

/* ---------- error pages ----------
   Same panel as the report, because an error is not a different product: it is
   this product, on a bad day, and a visitor who has just paid should not feel
   like they fell out of the site. */
.error-page {
  background: var(--surface-1); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 22px; margin-top: 20px;
}
.error-page h1 {
  font-size: 27px; line-height: 1.15; letter-spacing: -.035em;
  margin: 0 0 10px; font-weight: 800; text-wrap: balance;
}
.error-page .sub { color: var(--ink-2); margin: 0 0 14px; }
.error-page p { color: var(--ink-2); }
.error-page strong { color: var(--ink); }
