/*
  The ordering layer's styles. One stylesheet, no framework, no build step —
  CLAUDE.md convention 9: the web app adds its own simple templates, still no
  frontend frameworks.

  TOKENS ARE COPIED FROM `report/templates/report.html.j2`, NOT FROM
  `design/claude_design/`. The prototype is authoritative for the report's
  layout and copy, but its PALETTE has drifted: the prototype is sand and green
  (--bg #F6F4EE, --brand #1E6B4F) and the built report is cool grey and blue
  (--bg #F8F9FB, --brand #2563EB). These pages sit in front of that report and
  a client meets them minutes before they open it, so they follow what the
  report actually renders. If the report's palette moves again, this block is
  what has to move with it.

  Copied, never imported: the report is one standalone file that must work
  saved to a desktop, and a stylesheet served from here would tie the
  deliverable to the server.

  Contrast follows the report's rule — 4.5:1 for body text, 3:1 for large
  text. --muted (#4B5563) and --faint (#667085) both clear 4.5 on the card AND
  on the page beneath it, which is the constraint the report's own comment
  records for --faint.
*/

/* THE PRODUCT'S ONE WEBFACE, HERE SINCE 2026-09-07 BECAUSE HERE IS WHERE
   EVERY PAGE CAN SEE IT. It lived in `landing-hero.css`, which only `/`
   loads, so `/order` and the six status pages asked for "Inter" and were
   served no face — the whole ordering layer fell back to the system stack
   for anyone without Inter installed locally. Found by measuring
   `document.fonts` on each page rather than by reading the stacks, which
   all looked right.

   Latin only, `swap`, and a real fallback stack behind every use of it. The
   CSP names no third party: the face is served from this origin or it does
   not ship. The licence sits beside the file in `static/fonts/`. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/static/fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* neutrals */
  /* #F0F6FB SINCE 2026-09-06, COPIED FROM THE REPORT as every token here is.
     The report's page ground went from a cool grey to this blue wash (option F
     of `design/page-hues.html`), and
     `test_the_web_palette_matches_the_report_it_sits_in_front_of` is the guard
     that made this file follow: a client meets the waiting page minutes before
     they open the report, and the two must not be two different papers.

     WHAT IT REACHES TODAY IS NOTHING A CLIENT SEES, and that is worth writing
     down rather than assuming. Every served page now sets its own ground:
     `body:has(.lh)` in `landing-hero.css`, `body:has(.lp)` in `landing.css`,
     `body:has(.orderpage)` in `order.css`, and `body:has(.status)` in
     `status.css` — which covers the waiting page and the five status pages
     under `/r/{token}`. The only template with no such wrapper is
     `preview_wordmark.html.j2`. Checked by rendering the waiting page, not by
     reading: a first pass here claimed this token still painted it, because
     the rule that overrides it spans two lines and a one-line grep missed it.

     So this edit keeps a CONTRACT rather than changing a surface, and the
     contract is worth keeping: the guard is what stops the two palettes
     drifting, and a page that ever stops setting its own ground should
     inherit the report's, not a colour the report left behind in September.
     Whether that guard should still compare `--bg` at all is the web lanes'
     call, not this one's.

     CHANGED BY LANE R, OUT OF LANE AND ON THE OPERATOR'S DIRECT AUTHORISATION.
     The report's own token cannot move without this one; the guard exists to
     say so. `--muted` and `--faint` still clear 4.5 on it — 6.94 and 4.57,
     the constraint the block above this one records. */
  --bg: #F0F6FB;
  --card: #FFFFFF;
  --border: #E4E7EC;
  --text: #111827;
  /* Display numbers and serif titles only: --text warmed to the paper. */
  --ink-warm: #1F2937;
  --muted: #4B5563;
  --faint: #667085;
  /* client brand highlight */
  --brand: #2563EB;
  --brand-dark: #1D4ED8;
  --brand-tint: #E8EFFD;
  --brand-border: #C7D7FA;
  /* semantic warning tint (used sparingly) */
  --warn-bg: #F4EDDC;
  --warn-text: #8A6414;
  --warn-border: #E7DBB8;
  color-scheme: light;
}

* { box-sizing: border-box; }
/* clip, never hidden — the report's rule, and the reason is the same: hidden
   makes the page a scroll container and quietly breaks sticky positioning. */
html, body { margin: 0; padding: 0; overflow-x: clip; }
/* The sticky bar covers whatever an in-page anchor lands on, so every jump
   stops short of it. Generous rather than exact: the bar is content-height and
   grows when its two clauses wrap, which they do on a narrow screen. */
html { scroll-padding-top: 76px; }
@media (max-width: 620px) { html { scroll-padding-top: 104px; } }

body {
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand-dark); text-decoration: none; }
a:hover { color: var(--brand); }

/* Ink & paper: serif display faces. The report reserves Georgia for headings
   and for figures; body copy is the sans stack — and so is the WORDMARK, which
   is `.logo` on the report and carries no font-family of its own. */
h1, h2 {
  font-family: Georgia, "Times New Roman", serif;
  letter-spacing: 0;
  color: var(--ink-warm);
}
h1 { font-size: 26px; font-weight: 650; letter-spacing: -0.02em; line-height: 1.25; margin: 0 0 10px; }
h2 { font-size: 15.5px; font-weight: 650; letter-spacing: -0.01em; margin: 0 0 8px; }

/* --- the beta bar ------------------------------------------------------
   On EVERY page of the ordering layer, above the topbar. The report shows the
   same notice in the same words, so a client meets one statement whichever
   surface they are on.

   THE NOTICE FAMILY, NOT THE BRAND'S — `--warn-*`, which is what the report's
   own `.betabar` uses and for a reason worth repeating here: blue already
   means the client's own data on every chart in the report, so a bar wearing
   it reads as a figure about the brand rather than a note about the product.
   The landing page's design handoff specifies #F5EFDC on #6B5A2E, a shade off
   these; the report's palette wins because this bar is now shared with the
   report's own and two near-identical creams on adjacent surfaces read as a
   mistake. Geometry is the handoff's. */
.betabar {
  /* STICKY, so it survives the scroll (2026-08-20, on request). The landing
     page is long and the notice is the first thing a visitor scrolls past;
     a bar that only exists at the top of the document is one most people
     never see.

     `sticky` and not `fixed`: it stays in normal flow, so nothing below it
     needs a compensating padding-top the way the report's fixed bar does —
     and there is no height to keep in sync when the text wraps to two lines
     on a narrow screen.

     IT ONLY WORKS BECAUSE `html, body` USE `overflow-x: clip`. `hidden` would
     make the page a scroll container, and a scroll container is what sticky
     resolves against — the bar would scroll away silently. That is the report's
     own rule, written down in CLAUDE.md, and this is the second place in the
     project that depends on it. */
  position: sticky; top: 0; z-index: 100;
  background: var(--warn-bg); color: var(--warn-text);
  border-bottom: 1px solid var(--warn-border);
  text-align: center; padding: 20px; letter-spacing: 0.01em;
  font-size: 15px; font-weight: 600; line-height: 1.4;
}
/* THE BAR IN THE PRODUCT'S OWN FACE, AND AT THE WEIGHT ITS RULE ASKS FOR
   (2026-09-07). Two defects, found by measuring the rendered bar rather
   than reading these rules, and both invisible in the stylesheet:

   ITS TYPEFACE DEPENDED ON WHICH PAGE IT SAT ON. Not "it was not in Inter" —
   it was, on six of eight pages. The bar sits OUTSIDE `.lh`; it is the
   shell's, from `base.html.j2`, and it declared no face of its own, so it
   took whatever each page's body handed it. `status.css` sets
   `body:has(.status) { font-family: var(--st-sans) }` and `--st-sans` names
   Inter first, so all six status pages rendered it Inter 600. `/` and
   `/order` had only `body`'s stack above, which names no Inter at all, so
   the SAME bar rendered `-apple-system` there — the first line of those two
   pages in the system font with every line under it in Inter. Measured on
   all eight, not read.

   So this rule does not GIVE the bar Inter. It stops one element's typeface
   being decided by its neighbours, which is why it is not cosmetic and
   should not be reverted as such. Named here rather than by widening
   `body`'s stack: the bar is one element with one job, and changing what
   every page's body inherits is a bigger decision than this is.

   AND ITS `<strong>` COMPUTED TO 900, ON ALL EIGHT. `.betabar` asks for
   600; the UA sheet gives `strong` a weight of `bolder`, which is RELATIVE
   — bolder than 600 is 900 — so the bar's own claim was heavier than the
   50px headline three blocks below it. 600 is the fix, and the reason it is
   600 rather than 700 is the design already in the file: `.betabar` sets
   600 and `.betabar-tail` steps DOWN to 500, a deliberate two-step of claim
   then trailing clause. So a 600 `<strong>` is not the strong losing its
   emphasis; it is the bar's own weight, with the contrast left where the
   design put it. Nothing in this file or the report's copy declares 700 or
   900 anywhere, so either would enter the palette by accident.

   SO THE TWO HALVES OF THIS FIX HAVE DIFFERENT REACH, which is easy to
   misread: the FACE half changes `/` and `/order`, the two pages that were
   not already Inter. The WEIGHT half changes ALL EIGHT — the six status
   pages carried the identical 900 from the identical cause. Measured on all
   six at 1200 and 390: strong 900 183x19 -> 600 178x19, and 900 177x17 ->
   600 173x17 under the phone rule, with the bar's own box unchanged at 62
   and 47. Six of the eight surfaces corrected here are ones this lane
   cannot render.

   THE REPORT CARRIES A SECOND COPY OF THIS BAR and it still has the 900.
   `report.html.j2` repeats these rules because a standalone report cannot
   include a stylesheet the app serves, and its `<strong>` sits in the same
   600 container with the same UA `bolder`. That file is lane R's and
   `docs/parallel-sessions.md` singles it out as the one that must not be
   open in two sessions, so it is NOT changed here — it is asked for. Until
   it lands, the shell says this sentence at 600 and the report says it at
   900.

   NO COLOUR CHANGES HERE. `--warn-bg`, `--warn-text` and `--warn-border`
   are untouched: the landing page took a navy scheme on 2026-09-06 whose
   whole premise is that this bar keeps its cream and amber, and it is
   shared with the order form and the six status pages. */
.betabar {
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}
.betabar strong { font-weight: 600; }
.betabar-sep { opacity: 0.5; padding: 0 8px; }
.betabar-tail { font-weight: 500; }

/* --- shell ------------------------------------------------------------ */

.topbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 13px 24px;
  border-bottom: 1px solid var(--border); background: var(--card);
}
/* `.logo` on the report, to the pixel: 16px, 650, -0.01em, a 10px brand square
   at a 3px radius, 9px of gap. */
/* OPTION D (2026-09-03): no dot, and "Says" set in the accent. `.wordmark-dot`
   is gone with the markup that used it — a class no element carries reads as a
   live decision. The gap goes too: the parts are one word now, not an icon and
   a word. */
/* `inline-block` WITH `line-height`, NOT A FLEX ROW. The name is `ai`, a bold
   `Says` and `What` — three inline pieces — and a flex container would make
   each its own item and space the word apart (the same trap the /preview
   version hit). Normal inline flow keeps it one word; the 24px line-height and
   min-height are the tap target `test_the_shells_own_links...` asserts. */
.wordmark {
  display: inline-block; min-height: 24px; line-height: 24px;
  font-size: 16px; font-weight: 650; letter-spacing: -0.01em; color: var(--text);
}
/* #b3892f is the landing page's `--lh-accent`; the shell has no accent token of
   its own (`--brand` is the pre-rename blue), so the literal, once, here. */
/* #8a6620, the AA-safe amber (4.98:1 on paper), not #b3892f (3.05) — the
   word is 16px text. The landing page carries the same pair as
   --lh-accent-text; the shell has no accent token, so the literal. */
.wordmark-says { color: #8a6620; font-weight: 650; }
/* 24px OF REACH, AND IT IS THE TAP TARGET RATHER THAN A LOOK (2026-09-03).
   WCAG 2.2 AA 2.5.8 asks 24x24 of anything a finger has to hit, and a full QA
   of the live surface measured this link at 94x16 on `/order` — and therefore
   on the waiting page and every failure page, since they all wear this shell.
   An inline link is only as tall as its line; the flex box adds the reach
   above and below without moving the text. The landing page's own `.lh-nav a`
   has carried exactly this treatment since its header was built, which is how
   the shell's copy of the same link got missed: two navs, one rule each, and
   only one of them audited at the time. The wordmark gets the same
   `min-height` for the same reason — it is a link too, at 16px of line. */
.topbar nav a {
  display: inline-flex; align-items: center; min-height: 24px;
  font-size: 13.5px; color: var(--muted);
}
.topbar nav a:hover { color: var(--brand-dark); }
.topbar nav a:hover { text-decoration: underline; }

.page { max-width: 760px; margin: 0 auto; padding: 40px 24px 56px; }

.foot {
  border-top: 1px solid var(--border);
  padding: 18px 24px; color: var(--faint); font-size: 12.5px; text-align: center;
}
.foot p { margin: 0; }

/* --- landing ---------------------------------------------------------- */

.hero { margin-bottom: 36px; }
.lede { font-size: 15px; color: var(--muted); margin: 0 0 20px; }
.fineprint { font-size: 12.5px; color: var(--faint); margin: 14px 0 0; }

/* 3px is the report's dominant radius; its cards are 4px and nothing on it is
   rounder than 5px except the pills. 6px read as a different product. */
.cta {
  display: inline-block; background: var(--brand); color: #FFFFFF;
  border: 1px solid var(--brand-dark); border-radius: 4px;
  padding: 9px 17px; font-size: 14px; font-weight: 600;
  cursor: pointer; font-family: inherit;
}
.cta:hover { background: var(--brand-dark); color: #FFFFFF; }
.cta:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.cards { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: 4px; padding: 18px 22px; box-shadow: none;
}
.card p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--muted); }

/* --- the order form --------------------------------------------------- */

.order { margin-top: 22px; }

.field { margin-bottom: 20px; }
.field label { display: block; font-size: 13.5px; font-weight: 600; margin-bottom: 5px; }
.req {
  font-weight: 500; font-size: 10.5px; color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.04em; margin-left: 6px;
}
/* `select` BELONGS IN THIS LIST AND WAS NOT IN IT until 2026-08-30, which cost
   two separate things and both showed only on a phone. The two pickers —
   country and depth — were drawn by the operating system while the eight
   fields around them were drawn by us: measured at 390px, 19px tall against
   the inputs' 39, 13.3px type against 14, square corners against 3px, #EFEFEF
   against white, a #767676 border against --border, and no padding at all. In
   one column that reads as two different forms, and the country picker was the
   smallest thing on the page to hit.
   `font: inherit` is doing the sizing here, so a select left out of the rule
   also missed the 16px below. */
.field input, .field select, .field textarea {
  width: 100%; padding: 8px 10px; font: inherit; color: var(--text);
  background: var(--card); border: 1px solid var(--border); border-radius: 3px;
}
/* The OS disclosure arrow is KEPT — `appearance: none` would take it, and it is
   the only thing that says this control opens a picker rather than a keyboard.
   The box around it is ours; the affordance stays the platform's. */
.field select { line-height: 1.4; }
.field textarea { resize: vertical; line-height: 1.55; }
.field input::placeholder, .field textarea::placeholder { color: var(--faint); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: 2px solid var(--brand); outline-offset: -1px; border-color: var(--brand-border);
}
.hint { font-size: 12.5px; color: var(--muted); margin: 6px 0 0; }
/* The category suggestion's line. Tinted rather than loud: it is a proposal
   about a field the visitor still owns, not a warning and not a result.
   --brand-dark, not --brand, and the reason is measured rather than judged:
   at 12.5px this needs 4.5:1 on --bg (#F8F9FB), where --brand (#2563EB) gives
   4.91:1 and --brand-dark (#1D4ED8) gives 6.36:1. Both pass; the first passes
   by 0.41, which is the margin that disappears the next time anybody warms up
   the background. It is also the colour links already use on these pages. */
.suggestion { color: var(--brand-dark); }
/* THE FORM HIDES BY ATTRIBUTE, so this rule has to exist — the browser's own
   `[hidden] { display: none }` is user-agent weight and any rule of ours
   naming a class outranks it. Nothing here sets `display` on `.suggestion`
   today, which is exactly the state in which somebody adds one and quietly
   un-hides a paragraph that is meant to be empty. Same rule, same reason, as
   the report's own stylesheet. */
[hidden] { display: none !important; }

.errors {
  background: var(--warn-bg); border: 1px solid var(--warn-border);
  border-radius: 4px; padding: 13px 17px; margin-bottom: 22px; color: var(--warn-text);
}
.errors-head { margin: 0 0 6px; font-weight: 600; font-size: 13.5px; }
.errors ul { margin: 0; padding-left: 20px; font-size: 13.5px; }

/* --- status pages ----------------------------------------------------- */

.status { max-width: 560px; }
/* The report's client block, which is the pattern these pages want: a small
   grey label and the brand in plain text. (The report's OTHER eyebrow — 11px,
   650, brand-coloured — belongs over a spoke title, not over a brand name.) */
.client-eyebrow {
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--faint); margin: 0;
}
.client-name {
  font-size: 14.5px; font-weight: 600; color: var(--text); margin: 4px 0 14px;
}
.statusline {
  display: inline-block; background: var(--brand-tint);
  border: 1px solid var(--brand-border); border-radius: 4px;
  padding: 8px 13px; font-size: 13.5px; color: var(--brand-dark); margin: 0;
}

/* The progress bar. A div with a width, like every other bar in this project
   (the report's score rings and themes bar are a conic gradient and a div) —
   no chart library, nothing async, and it draws with scripts off, which the
   meta-refresh above it depends on. */
.progress { margin: 0 0 14px; }
.progress-track {
  height: 8px; border-radius: 4px; background: var(--brand-tint);
  border: 1px solid var(--brand-border); overflow: hidden;
}
.progress-fill {
  height: 100%; background: var(--brand); border-radius: 3px 0 0 3px;
  /* The page reloads rather than animating, so the transition only smooths
     the first paint. Kept short for that reason. */
  transition: width 240ms ease-out;
}
/* An unmeasured run gets an empty track and no figures, never a 0% fill:
   "nobody wrote it down" and "nothing has happened" are different claims. */
.progress-figures {
  display: flex; justify-content: space-between; gap: 12px;
  margin: 7px 0 0; font-size: 12.5px; color: var(--faint);
}
.progress-figures strong { color: var(--text); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .progress-fill { transition: none; }
}

@media (max-width: 600px) {
  .page { padding: 28px 18px 40px; }
  h1 { font-size: 22px; }

  /* THE BAR GIVES ITS SECOND LINE BACK TO THE PAGE. Measured at 390: 83px tall
     against 62 on a laptop, because the sentence wraps — and it is STICKY, so
     that is 83px of an 844px screen gone for the whole visit, 12.4% of an
     iPhone SE's 667. It was the largest reclaimable thing on the page, and what
     it cost was the promise about money outweighing the headline it sits above.

     THE TAIL GOES, NOT THE NOTICE. "Free to use while in beta" is the promise
     and it stays in full. "AI search visibility, one report at a time" is a
     descriptor, and on the landing page it is redundant with the headline
     directly beneath it — "AI search analytics for small businesses". The
     separator goes with the tail or it dangles.

     `display: none` rather than a shorter string, so there is one sentence in
     the markup said the same way everywhere and no second copy to keep in
     step. The bar is the shell's, so this reaches the order form and the
     waiting page too, which is right: a notice that changes shape between
     pages reads as two notices. */
  .betabar { padding: 13px 16px; font-size: 14.5px; }
  .betabar-sep, .betabar-tail { display: none; }
}

/* 16px ON A TOUCHSCREEN, AND NOT FOR LOOKS. iOS Safari zooms the whole page in
   when a field under 16px takes focus, and it does not zoom back out. This form
   is TEN fields long, so the visitor zooms on the first one and fills in the
   rest of it zoomed and scrolling sideways — on the page the product converts
   on. Measured at 390 before this: eight fields at 14px and the two selects at
   13.3px, every one of them a trigger.

   `pointer: coarse`, NOT A WIDTH, and that is the part worth reading. The
   behaviour belongs to the DEVICE, not to how many pixels across the window
   is, so a width breakpoint gets it wrong at both ends. This file's phone
   breakpoint is 600px; a phone held in landscape is 844 or 932 across and
   Safari zooms there exactly the same, so the rule would switch itself off in
   the orientation where sideways scrolling hurts most. An iPad does it too, at
   any width a desktop rule would call desktop. `pointer: coarse` asks the one
   question that actually predicts it — is the primary input a fingertip — and
   leaves every mouse-driven browser at 14px.

   `<meta name="viewport">` here is `width=device-width, initial-scale=1` with
   no `maximum-scale`, which is right and must stay: capping zoom to dodge this
   takes zoom away from everybody who needs it. 16px is the fix; the cap is not.

   ON THE FIELDS ONLY. Labels, hints and body copy keep their own sizes — what
   Safari reacts to is the font-size of the thing being focused, so growing the
   page's prose would change the design and fix nothing. */
/* AND `order.css` CARRIES THE SAME RULE FOR THE PROMPT EDITORS (2026-09-07).
   The selector below reaches the ten fields inside a `.field`; the order form's
   fourteen `.promptbox` inputs are not inside one — they are in a `<details>`
   list that is closed at load — so they were still at 15px and still zooming.
   Fixed in that file rather than by widening this selector, because they are
   styled at a specificity this rule loses to. Say it here so the next reader
   does not take this block for the whole story. */
@media (pointer: coarse) {
  .field input, .field select, .field textarea { font-size: 16px; }
}
