/* ==========================================================================
   /product — layout, components and motion.

   Reads tokens.css and invents no values of its own. If a colour, size,
   duration or easing appears here as a literal, it is either a structural
   constant (a border of 1px, a grid of 12) or it is a bug.

   Order below is: reset, materials, primitives, then one block per section in
   the order the visitor meets them, then motion, then the responsive and
   reduced-motion overrides that have to come last to win.
   ========================================================================== */

/* ── Reset ────────────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* Anchors in the sticky-nav era need this or a jumped-to heading lands
     underneath the header. The value is the nav height plus breathing room. */
  scroll-padding-top: 6.5rem;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--ivory);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* The page is one long document with a full-bleed hero; without this a
     sub-pixel overflow anywhere produces a horizontal scrollbar on mobile. */
  overflow-x: hidden;
}

h1, h2, h3, p, ul, ol, dl, dd, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, canvas, picture { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; }

/* One focus treatment for the whole page, and it is deliberately loud.
   :focus-visible rather than :focus so a mouse user never sees it and a
   keyboard user always does. */
:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--violet-lift);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

.skip {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-4);
  z-index: 100;
  padding: var(--sp-3) var(--sp-4);
  background: var(--ink-3);
  border: 1px solid var(--edge-2);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  transform: translateY(-160%);
  transition: transform var(--d-base) var(--ease-out);
}
.skip:focus-visible { transform: none; }

/* ── Illustration materials ───────────────────────────────────────────────
   The palette public/product/iso.js draws with. It lives here rather than in
   tokens.css because these are compositions of the base palette rather than
   members of it — a designer changing the brand changes tokens.css, and these
   follow.

   The light direction is fixed for the whole page: key from high behind-left.
   In the isometric projection that makes every top face the brightest plane,
   every left-facing plane mid, and every right-facing plane darkest. Three
   tones per material, and committing to them across every drawing is most of
   what makes the set look commissioned rather than assembled. */
:root {
  /* Black anodised aluminium — housings, mounts, the bed of the bench. */
  --iso-anod-top:    #20222C;
  --iso-anod-left:   #15161D;
  --iso-anod-right:  #0E0F14;

  /* Machined steel — the rails. The one place warm silver shows up. */
  --iso-steel-top:   #6A6E7E;
  --iso-steel-left:  #42454F;
  --iso-steel-right: #2B2D34;

  /* Optical glass — the gate plates. Drawn at fill-opacity 0.5 by iso.js, so
     these read lighter in isolation than they do on the page. */
  --iso-glass-top:   #9DB4C6;
  --iso-glass-left:  #5A6C7C;
  --iso-glass-right: #3C4854;
  --iso-glass-lit:   var(--cyan);

  /* Paper — the message. The only near-white solid in the system, which is
     what makes the envelope the eye's entry point into every drawing. */
  --iso-paper-top:    var(--ivory);
  --iso-paper-left:   #C2BFB6;
  --iso-paper-right:  #9A978F;
  --iso-paper-crease: #8A877F;

  --iso-edge:     var(--edge-lit);
  --iso-beam:     var(--violet);
  --iso-beam-dim: var(--violet-deep);
  --iso-tick:     var(--silver);
}

.iso { width: 100%; height: auto; }

/* ── Layout primitives ────────────────────────────────────────────────────
   One grid, declared once. Sections opt into columns by placing children on
   named lines rather than by nesting another grid, which is what keeps the
   optical alignment true across sections that look nothing alike. */

.section {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-block: var(--sp-section);
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: var(--sp-7);
}

/* Section headers sit on the left two-thirds. Not centred: a centred header
   over a left-aligned body is the most common way an otherwise disciplined
   page starts to look like a template. */
.section__head { grid-column: 1 / -1; max-width: 64ch; }

@media (min-width: 60rem) {
  .section__head { grid-column: 1 / span 8; }
}

/* ── Typography ───────────────────────────────────────────────────────────── */

.eyebrow {
  font-family: var(--mono);
  font-size: var(--t-eyebrow);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--gray);
  margin-bottom: var(--sp-4);
}
/* The tick before an eyebrow. A 1px rule rather than a dot or a chevron —
   it is the same hairline the drawings are made of. */
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 1.75rem;
  height: 1px;
  background: var(--violet);
  vertical-align: middle;
  margin-right: var(--sp-3);
  transform: translateY(-1px);
}

.h1 {
  font-size: var(--t-h1);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-h);
  font-weight: 500;
  text-wrap: balance;
}

.h2 {
  font-size: var(--t-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-h);
  font-weight: 500;
  text-wrap: balance;
}

.lede {
  margin-top: var(--sp-5);
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--gray);
  max-width: 58ch;
}
.lede--wide { max-width: 64ch; }
.lede--center { margin-inline: auto; text-align: center; }

/* The display face, used in exactly two places on this page: the core-value
   turn and the closing line. Any more and it reads as pastiche. */
.stmt {
  font-size: var(--t-h1);
  line-height: 1.06;
  letter-spacing: var(--tr-h);
  font-weight: 500;
  display: grid;
}
.stmt--center { text-align: center; justify-items: center; }
.stmt__line { display: block; }
.stmt__line--em {
  font-family: var(--display);
  font-weight: 400;
  font-style: italic;
  letter-spacing: -0.015em;
  color: var(--silver);
  /* The serif's cap height sits differently from the grotesk's; without this
     the two lines look mis-leaded even though the line-box is identical. */
  padding-block: 0.06em;
}

.link {
  color: var(--violet-lift);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--violet-lift) 35%, transparent);
  padding-bottom: 1px;
  transition: border-color var(--d-fast) var(--ease-out), color var(--d-fast) var(--ease-out);
}
.link:hover { border-bottom-color: var(--violet-lift); }

/* ── Buttons ──────────────────────────────────────────────────────────────
   Two only. A page with one job does not need a third. */

.btn {
  --btn-py: 0.8rem;
  --btn-px: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--btn-py) var(--btn-px);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  font-weight: 500;
  letter-spacing: 0;
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid transparent;
  transition:
    background-color var(--d-fast) var(--ease-out),
    border-color var(--d-fast) var(--ease-out),
    color var(--d-fast) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}
.btn--sm { --btn-py: 0.55rem; --btn-px: 1rem; font-size: var(--t-xs); }
.btn--lg { --btn-py: 1rem; --btn-px: 2rem; font-size: var(--t-body); }

.btn--primary {
  background: var(--violet);
  color: #FFFFFF;      /* 4.9:1 on --violet. Ivory would measure 3.6:1 and fail. */
  border-color: var(--violet);
}
.btn--primary:hover { background: var(--violet-lift); border-color: var(--violet-lift); }

.btn--ghost {
  color: var(--ivory);
  border-color: var(--edge-2);
  background: transparent;
}
.btn--ghost:hover { border-color: var(--edge-lit); background: var(--ink-2); }

/* ── Nav ──────────────────────────────────────────────────────────────────
   Sticky, and it earns that by being 56px tall and mostly transparent until
   the page has scrolled. `.is-stuck` is set by motion.js. */

.nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  background: color-mix(in srgb, var(--ink) 72%, transparent);
  border-bottom: 1px solid transparent;
  transition: background-color var(--d-base) var(--ease-out), border-color var(--d-base) var(--ease-out);
}
/* backdrop-filter is progressive: where it is unsupported the colour-mix
   background alone is still a legible bar. */
@supports (backdrop-filter: blur(1px)) {
  .nav { backdrop-filter: blur(14px) saturate(1.3); }
}
.nav.is-stuck {
  background: color-mix(in srgb, var(--ink) 88%, transparent);
  border-bottom-color: var(--edge);
}

.nav__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--sp-3) var(--gutter);
  display: flex;
  align-items: center;
  gap: var(--sp-6);
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  text-decoration: none;
  font-size: var(--t-sm);
  font-weight: 500;
  letter-spacing: -0.01em;
  margin-right: auto;
}
.nav__mark { width: 1.5rem; height: 1.5rem; flex: none; }
.nav__mark svg { width: 100%; height: 100%; }

.nav__links { display: none; gap: var(--sp-6); font-size: var(--t-sm); }
.nav__links a {
  text-decoration: none;
  color: var(--gray);
  padding-block: var(--sp-2);
  position: relative;
  transition: color var(--d-fast) var(--ease-out);
}
.nav__links a:hover { color: var(--ivory); }
/* Scroll-spy. The underline is drawn rather than a border so it can animate
   from the centre out without shifting the text. */
.nav__links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: var(--violet);
  transform: scaleX(0);
  transition: transform var(--d-base) var(--ease-out);
}
.nav__links a.is-current { color: var(--ivory); }
.nav__links a.is-current::after { transform: scaleX(1); }

@media (min-width: 60rem) {
  .nav__links { display: flex; }
}
@media (max-width: 30rem) {
  /* Below this the brand name and the CTA cannot both fit without the name
     truncating mid-word, and the CTA is the one that matters. */
  .nav__name { display: none; }
}

/* ── Hero ─────────────────────────────────────────────────────────────────
   A single grid whose stage spans every cell, with the content laid over it.
   The stage is full-bleed and the content respects the page gutter, which is
   what lets the sculpture run to the edge of the viewport while the headline
   stays on the grid. */

.hero {
  position: relative;
  min-height: min(92svh, 54rem);
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  align-content: center;
  max-width: var(--page-max);
  margin-inline: auto;
  padding: calc(var(--sp-section) * 0.9) var(--gutter) var(--sp-section);
  column-gap: var(--gutter);
  isolation: isolate;
}

.hero__stage {
  position: absolute;
  inset: 0;
  z-index: var(--z-canvas);
  /* Bleeds past the page max-width to the viewport edge. */
  left: 50%;
  translate: -50% 0;
  width: 100vw;
  overflow: hidden;
  pointer-events: none;
}
.hero__stage::after {
  /* Grades the sculpture out from under the text. A gradient used as a
     legibility device rather than as decoration — without it the headline sits
     on whatever the shader happens to be doing behind it, which is the one
     thing that would make this page unreadable. */
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to right, var(--ink) 4%, color-mix(in srgb, var(--ink) 55%, transparent) 46%, transparent 72%),
    linear-gradient(to bottom, var(--ink) 0%, transparent 22%, transparent 62%, var(--ink) 100%);
}

.hero__poster,
.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__canvas {
  opacity: 0;
  transition: opacity var(--d-slow) var(--ease-out);
}
/* Set by boot.js only after a frame has actually been drawn — a linked WebGL
   context that has presented nothing would otherwise fade the poster out over
   an empty canvas and make the hero blink. */
.hero__stage.is-live .hero__canvas { opacity: 1; }
.hero__stage.is-live .hero__poster { opacity: 0; transition: opacity var(--d-slow) var(--ease-out); }

.hero__content {
  position: relative;
  z-index: var(--z-content);
  grid-column: 1 / -1;
}
@media (min-width: 60rem) {
  .hero__content { grid-column: 1 / span 7; }
}

.hero__headline {
  font-size: var(--t-display);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-display);
  font-weight: 500;
  display: grid;
}
.hero__line { display: block; }
.hero__line--em {
  font-family: var(--display);
  font-weight: 400;
  font-style: italic;
  letter-spacing: -0.02em;
  color: var(--silver);
  padding-block: 0.04em;
}

.hero__lede {
  margin-top: var(--sp-5);
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--gray);
  max-width: 46ch;
}

.hero__actions {
  margin-top: var(--sp-6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
}

.hero__footnote {
  margin-top: var(--sp-4);
  font-size: var(--t-xs);
  color: var(--gray-dim);
}

.hero__caption {
  position: relative;
  z-index: var(--z-content);
  grid-column: 1 / -1;
  margin-top: var(--sp-8);
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.04em;
  color: var(--gray-dim);
}
@media (min-width: 60rem) {
  .hero__caption { grid-column: 1 / span 5; margin-top: var(--sp-9); }
}

/* ── Credibility ──────────────────────────────────────────────────────────── */

.section--credibility { row-gap: var(--sp-8); border-top: 1px solid var(--edge); }

.metrics {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-6) var(--gutter);
}
@media (min-width: 48rem) {
  .metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.metric { display: grid; gap: var(--sp-1); border-top: 1px solid var(--edge); padding-top: var(--sp-4); }
.metric__value {
  font-size: clamp(2rem, 1.4rem + 2.4vw, 3rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.metric__label { font-size: var(--t-sm); color: var(--ivory-dim); }
.metric__note { font-size: var(--t-xs); color: var(--gray-dim); }

.standards { grid-column: 1 / -1; display: grid; gap: var(--sp-7); }
@media (min-width: 60rem) {
  .standards { grid-column: 1 / -1; grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr)); column-gap: var(--gutter); }
  .standards__head { grid-column: 1 / span 5; }
  .standards__list { grid-column: 7 / span 6; }
}

.standards__list { display: grid; gap: 0; }
.std {
  display: grid;
  grid-template-columns: 7rem 1fr;
  gap: var(--sp-1) var(--sp-4);
  padding-block: var(--sp-4);
  border-top: 1px solid var(--edge);
  align-items: baseline;
}
.std:last-child { border-bottom: 1px solid var(--edge); }
.std__rfc {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--cyan);
  letter-spacing: 0.02em;
}
.std__name { font-size: var(--t-body); font-weight: 500; }
.std__note { grid-column: 2; font-size: var(--t-sm); color: var(--gray); }

.disclaimer {
  grid-column: 1 / -1;
  font-size: var(--t-sm);
  color: var(--gray-dim);
  border-left: 1px solid var(--violet);
  padding-left: var(--sp-4);
  max-width: 62ch;
}

/* ── The gap ──────────────────────────────────────────────────────────────── */

.gapgrid {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--sp-8) var(--gutter);
}
@media (min-width: 60rem) {
  /* Deliberately asymmetric: the record-checker column is narrower than the
     receiver column, because it has less to say. The layout makes the argument
     before the words do. */
  .gapgrid { grid-template-columns: 4fr 7fr; align-items: start; }
}

.gapcol { display: grid; gap: var(--sp-4); align-content: start; }
.gapcol__art {
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--ink-2);
  padding: var(--sp-6);
  display: grid;
  place-items: center;
  min-height: 13rem;
}
.gapcol--guess .gapcol__art { background: var(--ink-2); }
.gapcol--evidence .gapcol__art {
  background:
    radial-gradient(120% 90% at 22% 40%, var(--violet-wash) 0%, transparent 62%),
    var(--ink-2);
  border-color: var(--edge-2);
}
/* Smaller than the bench opposite it, but not lost. The size difference is
   part of the argument — this column has less to show — and at 9rem it read as
   a mistake rather than as a decision. */
.gapcol--guess .iso { max-width: 11.5rem; }

.gapcol__label { font-size: var(--t-h3); font-weight: 500; letter-spacing: -0.015em; }
.gapcol__caption { font-size: var(--t-sm); color: var(--gray); }

.gapcol__rows { display: grid; margin-top: var(--sp-2); }
.gaprow {
  display: grid;
  grid-template-columns: minmax(6.5rem, 9rem) 1fr;
  gap: var(--sp-4);
  padding-block: var(--sp-3);
  border-top: 1px solid var(--edge);
  font-size: var(--t-sm);
}
.gaprow:last-child { border-bottom: 1px solid var(--edge); }
.gaprow dt { color: var(--gray-dim); }
.gaprow dd { margin: 0; color: var(--ivory-dim); }
/* The last row of each column is the verdict, and it is the payoff. */
.gaprow:last-child dd { color: var(--ivory); font-weight: 500; }
.gapcol--guess .gaprow:last-child dd { color: var(--gray); }
.gapcol--evidence .gaprow:last-child dd { color: var(--cyan); }

.gapclose {
  grid-column: 1 / -1;
  font-size: var(--t-lede);
  line-height: 1.55;
  color: var(--ivory-dim);
  max-width: 62ch;
}
@media (min-width: 60rem) {
  .gapclose { grid-column: 4 / span 8; }
}

/* ── Capabilities ─────────────────────────────────────────────────────────── */

.capgrid {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--gutter);
}
@media (min-width: 48rem) { .capgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.cap {
  display: grid;
  gap: var(--sp-3);
  align-content: start;
  padding: var(--sp-6);
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--ink-2);
  transition: border-color var(--d-base) var(--ease-out), background-color var(--d-base) var(--ease-out);
}
.cap:hover { border-color: var(--edge-2); background: var(--ink-3); }

.cap__art {
  height: 7.5rem;
  display: grid;
  place-items: center;
  margin-bottom: var(--sp-2);
}
.cap__art .iso { max-height: 7.5rem; width: auto; max-width: 100%; }

.cap__index {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--violet-lift);
  letter-spacing: 0.08em;
}
.cap__title { font-size: var(--t-h3); font-weight: 500; letter-spacing: -0.015em; line-height: 1.25; }
.cap__benefit { font-size: var(--t-sm); color: var(--cyan); }
.cap__body { font-size: var(--t-sm); color: var(--gray); }

.cap__checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--edge);
}
.cap__checks li {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.02em;
  color: var(--gray-dim);
  border: 1px solid var(--edge);
  border-radius: var(--r-sm);
  padding: 0.25rem 0.5rem;
}

/* ── Interlude ────────────────────────────────────────────────────────────
   Full-bleed, and the only raster on the page. It breaks the rhythm between
   the two densest sections rather than adding information.

   The image is cropped by the container, not by the file: the height is set
   here and object-fit does the rest, so a source that arrives at a different
   ratio than requested — which the image gateway does routinely — still
   composes correctly. */

.interlude {
  position: relative;
  width: 100vw;
  left: 50%;
  translate: -50% 0;
  margin-block: var(--sp-6);
}

.interlude__frame {
  position: relative;
  width: 100%;
  /* Sized by height, NOT by aspect-ratio.
     aspect-ratio: 21/9 together with a max-height does not produce a capped
     full-width band — once the height clamps, the ratio pulls the WIDTH in to
     match, and a full-bleed band silently became 1045px wide inside a 1440px
     viewport. Stating the height and letting the width fill is the only way to
     get both. */
  height: clamp(13rem, 26vw, 26rem);
  overflow: hidden;
}

/* The <picture> has to be stretched explicitly. It is the img's containing
   block, and a percentage height on the img resolves against it — left at
   `height: auto` the img's `height: 100%` has nothing to resolve against and
   the band collapses to the image's intrinsic ratio. */
.interlude__frame picture {
  width: 100%;
  height: 100%;
}

.interlude__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Grades the image into the page at both edges. Without it the band has two
   hard horizontal seams, which on a near-black page reads as a mistake rather
   than as a full-bleed image. */
.interlude__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    var(--ink) 0%,
    transparent 26%,
    transparent 70%,
    var(--ink) 100%
  );
}

/* Under the band, not over it.
   This line was first set across the bottom of the image: a near-white
   sentence over the brightest part of the picture — the lit glass plates —
   and it was simply unreadable. A scrim heavy enough to fix that would have
   obscured the image it sat on, which defeats the point of having one. Below
   the band, the type is legible at full contrast and the picture stays whole. */
.interlude__line {
  max-width: min(56ch, calc(100% - var(--gutter) * 2));
  margin: clamp(var(--sp-5), 3vw, var(--sp-7)) auto 0;
  text-align: center;
  text-wrap: balance;
  font-size: var(--t-lede);
  line-height: 1.5;
  color: var(--ivory-dim);
}

/* ── Showcase ─────────────────────────────────────────────────────────────── */

.scorer {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--sp-7) var(--gutter);
  align-items: start;
}
@media (min-width: 60rem) {
  .scorer { grid-template-columns: 5fr 7fr; }
  .scorer__readout { position: sticky; top: 6rem; }
}

.scorer__readout { display: grid; gap: var(--sp-5); }

.readout {
  border: 1px solid var(--edge-2);
  border-radius: var(--r-lg);
  background:
    radial-gradient(110% 80% at 20% 0%, var(--violet-wash) 0%, transparent 60%),
    var(--ink-2);
  padding: var(--sp-6);
  display: grid;
  gap: var(--sp-2);
}
.readout__label {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--gray);
}
.readout__value {
  font-size: clamp(3.25rem, 2.2rem + 4vw, 5rem);
  line-height: 1;
  letter-spacing: -0.04em;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 0.1em;
}
.readout__of { font-size: 0.34em; color: var(--gray-dim); letter-spacing: 0; }

/* Verdict colour is never the only carrier — the word itself is the label, and
   the mark before it differs per tone. A page that says "at risk" only in
   amber says nothing to a colourblind reader. */
.verdict {
  font-size: var(--t-body);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.verdict::before {
  content: "";
  width: 0.6rem; height: 0.6rem;
  border-radius: 2px;
  background: currentColor;
}
.verdict--pass { color: var(--pass); }
.verdict--warn { color: var(--warn); }
.verdict--warn::before { transform: rotate(45deg); }
.verdict--fail { color: var(--fail); }
.verdict--fail::before { border-radius: 50%; }

.readout__note { font-size: var(--t-sm); color: var(--gray); }

.scorer__bench {
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--ink-2);
  padding: var(--sp-4);
}
.scorer__caveat {
  font-size: var(--t-xs);
  line-height: 1.6;
  color: var(--gray-dim);
  border-left: 1px solid var(--edge-2);
  padding-left: var(--sp-4);
}

.finds { display: grid; gap: var(--sp-2); }

.find {
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
  background: var(--ink-2);
  overflow: hidden;
  transition: border-color var(--d-base) var(--ease-out), background-color var(--d-base) var(--ease-out);
}
.find.is-on { border-color: var(--edge-2); background: var(--ink-3); }

.find__toggle {
  width: 100%;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  text-align: left;
}

/* The switch. Built from two elements rather than a styled checkbox because
   this is not a form control — nothing is submitted — and a <button> that
   reports aria-pressed is what assistive technology can actually describe. */
.find__switch {
  width: 2.25rem;
  height: 1.25rem;
  border-radius: 999px;
  border: 1px solid var(--edge-2);
  background: var(--ink);
  padding: 2px;
  flex: none;
  transition: background-color var(--d-base) var(--ease-out), border-color var(--d-base) var(--ease-out);
}
.find__knob {
  display: block;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  background: var(--gray-dim);
  transition: transform var(--d-base) var(--ease-out), background-color var(--d-base) var(--ease-out);
}
.find.is-on .find__switch { background: color-mix(in srgb, var(--violet) 26%, transparent); border-color: var(--violet); }
.find.is-on .find__knob { transform: translateX(1rem); background: var(--violet-lift); }

.find__text { display: grid; gap: var(--sp-1); min-width: 0; }
.find__meta { display: flex; align-items: center; gap: var(--sp-3); }
.find__group {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-dim);
}
.sev {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.1rem 0.35rem;
  border-radius: var(--r-sm);
  border: 1px solid currentColor;
}
.sev--fail { color: var(--fail); }
.sev--warn { color: var(--warn); }

.find__title { font-size: var(--t-sm); color: var(--ivory-dim); }
.find.is-on .find__title { color: var(--ivory); }

.find__weight {
  font-family: var(--mono);
  font-size: var(--t-sm);
  font-variant-numeric: tabular-nums;
  color: var(--gray-dim);
  transition: color var(--d-base) var(--ease-out);
}
.find.is-on .find__weight { color: var(--fail); }

.find__detail {
  font-size: var(--t-xs);
  line-height: 1.6;
  color: var(--gray);
  padding: 0 var(--sp-4) var(--sp-4) calc(var(--sp-4) + 2.25rem + var(--sp-4));
  margin: 0;
}

/* ── Process ──────────────────────────────────────────────────────────────── */

.process__art {
  grid-column: 1 / -1;
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background:
    radial-gradient(90% 120% at 50% 100%, var(--violet-wash) 0%, transparent 65%),
    var(--ink-2);
  padding: var(--sp-6) var(--sp-5);
}

.steps { grid-column: 1 / -1; display: grid; gap: var(--gutter); counter-reset: step; }
@media (min-width: 48rem) { .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.step { display: grid; gap: var(--sp-2); align-content: start; border-top: 1px solid var(--edge); padding-top: var(--sp-4); }
.step__n {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--violet-lift);
  letter-spacing: 0.08em;
}
.step__title { font-size: var(--t-h3); font-weight: 500; letter-spacing: -0.015em; }
.step__body { font-size: var(--t-sm); color: var(--gray); }

.aside {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--sp-3);
  align-content: start;
  padding: var(--sp-6);
  border: 1px solid var(--edge);
  border-left: 1px solid var(--cyan);
  border-radius: var(--r-lg);
  background: var(--ink-2);
  max-width: 46rem;
}
.aside__title { font-size: var(--t-h3); font-weight: 500; letter-spacing: -0.015em; }
.aside__body { font-size: var(--t-sm); color: var(--gray); }
.aside .link { justify-self: start; font-size: var(--t-sm); }

/* ── Outcomes ─────────────────────────────────────────────────────────────
   Editorial rather than card-based: a rule, a tag, a headline, and two
   columns of body. It reads as a page from a journal, which is the one place
   on this site where that register is right. */

.cases { grid-column: 1 / -1; display: grid; }

.case {
  display: grid;
  gap: var(--sp-3);
  padding-block: var(--sp-7);
  border-top: 1px solid var(--edge);
}
.case:last-child { border-bottom: 1px solid var(--edge); }

@media (min-width: 60rem) {
  .case {
    grid-template-columns: 6rem minmax(0, 5fr) minmax(0, 5fr);
    column-gap: var(--gutter);
    align-items: start;
  }
  /* Three rows: the headline across both text columns, then the symptom and
     its resolution SIDE BY SIDE, then the cost underneath.
     The rows are stated explicitly because auto-placement puts the resolution
     on a row of its own — the problem and the fix end up stacked rather than
     opposed, which is the whole reason this section is two columns. */
  .case__tag        { grid-row: 1 / span 3; }
  .case__headline   { grid-column: 2 / span 2; grid-row: 1; }
  .case__symptom    { grid-column: 2; grid-row: 2; }
  .case__resolution { grid-column: 3; grid-row: 2; }
  .case__cost       { grid-column: 2 / span 2; grid-row: 3; }
}

.case__tag {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cyan);
}
.case__headline {
  font-size: var(--t-h2);
  line-height: 1.18;
  letter-spacing: var(--tr-h);
  font-weight: 500;
  text-wrap: balance;
}
.case__symptom, .case__resolution { font-size: var(--t-sm); color: var(--gray); }
.case__resolution { color: var(--ivory-dim); }
.case__cost {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--fail);
  justify-self: start;
}
.case__cost-label {
  color: var(--gray-dim);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ── Final ────────────────────────────────────────────────────────────────── */

.section--final {
  place-items: center;
  text-align: center;
  padding-block: calc(var(--sp-section) * 1.15);
  border-top: 1px solid var(--edge);
  background:
    radial-gradient(70% 120% at 50% 100%, var(--violet-wash) 0%, transparent 68%);
}
.section--final > * { grid-column: 1 / -1; }
.final__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); justify-content: center; margin-top: var(--sp-6); }

/* ── Footer ───────────────────────────────────────────────────────────────── */

.foot {
  border-top: 1px solid var(--edge);
  padding: var(--sp-8) var(--gutter) var(--sp-6);
}
.foot__inner {
  max-width: var(--page-max);
  margin-inline: auto;
  display: grid;
  gap: var(--sp-7);
}
@media (min-width: 60rem) {
  .foot__inner { grid-template-columns: 5fr 7fr; }
}
.foot__statement { font-size: var(--t-sm); color: var(--gray); max-width: 42ch; }
.foot__groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--sp-6); }
.foot__title {
  font-family: var(--mono);
  font-size: var(--t-xs);
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--gray-dim);
  margin-bottom: var(--sp-3);
}
.foot__group ul { display: grid; gap: var(--sp-2); }
.foot__group a {
  font-size: var(--t-sm);
  color: var(--ivory-dim);
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out);
}
.foot__group a:hover { color: var(--ivory); }
.foot__legal {
  max-width: var(--page-max);
  margin: var(--sp-8) auto 0;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--edge);
  font-size: var(--t-xs);
  color: var(--gray-dim);
}

/* ── Motion ───────────────────────────────────────────────────────────────
   Reveals are opt-in via [data-reveal] and are ARMED by motion.js rather than
   being hidden by default in CSS.

   That order matters more than it looks. If the initial state were
   `opacity: 0` in the stylesheet, then any failure to run the observer — a
   thrown module, a browser that never fires the callback, a headless renderer
   under a virtual time budget — would leave the entire page invisible. As
   written, the default state is VISIBLE, motion.js adds `is-armed` only after
   measuring a block as off screen, and it releases everything on a timer
   regardless. A missed frame costs an animation, never a section of text. */

[data-reveal].is-armed {
  opacity: 0;
  transform: translate3d(0, 1.25rem, 0);
}
[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--d-reveal) var(--ease-entrance),
    transform var(--d-reveal) var(--ease-entrance);
  /* Stagger within a group. --i is set in render.js per element. */
  transition-delay: calc(var(--i, 0) * 70ms);
}

/* The bench beam animates when the score changes. The transition lives on the
   segments themselves so showcase.js only has to write new attribute values —
   it never touches timing. */
.iso-beam-seg {
  transition:
    stroke-width var(--d-slow) var(--ease-out),
    opacity var(--d-slow) var(--ease-out);
}

/* Magnetic buttons. The translate is written by motion.js as a custom
   property, so the transition can live here and the script stays declarative. */
[data-magnetic] {
  translate: var(--mx, 0) var(--my, 0);
  transition: translate var(--d-base) var(--ease-out);
}
[data-magnetic].is-pulling { transition: translate 80ms linear; }

/* ── Reduced motion ───────────────────────────────────────────────────────
   Not "no animation" — "no motion". Opacity still fades, because a fade is
   not vestibular. What goes is every translate, the magnetic pull, and the
   beam's animated re-draw.

   The 3D layer handles this separately: scene.js renders exactly one correctly
   lit frame and then stops. See its own note. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  [data-reveal].is-armed { transform: none; }
  [data-reveal].is-revealed {
    transform: none;
    transition: opacity var(--d-base) linear;
    transition-delay: 0ms;
  }

  [data-magnetic] { translate: none !important; transition: none; }

  .iso-beam-seg { transition: none; }

  .btn, .find, .cap, .nav, .nav__links a::after { transition-duration: 1ms; }
}
