/* ============================================================
   InsightsHive — Retail Execution Intelligence
   Design system per repositioning plan §12.
   One dark ground throughout — see the palette note below. No emoji,
   no decorative AI visuals. Colour carries hierarchy and data only.
   ============================================================ */

/* One ground, one palette. The site used to run a light theme with the hero and
   the two bands punched out of it in --night; every one of those boundaries was
   a visible seam, and the dust field had to be cut into a separate canvas per
   dark region to survive them. The page is now night throughout, so there is a
   single ground, a single canvas, and nothing left to butt against.
   Nothing ever set [data-theme], so the old toggle hooks are gone with it;
   prefers-color-scheme no longer branches because there is only one theme. */
:root {
  /* Neutrals. Values carried over from the cinema hero, which was already
     tuned against this ground. */
  --night:      #17131e;
  --paper:      var(--night);
  --surface:    #201a29;   /* raised: cards, the closing wash */
  --surface-2:  #2b2235;
  --line:       #332b3d;
  --line-soft:  #262030;
  --ink:        #f7f3f9;
  --ink-2:      #cdc3d6;
  --ink-3:      #9d92aa;

  /* Brand purple, lightened for a dark ground. #cf93e6 measures 7.8:1 on
     --night; the original #a359bc managed only 2.9:1 and is unusable here. */
  --accent:      #cf93e6;
  --accent-2:    #ddb0ee;
  --accent-deep: #cf93e6;
  --accent-ink:  #1b1622;
  --accent-wash: #2b2235;

  /* Semantic — data states only */
  --good: #6fc79a;
  --warn: #e0ac5a;
  --bad:  #e88b80;

  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;

  --measure: 68ch;
  /* The aisle gutter. One value drives the marker column and the matching
     indent on every band, so the page keeps a single left edge. */
  --bay: clamp(112px, 9vw, 136px);
  --bay-edge: max(1.25rem, calc(50% - 620px));
  --radius: 8px;
  --gap: 1.5rem;

  --step--1: clamp(0.82rem, 0.8rem + 0.1vw, 0.88rem);
  --step-0:  clamp(1rem, 0.97rem + 0.15vw, 1.09rem);
  --step-1:  clamp(1.2rem, 1.13rem + 0.35vw, 1.4rem);
  --step-2:  clamp(1.45rem, 1.3rem + 0.7vw, 1.9rem);
  --step-3:  clamp(1.8rem, 1.5rem + 1.4vw, 2.6rem);
  --step-4:  clamp(2.2rem, 1.7rem + 2.4vw, 3.5rem);
}

/* Interactive dot field ------------------------------------------------
   The drifting dots behind the page, with a small gravity well under the
   cursor (js/field.js). Ornamental only: aria-hidden in the markup, never
   takes pointer events, and js/field.js holds it still under
   prefers-reduced-motion.

   One layer. There used to be three — a fixed page canvas plus an absolute one
   inside the hero and each band, because those painted an opaque ground that
   would have covered a single page-wide canvas. Three canvases meant three
   independent seeds, three densities and, worst of all, one fixed layer against
   two that scrolled: the dust visibly changed character and parallax at every
   boundary, which is what actually drew the seam. Now that the ground is
   continuous, one fixed canvas ran the whole page.

   Since 2026-09-18 the dust lives in the homepage hero only; the rest of the
   page is plain --night. The canvas sits inside the hero and scrolls with it,
   and its mask fades the dust out over the hero's last stretch, so the hero
   still ends without a line. */
:root {
  /* CSS area per dot — lower is denser. Tuned for a viewport-sized layer over
     --night: dense enough to read as a texture rather than scattered stars. */
  --field-density: 300;
  --field-dot:  rgba(247, 243, 249, .52);
  --field-link: rgba(207, 147, 230, .34);
  /* Dots caught in the well take this instead, so the gravity is legible
     without brightening the whole field into noise. */
  --field-hot:  rgba(255, 252, 255, .95);
}
/* z-index 0 sits it with the hero glow, under the hero copy (z-index 1).
   A canvas is a replaced element, so inset:0 alone leaves it at its intrinsic
   300x150 in the corner — the explicit width/height are what stretch it. */
.fieldbg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 0; display: block; pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; color-scheme: dark; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: var(--step-0);
  line-height: 1.6;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

h1,h2,h3,h4 {
  font-family: var(--display);
  font-stretch: 88%;
  color: var(--ink);
  font-weight: 600;
  line-height: 1.14;
  letter-spacing: -0.022em;
  text-wrap: balance;
  margin: 0;
}
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-1); }
h4 { font-size: var(--step-0); font-weight: 600; }
p  { margin: 0; max-width: var(--measure); }
a  { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px;
}
ul { margin: 0; padding-left: 1.1em; }
li { margin: 0 0 .35em; }
strong { font-weight: 600; }

.wrap { width: min(1240px, 100% - 2.5rem); margin-inline: auto; }

/* Bay rhythm. Sections are bays on a gondola: each rests on a shelf rail, and
   carries its label in the left gutter the way an aisle marker does. Replaces
   alternating tint bands, which were doing decorative work rather than
   structural work. Tint is now reserved for the closing CTA. */
.section {
  padding-block: clamp(2.4rem, 4.2vw, 4.4rem);
  border-bottom: 2px solid var(--line);
  position: relative;
}
.section::after {
  content: ""; position: absolute; left: var(--bay-edge); bottom: -2px;
  width: clamp(48px, 9vw, 104px); height: 2px; background: var(--accent);
}
.section:last-of-type { border-bottom: 0; }
.section:has(+ .band) { border-bottom: 0; }
.section:has(+ .band)::after { display: none; }
.section:last-of-type::after { display: none; }
/* The closing wash. A flat --surface block cut two hard edges into the page's
   one ground, so it is a gradient that rises and falls rather than a rectangle.
   It goes on background-image, not a pseudo-element: ::before is the bay label
   and ::after the accent stub, both already spoken for. */
.section--tint {
  background: none;
  background-image: linear-gradient(
    to bottom, transparent 0, var(--surface) 22%, var(--surface) 78%, transparent 100%);
}
/* --accent-deep is the same value as --accent on one dark ground; the alias is
   kept so these call sites still read as "link on a wash".
   :not(.btn) matters — a type selector here out-specifies .btn--primary. */
.section--tint a:not(.btn), .foot a.textlink, .notice a:not(.btn) { color: var(--accent-deep); }
.section--line { border-top: 0; }

/* Left gutter marker — the aisle label. Only on the homepage, so it stays a
   signature rather than furniture. */
@media (min-width: 1180px) {
  /* Every band is indented by the gutter, including the hero and the closing
     CTA, so the page has one left edge and the marker column is a real column
     rather than an indent that only some sections pay for. */
  .section > .wrap,
  .hero > .wrap { padding-left: var(--bay); }
  .section[data-bay]::before {
    content: attr(data-bay);
    position: absolute;
    top: clamp(2.4rem, 4.2vw, 4.4rem);
    left: var(--bay-edge);
    width: calc(var(--bay) - 24px);
    font-family: var(--display); font-stretch: 76%;
    font-size: .72rem; font-weight: 600; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-3);
    line-height: 1.35;
    border-top: 2px solid var(--accent);
    padding-top: .55rem;
  }
}
@media (max-width: 1179px) {
  /* attr() resolves against the element the pseudo belongs to, so this has to
     sit on the section that carries the attribute. It previously hung off
     .wrap and rendered an empty rule on every section below 1060px. */
  .section[data-bay]::before {
    content: attr(data-bay);
    display: block; width: fit-content;
    margin-left: var(--bay-edge); margin-bottom: 1.75rem;
    font-family: var(--display); font-stretch: 76%;
    font-size: .7rem; font-weight: 600; letter-spacing: .14em;
    text-transform: uppercase; color: var(--ink-3);
    border-top: 2px solid var(--accent);
    padding-top: .45rem;
  }
}

/* Text blocks hold a narrower measure so the bay has a clear left column and
   air on the right; grids and rails still run the full bay width. */
.hold { max-width: 60ch; }
.stack { display: flex; flex-direction: column; gap: var(--gap); }
.stack--s { gap: .75rem; }
.stack--l { gap: 1.9rem; }

.eyebrow, .card__module {
  font-family: var(--display); font-stretch: 76%;
  font-size: var(--step--1); font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent-deep); margin: 0;
}
.lede { font-size: var(--step-1); color: var(--ink-2); line-height: 1.5; }
.muted { color: var(--ink-3); }
.small { font-size: var(--step--1); }

/* Header + nav ------------------------------------------------ */
.masthead { border-bottom: 1px solid var(--line-soft); background: var(--paper); position: sticky; top: 0; z-index: 50; }
.masthead__inner { display: flex; align-items: center; gap: 2rem; min-height: 68px; }
.brand { display: inline-flex; align-items: center; gap: .6rem; font-family: var(--display); font-stretch: 88%; font-weight: 600; font-size: 1.15rem; letter-spacing: -.02em; color: var(--ink); text-decoration: none; }
.brand__mark { color: var(--accent); flex: none; }
.nav { margin-left: auto; display: flex; align-items: center; gap: 1.6rem; }
.nav a { color: var(--ink-2); text-decoration: none; font-size: .95rem; }
.nav a:hover { color: var(--ink); }
.nav a[aria-current="page"] { color: var(--ink); font-weight: 500; }
/* .nav a (0,1,1) out-specifies .btn--primary (0,1,0), which left the header's
   primary CTA at --ink-2 on purple: 2.8:1 in light, 1.9:1 in dark. */
.nav a.btn--primary { color: var(--accent-ink); }
.nav a.btn--ghost { color: var(--ink); }
.nav__cta { margin-left: .4rem; }
/* Language switch. Deliberately quiet — a reader who wants the other language
   looks for it, and it should never compete with the CTA. Sits before the CTA
   so the button stays the last thing in the bar. */
.nav a.nav__lang {
  padding: .15rem .45rem;
  font-family: var(--display); font-stretch: 82%; font-size: .78rem;
  font-weight: 600; letter-spacing: .08em; color: var(--ink-3);
  border: 1px solid var(--line); border-radius: 3px;
}
.nav a.nav__lang:hover { color: var(--ink); border-color: var(--ink-3); }
.navtoggle { display: none; margin-left: auto; background: none; border: 1px solid var(--line); border-radius: var(--radius); padding: .45rem .7rem; color: var(--ink); font: inherit; font-size: .9rem; }

@media (max-width: 860px) {
  .navtoggle { display: block; }
  .nav { display: none; position: absolute; inset: 68px 0 auto; flex-direction: column; align-items: stretch; gap: 0; background: var(--paper); border-bottom: 1px solid var(--line); padding: .5rem 1.25rem 1.25rem; }
  .nav[data-open="true"] { display: flex; }
  .nav a { padding: .7rem 0; border-bottom: 1px solid var(--line-soft); }
  .nav__cta { margin: .8rem 0 0; }
}

/* Buttons ----------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .72rem 1.25rem; border-radius: var(--radius);
  font-size: .95rem; font-weight: 500; text-decoration: none;
  border: 1px solid transparent; cursor: pointer; font-family: inherit;
}
.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { background: var(--accent-2); }
.btn--ghost { border-color: var(--line); color: var(--ink); background: var(--paper); }
.btn--ghost:hover { border-color: var(--ink-3); }
.btn-row { display: flex; flex-wrap: wrap; gap: .75rem; }
.textlink { font-weight: 500; }

/* Grids ------------------------------------------------------- */
.grid { display: grid; gap: var(--gap); }
/* auto-fit alone put column count a few pixels from flipping — a 10px change in
   the gutter turned a 3+2 row of five into a 4+1. Column counts are fixed above
   the breakpoint where they fit, and auto-fit only handles the way down. */
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
@media (min-width: 760px) { .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 900px) { .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.grid--kpi { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
/* Four named modules read as a set, not as a row of three and a straggler. */
.grid--modules { grid-template-columns: 1fr; }
@media (min-width: 720px) { .grid--modules { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.card {
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.6rem; background: var(--paper);
  display: flex; flex-direction: column; gap: .6rem; height: 100%;
}
.card h3 { font-size: var(--step-1); }
.card__promise { color: var(--accent); font-weight: 500; font-size: .95rem; }
/* Homepage module cards lead with the benefit; the module name is the label. */
.card__module { font-size: .72rem; }
.card__list { list-style: none; padding: 0; margin: .3rem 0 0; color: var(--ink-2); font-size: .95rem; }
.card__list li { padding-left: 1rem; position: relative; }
.card__list li::before { content: ""; position: absolute; left: 0; top: .66em; width: 5px; height: 1px; background: var(--ink-3); }
.card__note { margin-top: auto; padding-top: .9rem; border-top: 1px solid var(--line-soft); color: var(--ink-3); font-size: var(--step--1); }
.card--link { text-decoration: none; color: inherit; }
.card--link:hover { border-color: var(--ink-3); }

/* Proof tiles ------------------------------------------------- */
/* Shelf edge: the page's structural device. Anything measured rests on a rule,
   with a short accent segment at the left reading as the label clip.

   One rule, learned from a walkthrough: a SHORT accent segment sitting on a long
   grey rule directly beneath a number reads as a half-empty progress bar. It was
   meant as a label clip; it was read as a broken gauge, because 94% and 31% drew
   identical bars. So the device now splits by context —
     short segment  = section-level ornament, never next to a figure
     full width     = an underline under a figure, no measurement implied
     proportional   = an actual bar, width driven by the value (.marker with --v)
   Nothing on the page may sit between those three. */
.stat, .kpi { position: relative; padding-bottom: 1rem; border-bottom: 2px solid var(--line); }
.stat::after, .kpi::after {
  content: ""; position: absolute; left: 0; bottom: -2px;
  height: 2px; background: var(--accent);
}
.stat::after, .kpi::after { width: 100%; }
.stat__fig {
  font-family: var(--display); font-stretch: 85%;
  font-size: var(--step-3); font-weight: 600; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums lining; color: var(--ink); line-height: 1.05;
}
.stat__label { font-weight: 500; margin-top: .3rem; }
.stat__qual { color: var(--ink-3); font-size: var(--step--1); margin-top: .3rem; }
.stat--todo::after { background: var(--line); }
.stat--todo .stat__fig { color: var(--ink-3); }

/* KPI list ---------------------------------------------------- */
.kpi h3 {
  font-family: var(--display); font-stretch: 76%;
  font-size: var(--step-0); font-weight: 600; text-transform: uppercase;
  letter-spacing: .055em; line-height: 1.2;
}
.kpi p { color: var(--ink-3); font-size: .93rem; margin-top: .3rem; }

/* Abstracted product visualisation — plan §12.6 ---------------- */
/* One capture, three returns — not four steps. The section claims a single
   visit produces the whole picture at once, so the capture stands alone and the
   three returns share ONE accent rule: they arrive together, in no order. A run
   of evenly ticked stops said the opposite of the headline above it. */
.viz { position: relative; display: grid; gap: 0; border-bottom: 2px solid var(--line); grid-template-columns: minmax(0, .7fr) minmax(0, 3fr); }
.viz::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 30px; height: 2px; background: var(--accent); }

/* The source. No rule above it — it is the input, not one of the returns. */
.viz__source { display: flex; flex-direction: column; gap: .8rem; padding: calc(.55rem + 2px) 1.9rem 1.7rem 0; }
/* Full-strength ink on the source label, so the missing rule above it reads as
   a distinction rather than an oversight. */
.viz__source .viz__stage { color: var(--ink); }

/* The returns. One rule spanning all three. The heavier divider marks the split
   between what goes in and what comes back; the hairlines between the three are
   lighter, because those three are peers rather than stages. */
.viz__returns {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 2px solid var(--accent); border-left: 1px solid var(--line);
  padding-top: .55rem;
}
.viz__panel { padding: 0 1.35rem 1.7rem; border-left: 1px solid var(--line-soft); display: flex; flex-direction: column; gap: .8rem; min-height: 168px; }
.viz__panel:first-child { border-left: 0; }

/* Time-back race (homepage, benefit 2). Two columns draining on one clock:
   30 minutes by hand against 2.3 with us, both played at 30x by js/site.js.
   Once ours is done, its clock fades, a tick shows in the empty column and
   the selling list lands beside it. */
.race { display: grid; gap: 1.6rem; border-bottom: 2px solid var(--line); padding-bottom: 2rem; position: relative; }
.race::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 30px; height: 2px; background: var(--accent); }
.race__duel { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1rem, 4vw, 4rem); }
.race__lane { display: grid; justify-items: center; gap: .9rem; text-align: center; align-content: start; }
.race__name { font-family: var(--display); font-stretch: 76%; font-size: var(--step--1); font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
/* Same height in both lanes so the clocks stay level. */
.race__name { min-height: 2rem; display: inline-flex; align-items: center; }
.race__lane--us .race__name { color: var(--ink); gap: .45em; font-size: 1.15rem; letter-spacing: .08em; }
.race__mark { flex: none; margin: -8px 0; color: var(--accent); }
/* The old way steps back: the eye should sit on ours. */
.race__lane--old { opacity: .55; }
.race__clock { font-family: var(--display); font-stretch: 85%; font-size: var(--step-4); font-weight: 600; line-height: 1;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums lining; color: var(--ink-2); transition: opacity .4s; }
.race__lane--us .race__clock { color: var(--accent); }
/* Ours is done: the number steps aside so the selling list takes the eye. */
.race__lane--us.done .race__clock, .race__lane--us.done .race__of { opacity: 0; }
.race__lane.done .race__clock { color: var(--ink-3); }
.race__of { font-size: .85rem; color: var(--ink-3); margin-top: -.5rem; transition: opacity .4s; }
/* Column in the middle, the selling list to its right, so both columns line
   up across the two lanes whatever the list does. */
.race__body { justify-self: stretch; display: grid; grid-template-columns: 1fr auto 1fr; align-items: stretch;
  column-gap: clamp(.8rem, 2.4vw, 1.8rem); }
.race__body > .race__col { grid-column: 2; align-self: start; }
/* The column: full at the start, emptying toward the floor as the check runs. */
.race__col { position: relative; width: clamp(56px, 7vw, 84px); height: clamp(200px, 26vw, 280px);
  border: 1px solid var(--line); border-radius: 6px; background: rgba(255,255,255,.025); overflow: hidden;
  transition: border-color .5s; }
.race__fill { position: absolute; left: 0; right: 0; bottom: 0; height: 100%; background: var(--ink-3); }
.race__lane--us .race__fill { background: var(--accent); }
.race__lane--us.done .race__col { border-color: var(--good); }
/* Our "Done.", inside the empty column under a pixel tick. */
.race__check { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6rem; font-family: var(--display); font-stretch: 76%; font-weight: 600; font-size: 1rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink); opacity: 0; transition: opacity .4s; }
.race__tick { fill: var(--good); }
/* The display face has no Cyrillic, so "Готово." falls back to a wider one. */
html[lang="bg"] .race__check { font-size: .8rem; letter-spacing: .02em; }
.race__lane--us.done .race__check { opacity: 1; }
/* What the rep is doing now, then what happens after the check. */
.race__now { min-height: 1.6em; font-size: .95rem; color: var(--ink-2); }
.race__after { display: flex; align-items: flex-start; justify-content: center; gap: .55rem; min-height: 3.2em;
  font-size: .95rem; color: var(--ink-2); opacity: 0; transition: opacity .5s; max-width: 30ch; text-align: left; }
.race__after.on { opacity: 1; }
/* The dot is drawn here: the build strips empty elements from the markup. */
.race__after::before { content: ""; width: 10px; height: 10px; border-radius: 50%; flex: none; margin-top: .45em; background: var(--bad); }
.race__lane--us .race__after::before { background: var(--good); }
/* The benefit: what our rep does with the time while the other one is still
   counting. Items hold their space from the start so nothing jumps. */
/* Set like our lane's heading ("With [mark] Insights Hive"), full brightness,
   and spread over the column's height: first line level with its top, last
   with its floor. */
.race__sell { grid-column: 3; list-style: none; margin: 0; padding: 0; display: grid; align-content: space-between;
  gap: .5rem; max-width: 26ch; text-align: left; font-family: var(--display); font-stretch: 76%; font-weight: 600;
  font-size: clamp(.85rem, 1.25vw, 1.05rem);
  letter-spacing: .08em; text-transform: uppercase; line-height: 1.15; color: var(--ink); }
/* Bulgarian runs about a third longer than English. */
html[lang="bg"] .race__sell { font-size: clamp(.78rem, 1vw, .92rem); letter-spacing: .03em; gap: .35rem; }
/* Tablet widths: the column is shorter and the space beside it narrower, so
   the list tightens to stay within the column's height. */
@media (min-width: 701px) and (max-width: 1000px) {
  .race__sell { font-size: .78rem; letter-spacing: .04em; gap: .3rem; }
  html[lang="bg"] .race__sell { font-size: .72rem; letter-spacing: .02em; gap: .25rem; }
  .race__col { height: clamp(234px, 26vw, 280px); }
}
.race__sell li { display: flex; gap: .5rem; opacity: 0; transform: translateX(-6px); transition: opacity .5s, transform .5s; }
.race__sell li::before { content: "+"; flex: none; width: .8em; font-weight: 700; font-size: 1.2em; line-height: 1.1; color: var(--good); }
.race__sell li.on { opacity: 1; transform: none; }
.race__foot { display: flex; justify-content: space-between; align-items: center; gap: 1rem; font-size: .75rem; color: var(--ink-3); }
.race__replay { font: 500 .78rem/1 "Inter", sans-serif; color: var(--accent); background: none; cursor: pointer;
  border: 1px solid var(--line); border-radius: 999px; padding: .45rem .85rem; }
.race__replay:hover { border-color: var(--accent); }
@media (max-width: 700px) {
  .race__clock { font-size: var(--step-3); }
  .race__now, .race__after { font-size: .82rem; }
  /* Our heading wraps to two lines on a phone; both get room for two so the
     clocks stay level. */
  .race__name { min-height: 3.1rem; flex-wrap: wrap; justify-content: center; column-gap: .35em; line-height: 1.2; }
  .race__lane--us .race__name { font-size: .88rem; }
  .race__mark { width: 24px; height: 24px; margin: -4px 0; }
  /* Too narrow for a list beside the column: it goes underneath. */
  .race__body { display: contents; }
  .race__body > .race__col, .race__sell { grid-column: auto; }
  .race__sell { order: 1; font-size: .88rem; gap: .6rem; justify-self: start; }
  .race__check { font-size: .8rem; gap: .45rem; }
  .race__tick { width: 27px; height: 18px; }
  .race__foot { flex-direction: column; align-items: flex-start; }
}

/* Numbered step flow — borrows the .viz grammar deliberately (panels divided by
   a hairline, resting on the shelf edge) so the platform page and the homepage
   read as one system. Horizontal across five columns on desktop, stacked on
   mobile, which is the shape a sequence should take on a phone. */
.steps {
  list-style: none; margin: 0; padding: 0; position: relative;
  display: grid; gap: 0; grid-template-columns: repeat(5, minmax(0, 1fr));
  border-bottom: 2px solid var(--line);
}
.steps::after { content: ""; position: absolute; left: 0; bottom: -2px; width: 30px; height: 2px; background: var(--accent); }
.steps__item { display: flex; flex-direction: column; gap: .75rem; padding: 0 1.35rem 1.7rem; border-left: 1px solid var(--line-soft); }
.steps__item:first-child { border-left: 0; padding-left: 0; }
.steps__n {
  display: grid; place-items: center; width: 2.1rem; height: 2.1rem; flex: none;
  border: 2px solid var(--accent); border-radius: 50%; color: var(--accent-deep);
  font-family: var(--display); font-stretch: 85%; font-weight: 600; font-size: .95rem;
  font-variant-numeric: tabular-nums lining; line-height: 1;
}
.steps__item p { margin: 0; color: var(--ink-2); font-size: .95rem; }
@media (max-width: 900px) {
  .steps { grid-template-columns: 1fr; }
  .steps__item { flex-direction: row; align-items: flex-start; gap: 1rem; border-left: 0; padding: 0 0 1.15rem; }
  .steps__item p { padding-top: .35rem; }
}

/* Below 1080 the three returns are too narrow beside the capture — the KPI rows
   start breaking mid-label. The capture moves above them and the three run the
   full width, which states the claim more plainly than the side-by-side does. */
@media (max-width: 1079px) {
  .viz { grid-template-columns: 1fr; }
  .viz__source { padding: 0 0 1.6rem; }
  .viz__returns { border-left: 0; padding-top: 1.5rem; }
  .viz__panel:first-child { padding-left: 0; }
}
/* Fully stacked, the layout still carries one break — capture, then everything
   else — which is the claim. The three returns stay peers behind the rule. */
@media (max-width: 819px) {
  .viz__returns { grid-template-columns: 1fr; }
  .viz__panel { border-left: 0; border-top: 1px solid var(--line-soft); padding: 1.5rem 0 0; min-height: 0; }
  .viz__panel:first-child { border-top: 0; padding-top: 0; }
  .viz__panel:last-child { padding-bottom: 1.5rem; }
}
.viz__stage { font-family: var(--display); font-stretch: 76%; font-size: var(--step--1); font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-3); }
.viz__fig { font-family: var(--display); font-stretch: 85%; font-size: var(--step-2); font-weight: 600; letter-spacing: -.03em; font-variant-numeric: tabular-nums lining; }
.viz__row { display: flex; justify-content: space-between; align-items: baseline; gap: .75rem; border-bottom: 1px solid var(--line-soft); padding-bottom: .4rem; }
.viz__row span:last-child { font-variant-numeric: tabular-nums; font-weight: 500; }
.viz__task { border-left: 2px solid var(--accent); padding-left: .8rem; color: var(--ink-2); font-size: .95rem; }

/* Photo placeholder — real photography to be supplied. Neutral ground, not the
   lavender wash: a 360px block of tint was the loudest thing above the fold and
   the main reason the page read consumer rather than enterprise. */
.photo {
  border: 1px dashed var(--line); border-radius: var(--radius);
  background: var(--surface); min-height: 168px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 1.5rem; color: var(--ink-3); font-size: var(--step--1);
}
/* The read-out plates over the lower-left, so the placeholder note — which has
   to stay legible until cleared photography lands — sits top-right of it. */
.photo--hero { min-height: 272px; align-items: flex-start; justify-content: flex-end; text-align: right; padding: 1.5rem; }

/* Measured values are set the way every other figure on the page is set: a
   condensed label, a lining figure, and a shelf rule with the accent clip.
   These were lavender pills, which read as consumer status badges and were
   the loudest thing in light mode and invisible in dark. */
/* A marker carries a figure, so its accent is a real bar. Set --v inline to the
   percentage the marker reports and the bar draws to it: 96% draws wide, 28%
   draws narrow. Markers reporting a COUNT (42 SKUs, 3 gaps) or no figure at all
   set no --v and default to a full-width underline — a partial bar under a count
   would be measuring against a whole that does not exist. */
.marker {
  --v: 100;
  position: relative; display: flex; align-items: baseline; gap: .75rem;
  padding: .1rem 0 .5rem; border-bottom: 2px solid var(--line);
  font-family: var(--display); font-stretch: 76%;
  font-size: .7rem; font-weight: 600; letter-spacing: .11em;
  text-transform: uppercase; color: var(--ink-3);
}
.marker::after {
  content: ""; position: absolute; left: 0; bottom: -2px;
  width: calc(var(--v) * 1%); height: 2px; background: var(--accent);
  transition: width .5s cubic-bezier(.2,.7,.3,1);
}
@media (prefers-reduced-motion: reduce) { .marker::after { transition: none; } }
.marker strong {
  margin-left: auto;
  font-stretch: 85%; font-size: var(--step-0); font-weight: 600;
  letter-spacing: -.02em; text-transform: none; color: var(--ink);
  font-variant-numeric: tabular-nums lining;
}
.markers { display: grid; gap: .85rem; }


/* Hero -------------------------------------------------------- */
.hero { padding-block: clamp(2.2rem, 4vw, 4rem) clamp(2rem, 3.5vw, 3.4rem); border-bottom: 2px solid var(--line); position: relative; }
.hero::after { content:""; position:absolute; left: var(--bay-edge); bottom:-2px; width: clamp(48px,9vw,104px); height:2px; background: var(--accent); }
.hero__grid { display: grid; gap: 3rem; grid-template-columns: minmax(0,1.05fr) minmax(0,1fr); align-items: center; }
@media (max-width: 900px) { .hero__grid { grid-template-columns: 1fr; } }

/* ---- Cinematic hero -----------------------------------------------------
   The hero no longer paints a ground of its own — the page is already --night —
   so all that is left of it is the glow. That glow lives on a masked pseudo-
   element rather than on the hero's own background, because a background would
   stop dead at the hero's bottom edge and redraw the seam this whole change
   exists to remove. The mask fades it out over the last third instead. */
.hero--cinema {
  background: none;
  border-bottom: 0;
  padding-block: clamp(3rem, 6vw, 6.5rem) clamp(3rem, 6vw, 6rem);
  position: relative; overflow: hidden;
}
.hero--cinema::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    radial-gradient(90% 80% at 50% 45%, rgba(162,88,188,.30), transparent 65%);
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
.hero--cinema::after { background: var(--accent); }
/* z-index 1 lifts the copy clear of the glow and the dust canvas (both
   z-index 0). No left gutter: the hero is one centred column. */
.hero--cinema > .wrap { position: relative; z-index: 1; padding-left: 0; }
.band > .wrap { position: relative; z-index: 1; }

/* The masthead used to need a :has(.hero--cinema) carve-out so a white strip
   would not sit over the dark opening shot. The page is night throughout now,
   so the normal header already matches and the carve-out is gone. */
/* One centred column since 2026-09-25 (BENEFITS.md, "K2"). The shelf picture
   is gone: the dust field is the picture. js/field.js parks one purple dot
   (the rep) under the button and sends ten out to ring the headline. The veil
   behind the copy dims the dust under the words so the eye goes to the text
   and the dots frame it. */
.hero--cinema { min-height: min(86svh, 820px); display: grid; align-items: center; }
.hero--cinema .hero__grid { grid-template-columns: 1fr; justify-items: center; text-align: center; }
.hero--cinema .hero__grid > .stack { position: relative; z-index: 0; align-items: center; }
.hero--cinema .hero__grid > .stack::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  inset: -14% -10%;
  background: radial-gradient(closest-side, rgba(23,19,30,.86) 0%, rgba(23,19,30,.72) 55%, rgba(23,19,30,0) 100%);
}
.hero--cinema h1 {
  font-size: clamp(2.3rem, 1.2rem + 3.9vw, 4.9rem);
  font-stretch: 92%; letter-spacing: -.035em; line-height: 1.02; text-wrap: balance;
}
.hero--cinema .btn-row { justify-content: center; }
.hero--cinema .lede { color: var(--ink-2); font-size: var(--step-1); }
.hero--cinema .eyebrow { color: var(--accent); }
/* Cleared explicitly rather than inherited, so the ghost button stays a hairline
   outline over the glow instead of punching a flat --paper rectangle through it. */
.hero--cinema .btn--ghost { background: transparent; color: var(--ink); border-color: #4f4560; }
.hero--cinema .btn--ghost:hover { border-color: var(--ink-2); }
.hero--cinema .marker,
.hero--cinema .marker:not(:has(strong)) { color: var(--ink-2); border-bottom-color: var(--line); }
.hero--cinema .marker strong { color: var(--ink); }
.hero--cinema .small.muted { color: var(--ink-3); }

/* Backed-by strip under the hero. Equal light tiles, because the three logos
   arrive on three different grounds and none holds up directly on --night. */
.backers { padding-block: 0 clamp(2rem, 4vw, 3.2rem); }
.backers__row { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.backers__row .eyebrow { margin: 0; color: var(--ink-3); }
.backers__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: .75rem; }
.backers__tile {
  display: flex; align-items: center; justify-content: center;
  width: 12.5rem; height: 4.5rem; padding: .7rem 1.1rem;
  background: #f9f8f4; border-radius: var(--radius);
}
.backers__tile img { display: block; max-width: 100%; max-height: 100%; width: auto; height: auto; }
@media (max-width: 560px) {
  .backers__list { width: 100%; }
  .backers__tile { flex: 1 1 100%; width: auto; height: 4rem; }
  .backers__tile img { max-width: 12rem; }
}


/* A real shelf photograph, once one exists. Sits in the Capture panel where the
   placeholder was, at the same rhythm as the rest of the bay. */
.capture {
  display: block; width: 100%; height: auto; aspect-ratio: 4 / 3;
  object-fit: cover; border-radius: var(--radius);
  border: 1px solid var(--line-soft); background: var(--surface);
}

/* ---- Full-bleed bands ---------------------------------------------------
   Each band carries one drawing, one headline and one line of copy — the
   picture does the arguing, so they read fast even though they add height.
   They no longer paint a ground: the page is already --night, and an opaque
   ground is exactly what used to cut them out of it. What is left is a purple
   wash, masked so it rises and falls instead of switching on at an edge. */
.band {
  padding-block: clamp(3rem, 5.5vw, 5.5rem);
  border-bottom: 0; position: relative; overflow: hidden;
}
.band::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(90% 80% at 22% 30%, rgba(162,88,188,.26), transparent 62%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 26%, #000 74%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 26%, #000 74%, transparent 100%);
}
.band h2 { font-size: clamp(1.9rem, 1.3rem + 2.4vw, 3.1rem); letter-spacing: -.03em; line-height: 1.03; max-width: 15ch; }
.band .lede { color: var(--ink-2); max-width: 44ch; }
.band__grid { display: grid; gap: clamp(2rem, 4vw, 4rem); grid-template-columns: minmax(0,1fr) minmax(0,.85fr); align-items: center; }
.band--flip::before { background-image: radial-gradient(85% 80% at 78% 40%, rgba(162,88,188,.24), transparent 62%); }
.band--flip .band__grid { grid-template-columns: minmax(0,.9fr) minmax(0,1.1fr); }
@media (max-width: 900px) { .band__grid, .band--flip .band__grid { grid-template-columns: 1fr; } }

/* ---- Three stepped pictures ---------------------------------------------
   The rep's list and the fog map work the same way as the
   race: the markup renders the LAST frame so the picture still tells its story
   with JavaScript off, and js/site.js then steps `data-step` from 0 while CSS
   draws each state. A [data-at] element belongs to the steps it names. */
[data-seq] [data-at] { opacity: 0; transition: opacity .35s; }
[data-seq][data-step="0"] [data-at~="0"],
[data-seq][data-step="1"] [data-at~="1"],
[data-seq][data-step="2"] [data-at~="2"],
[data-seq][data-step="3"] [data-at~="3"] { opacity: 1; }

/* The handset: the rep's list, and the job that wrote itself onto it. */
.tasks { display: grid; gap: 1.1rem; }
.phone svg { width: min(100%, 300px); height: auto; display: block; margin-inline: auto; }
.ph-body { fill: #241d2e; stroke: #4a3f58; stroke-width: 1.5; }
.ph-screen { fill: #100d16; }
.ph-notch { fill: #4a3f58; }
.ph-eyebrow { fill: #9d92aa; font-family: var(--display); font-stretch: 76%; font-size: 9.5px; font-weight: 600; letter-spacing: .13em; }
.pl-clock { fill: var(--ink); font-family: var(--display); font-stretch: 85%; font-size: 15px;
  font-weight: 600; text-anchor: end; font-variant-numeric: tabular-nums lining; }
.pl-box { fill: #1c1725; stroke: #3a3145; stroke-width: 1; transition: stroke .45s; }
.pl-dot { fill: none; stroke: #5b5069; stroke-width: 1.6; transition: fill .45s, stroke .45s; }
.pl-h { fill: #f7f3f9; font-family: var(--display); font-stretch: 88%; font-size: 13.5px; font-weight: 600; transition: fill .45s; }
.pl-b { fill: #a99fb5; font-size: 11.5px; }
.pl-tag { font-family: var(--display); font-stretch: 76%; font-size: 8.5px; font-weight: 700;
  letter-spacing: .09em; fill: var(--accent); }
.pl-tag--done { fill: var(--good); }
.pl-tick { fill: none; stroke: #10251a; stroke-width: 2.2; stroke-linecap: round;
  stroke-linejoin: round; opacity: 0; transition: opacity .3s; }
.pl-foot { fill: #7d7389; font-size: 11px; text-anchor: middle; }
/* The two jobs the rep already had slide down to make room for the new one. */
.pl-rest { transition: transform .5s cubic-bezier(.2,.8,.3,1); }
.tasks[data-step="1"] .pl-rest, .tasks[data-step="2"] .pl-rest { transform: translate(0, 156px); }
.pl-new { opacity: 0; transform: translate(0, 74px); transition: opacity .35s, transform .45s cubic-bezier(.2,.8,.3,1); }
.tasks[data-step="1"] .pl-new, .tasks[data-step="2"] .pl-new { opacity: 1; transform: translate(0, 86px); }
.pl-new .pl-box { stroke: var(--accent); stroke-width: 1.8; }
.pl-new .pl-dot { stroke: var(--accent); }
.tasks[data-step="2"] .pl-new .pl-box { stroke: var(--good); }
.tasks[data-step="2"] .pl-new .pl-dot { fill: var(--good); stroke: var(--good); }
.tasks[data-step="2"] .pl-new .pl-tick { opacity: 1; }
.tasks[data-step="2"] .pl-new .pl-h { fill: #a99fb5; }
.tasks__line {
  font-family: var(--display); font-stretch: 76%; font-size: .72rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3);
  /* Bulgarian takes two lines here; balanced so the second is not one word. */
  text-align: center; text-wrap: balance; max-width: 34ch; margin-inline: auto;
}

/* The estate under fog, then without it. The old map was 300 grey dots and 7
   that pulsed: always the same, no old way and no payoff. Now the fog rolls
   off left to right and a mask hands each store its colour as it passes. */
.fog { display: grid; gap: .8rem; }
/* No panel around it any more: the EU outline is its own frame, and a box with
   big empty corners round a tall silhouette only drew attention to them. */
/* 570, not 520: the viewBox gained 60 units of margin on the right for the
   bubbles to hang in, and this keeps the land itself the size it was. */
.fog__map svg { width: 100%; max-width: 570px; height: auto; display: block; margin-inline: auto; }
.fog-land { fill: #272031; fill-rule: evenodd; stroke: #473c5c; stroke-width: .9; }
.fog-ok { fill: #8d82a0; }
.fog-warn { fill: var(--warn); }
.fog-seen { fill: #8d82a0; opacity: calc((1 - var(--p, 1)) * .8); }
/* The seven were salmon red. They are the brand purple now (Ivan,
   2026-09-20). The number on them is set in --accent-ink, the same near
   black every purple button on the site puts its label in. */
.fog-rank circle { fill: var(--accent); stroke: var(--accent-ink); stroke-width: 1.5; }
.fog-rank text { fill: var(--accent-ink); font-family: var(--display); font-stretch: 85%;
  font-size: 11px; font-weight: 700; text-anchor: middle; }
/* Dark rather than transparent: if the turbulence filter is ever unavailable
   this still reads as an unlit estate instead of vanishing. */
.fog__cloud rect { fill: #1b1622; }
/* One travelling gradient drives both masks: where it hands a store its colour
   it takes the cloud away, so the two always agree and the fog has a soft edge
   rather than the straight side of the rectangle that draws it.

   --p is the scroll position, 0 to 1, set by js/site.js. No transition: the
   reader's scroll IS the timeline, and easing it would only lag behind them.
   The default of 1 is what an unscripted page gets — fully clear. */
.fog__wipe { transform: translateX(calc(-1350px + var(--p, 1) * 790px)); }

/* ---- The pinned map -----------------------------------------------------
   The section holds still while the fog comes off. .pin is a tall spacer and
   .pin__stage sticks to the top inside it, so the page scrolls normally the
   whole time — the scrollbar never stops, the wheel is never swallowed, and
   Page Down still works. Scroll-jacking would break all three.

   Everything here is behind .pinned, which js/site.js adds. Without it this is
   an ordinary band: a reader with no JavaScript must not be given an extra
   screen of empty scrolling for an effect that is never going to run. */
.band--pin { padding-block: 0; }
/* overflow:hidden on the band would make it the sticky container and the stage
   would never stick. The wash inside is absolutely positioned to the band, so
   it has nothing to spill. */
.band--pin, .band--pin .pin { overflow: visible; }
.band--pin .pin__stage > .wrap { padding-block: clamp(2rem, 4vw, 4rem); }
/* The stage is the box the section was: the bay label and the hero's dust
   canvas are positioned against it. */
.band--pin .pin__stage { position: relative; }

.band--pin.pinned .pin { height: calc(100svh + var(--reveal)); }
.band--pin.pinned .pin__stage {
  position: sticky; top: var(--head, 0px);
  min-height: calc(100svh - var(--head, 0px));
  display: grid; align-content: center;
}
/* How much scrolling the reveal is worth. Raised from 78svh on Ivan's call
   (2026-09-20): at the old setting the seven bubbles landed about 32px of
   scroll apart, less than half a wheel notch, so they arrived in a clump and
   the detail went past unread. At this setting each one is worth roughly a
   notch of its own. Still the only section on the page that holds. */
.band--pin.pinned { --reveal: clamp(1100px, 190svh, 1800px); }

/* Inside the pin the map has a ceiling as well as a width: the stage is one
   screen tall and the headline, the captions and the legend have to fit in it
   alongside. Without this the bottom of a tall map is cut off on a short
   window, which is exactly where the payoff is. */
.band--pin.pinned .fog__map svg {
  max-height: calc(100svh - var(--head, 0px) - 15rem);
}
@media (max-width: 900px) {
  .band--pin.pinned .fog__map svg { max-height: calc(100svh - var(--head, 0px) - 24rem); }
}

/* The payoff beat: the seven that need someone ping in turn once the fog is
   gone. Same trick the old estate map used, but now it fires at the end of a
   story instead of pulsing forever at a reader who has not been told why. */
.fog-rank__ping { fill: none; stroke: var(--accent); stroke-width: 1.6; opacity: 0; }
.fog[data-step="2"] .fog-rank__ping { animation: fog-ping 2.6s ease-out infinite backwards; }
@keyframes fog-ping {
  0%       { r: 10.5; opacity: .7; }
  60%, 100% { r: 32; opacity: 0; }
}

/* A bubble on each of the seven saying what is wrong in that store. It is
   drawn inside the map's viewBox, so it shrinks with the map and two bubbles
   can never collide at any width — the sides and widths were picked against
   EU_RANKED in build.py. The fill is opaque: a callout sitting over the
   coastline has to be read, not shone through.

   They land one at a time, in rank order, over the tail of the reveal: each
   one carries its own --t, the point in the scroll it arrives at, and rides
   the same --p the fog does. No timer, so a reader who stops scrolling stops
   the story where they left it. With no JavaScript, or reduced motion, --p is
   1 and all seven are simply there. */
.fog-call { opacity: calc((var(--p, 1) - var(--t, 0)) * 20); }
.fog-call rect { fill: var(--accent-ink); stroke: var(--accent); stroke-width: 1.2; }
/* The elbow: out of the disc at a slant, then flat into the box. */
.fog-call polyline { fill: none; stroke: var(--accent); stroke-width: 1.2;
  stroke-linejoin: round; stroke-linecap: round; }
.fog-call text { fill: var(--ink); font-family: var(--display); font-stretch: 85%;
  font-size: 18px; font-weight: 600; text-anchor: middle; }
/* The second line answers the first. Green is the site's one "good" colour,
   the same one the hero shelf turns when the gap is filled. */
/* Two classes deep on purpose: a bare .fog-call__fix loses to `.fog-call text`
   above, which is one class and one element, and the line came out white. */
.fog-call .fog-call__fix { fill: var(--good); font-size: 13px; font-weight: 700;
  letter-spacing: .1em; }

/* Bulgarian runs a third longer than English, so the caption is allowed to
   drop onto its own row rather than shrink and leave a word stranded. */
.fog__cap { display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: baseline; gap: .3rem 1rem; }
.fog__when, .fog__note { display: grid; }
.fog__when i, .fog__note i { grid-area: 1 / 1; font-style: normal; }
.fog__when { font-family: var(--display); font-stretch: 76%; font-size: .72rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; color: var(--accent); }
.fog__note { flex: 0 0 auto; font-size: .85rem; color: var(--ink-2); justify-items: end; text-align: right; }
.fog__foot { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.fog__key { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .25rem 1.1rem;
  font-size: .75rem; color: var(--ink-3); }
/* Hidden rather than faded: a legend that keeps the space of the items it is
   not showing leaves a hole where the old step's words were. */
.fog__key li { display: none; align-items: center; gap: .45rem; }
/* The swatch is a pseudo-element, not a span: suppress_notes() strips empty
   spans on the way out, and an inline --sw is how .marker already carries a
   per-item value. */
.fog__key li::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--sw, #8d82a0);
}
.fog[data-step="0"] .fog__key li[data-at~="0"],
.fog[data-step="1"] .fog__key li[data-at~="1"],
.fog[data-step="2"] .fog__key li[data-at~="2"] { display: inline-flex; }

/* Phones: the map's two captions stop sharing a line. */
@media (max-width: 560px) {
  /* The map shrinks to 358px but keeps its 640-wide viewBox, so everything on
     it shrinks with it and the ranking numbers stop being readable. `r` as a
     CSS property is modern-browser only; anything older keeps the attribute,
     which is the desktop size, so this degrades to "small but correct". */
  .fog-rank circle { r: 15; }
  .fog-rank text { font-size: 15px; }
  .fog-ok { r: 4.2; }
  .fog-warn { r: 5.2; }
  .fog-seen { r: 4.6; }
  .fog__cap { flex-direction: column; gap: .35rem; }
  .fog__note { justify-items: start; text-align: left; }
  .fog__foot { flex-direction: column; align-items: flex-start; }
}

/* Reduced motion: every picture is already rendered at its last step, so there
   is nothing left to animate — the fog would only obscure it. */
@media (prefers-reduced-motion: reduce) {
  .fog__cloud, .fog__seen { display: none; }
  /* Matched on the same selector that switches the ping on: a media query adds
     no specificity, so a bare .fog-rank__ping here loses to it and the rings
     keep pulsing at exactly the reader who asked them not to. */
  .fog[data-step="2"] .fog-rank__ping { animation: none; }
  .pl-rest, .pl-new, .pl-box, .pl-dot,
  .pl-h, .pl-tick, [data-seq] [data-at] { transition: none; }
}

/* The shot: photograph with the read-out plated onto its lower-left corner.
   This is the one moment of the page — a shelf becomes numbers — so the
   markers belong on the image, not stacked beneath it. */
.shot { position: relative; }
.shot .markers {
  position: absolute; left: 0; bottom: 0; width: min(300px, 76%);
  background: var(--paper);
  border-top: 2px solid var(--accent); border-right: 1px solid var(--line);
  padding: 1.05rem 1.15rem 1.15rem; gap: .8rem;
}
.shot .marker:last-child { border-bottom: 0; padding-bottom: 0; }
.shot .marker:last-child::after { display: none; }
/* A marker with no figure is a state, not a measurement. */
.marker:not(:has(strong)) { color: var(--ink-2); }

/* Trust bar --------------------------------------------------- */
.trust { display: flex; flex-wrap: wrap; gap: .6rem 2.2rem; color: var(--ink-2); }
.trust li { list-style: none; margin: 0; }

/* Flow / comparison ------------------------------------------- */
.flow { display: flex; flex-wrap: wrap; align-items: center; gap: .55rem; font-size: .95rem; }
/* --surface-2 and --accent-wash are the same value, so the two rows were
   identical. The old process is drawn as outline, ours as filled. */
.flow span { background: none; border: 1px solid var(--line); border-radius: 2px; padding: .4rem .75rem; color: var(--ink-3); }
.flow--ih span { background: var(--accent-wash); border-color: transparent; color: var(--accent-deep); font-weight: 500; }
.flow em { color: var(--ink-3); font-style: normal; }

/* Forms ------------------------------------------------------- */
.form { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.field { display: flex; flex-direction: column; gap: .35rem; }
.field--full { grid-column: 1 / -1; }
label { font-size: .92rem; font-weight: 500; }
input, select, textarea {
  font: inherit; font-size: .97rem; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius); padding: .65rem .75rem; width: 100%;
}
textarea { min-height: 110px; resize: vertical; }
.form__actions { grid-column: 1/-1; display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }
.form__msg { grid-column: 1/-1; margin: 0; font-size: .95rem; color: var(--good); }
.form__msg[data-msg="failed"] { color: var(--bad); }
/* Honeypot: off-screen rather than display:none, which some bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Tables ------------------------------------------------------ */
.tablewrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: .95rem; }
th, td { text-align: left; padding: .7rem .9rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
th { font-weight: 600; color: var(--ink); border-bottom-color: var(--line); }

/* Notices ----------------------------------------------------- */
.notice { border: 1px solid var(--warn); border-left-width: 3px; border-radius: var(--radius); padding: 1rem 1.2rem; color: var(--ink-2); background: var(--surface); font-size: .95rem; }
.notice strong { color: var(--ink); }

/* Footer ------------------------------------------------------ */
/* No margin: the CTA band and the footer are both --surface, and a 2rem strip
   of paper between them read as a seam rather than a break. */
.foot { border-top: 1px solid var(--line); background: var(--surface); padding-block: 3rem 2rem; }
.foot__grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.foot h4 { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-3); font-weight: 600; }
.foot ul { list-style: none; padding: 0; margin: .7rem 0 0; }
.foot li { margin-bottom: .45rem; }
.foot a { color: var(--ink-2); text-decoration: none; font-size: .94rem; }
.foot a:hover { color: var(--ink); text-decoration: underline; }
.foot__base { margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; color: var(--ink-3); font-size: var(--step--1); }
.linkbtn { background: none; border: 0; padding: 0; font: inherit; font-size: .94rem; color: var(--ink-2); cursor: pointer; }
.linkbtn:hover { color: var(--ink); text-decoration: underline; }

/* Analytics consent banner. Both choices share one button style: rejecting
   must be as easy and as prominent as accepting. */
.consent { position: fixed; left: 0; right: 0; bottom: 0; z-index: 90; background: var(--paper); border-top: 1px solid var(--line); box-shadow: 0 -6px 24px rgba(0, 0, 0, .08); padding-block: 1rem; }
.consent__inner { display: flex; flex-wrap: wrap; gap: 1rem 1.5rem; align-items: center; justify-content: space-between; }
.consent p { margin: 0; max-width: 68ch; color: var(--ink-2); font-size: .95rem; }

/* Outstanding content. These are deliberately visible in staging — the point is
   that nobody can mistake a placeholder for finished copy, or miss one on the
   way to publishing. build.py refuses a production build while any remain. */
.todo {
  color: var(--warn);
  border: 1px dashed var(--warn); border-radius: 2px;
  padding: .05em .4em; font-style: normal; font-weight: 500;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
}

.skip { position: absolute; left: -9999px; }
.skip:focus { left: 1rem; top: .6rem; z-index: 100; background: var(--accent); color: var(--accent-ink); padding: .6rem 1rem; border-radius: var(--radius); }


/* Leadership portraits -------------------------------------- */
.person { align-items: flex-start; }
.person__photo {
  width: 92px; height: 92px; border-radius: 50%;
  object-fit: cover; display: block;
  border: 1px solid var(--line); background: var(--surface-2);
}
@media (min-width: 700px) { .person__photo { width: 104px; height: 104px; } }


/* Article ----------------------------------------------------- */
.article { max-width: 72ch; }
.article h2 { font-size: var(--step-2); margin-top: 2.2rem; }
.article h3 { margin-top: 1.8rem; }
.article p, .article ul { margin-top: 1rem; }
.article__meta { color: var(--ink-3); font-size: var(--step--1); }
.article__lede { font-size: var(--step-1); color: var(--ink-2); line-height: 1.5; }
.legal { max-width: 74ch; }
.legal h2 { font-size: var(--step-1); margin-top: 2rem; }
.legal p, .legal ul { margin-top: .8rem; }
.legal table { margin-top: 1rem; }


/* Hero markers settle onto the shelf photograph on load — the product thesis
   (a photo becomes numbers) as a single orchestrated moment. */
@media (prefers-reduced-motion: no-preference) {
  .hero .markers .marker {
    opacity: 0;
    animation: settle .55s cubic-bezier(.2,.7,.3,1) forwards;
  }
  .hero .markers .marker:nth-child(1) { animation-delay: .25s; }
  .hero .markers .marker:nth-child(2) { animation-delay: .40s; }
  .hero .markers .marker:nth-child(3) { animation-delay: .55s; }
}
@keyframes settle {
  from { opacity: 0; transform: translateY(-7px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

/* Table figures align in columns */
td, th { font-variant-numeric: tabular-nums lining; }


/* Sequence numerals. Used only for See -> Understand -> Act, where the order is
   the information. Everywhere else sections are named, not numbered. */
.seq { counter-reset: seq; }
.seq > * { counter-increment: seq; position: relative; padding-top: 2.2rem; }
.seq > *::before {
  content: counter(seq, decimal-leading-zero);
  position: absolute; top: 0; left: 0;
  font-family: var(--display); font-stretch: 76%;
  font-size: .78rem; font-weight: 600; letter-spacing: .12em;
  color: var(--accent); font-variant-numeric: tabular-nums lining;
  border-top: 2px solid var(--accent); padding-top: .5rem; min-width: 34px;
}

/* Demo page: Google's pop-up booking button, made bigger. Its own class (qxCTlb)
   is set by Google's stylesheet; .booking in front beats it without !important. */
.booking { margin-bottom: 2rem; }
.booking .qxCTlb { font-size: 1.2rem; font-weight: 600; padding: 1.1rem 2.4rem; height: auto; line-height: 1.2; border-radius: 12px; }
