/* ============================================================================
   NOVA — PRIMITIVES                                            spec §2.5, §2.6
   ----------------------------------------------------------------------------
   Depends on tokens.css. Raw values appear nowhere below except where a value
   is genuinely structural (grid counts, 1px hairlines, keyframe percentages).
   ========================================================================= */

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

/* [hidden] must WIN. The UA sheet sets [hidden]{display:none} at specificity
   (0,1,0), so any component rule that sets `display` — .trust .card{display:flex}
   at (0,2,0), for instance — silently un-hides it. That is how the "You can
   challenge it" card, hidden precisely because the feature does not exist yet,
   ended up rendering on the live page. Anything relying on `hidden` to keep an
   unshipped claim off the site needs this to be unconditional. */
[hidden] { display: none !important; }
/* The page colour lives on <html>, NOT on <body>, and that is load-bearing
   rather than stylistic. An opaque background on <body> paints above body's own
   fixed, z-index:0 children, so the §3.9 full-viewport WebGL layer was
   completely invisible — correctly positioned, correctly drawn, and painted
   over. On <html> it becomes the root canvas background, which paints first and
   behind everything, so a background layer can actually sit in front of it.
   Anything full-bleed behind content depends on this staying here. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--bg-base);
}
body {
  margin: 0;
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--lh-base);
  letter-spacing: var(--tr-base);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* No horizontal overflow, ever — checked at 390px. */
  overflow-x: hidden;
}
img, svg, video { display: block; max-width: 100%; }

/* Every element carrying a score, grade, price or percentage. A 14/15 that
   jitters as it counts is the clearest possible tell of an amateur build. */
.tnum, [data-tnum] { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }

/* ---- Type scale --------------------------------------------------------- */
.t-xs   { font-size: var(--text-xs);   line-height: var(--lh-xs);   letter-spacing: var(--tr-xs); }
.t-sm   { font-size: var(--text-sm);   line-height: var(--lh-sm);   letter-spacing: var(--tr-sm); }
.t-base { font-size: var(--text-base); line-height: var(--lh-base); letter-spacing: var(--tr-base); }
.t-lg   { font-size: var(--text-lg);   line-height: var(--lh-lg);   letter-spacing: var(--tr-lg); }
.t-xl   { font-size: var(--text-xl);   line-height: var(--lh-xl);   letter-spacing: var(--tr-xl); }
.t-2xl  { font-size: var(--text-2xl);  line-height: var(--lh-2xl);  letter-spacing: var(--tr-2xl); }
.t-3xl  { font-size: var(--text-3xl);  line-height: var(--lh-3xl);  letter-spacing: var(--tr-3xl); }

h1, h2, h3, h4 { margin: 0; font-weight: 620; }
/* Optical alignment: hang the side bearing so the visual edge of the text,
   not its glyph box, meets the grid. Only worth doing at display sizes. */
.t-2xl, .t-3xl { margin-left: -0.045em; }
p { margin: 0; max-width: var(--measure); }
.dim  { color: var(--text-secondary); }
.dim2 { color: var(--text-tertiary); }

/* ---- Section — rhythm + measure ---------------------------------------- */
.section { padding-block: var(--section-y); }
.section--tight { padding-block: var(--s-12); }
.section__inner {
  width: 100%; max-width: 1200px; margin-inline: auto;
  padding-inline: var(--s-6);
}
@media (min-width: 768px) { .section__inner { padding-inline: var(--s-8); } }

/* ---- Bento — 12-col desktop, 1-col mobile ------------------------------ */
.bento { display: grid; grid-template-columns: 1fr; gap: var(--s-4); }
@media (min-width: 768px) {
  .bento { grid-template-columns: repeat(12, 1fr); gap: var(--s-6); }
  .bento > [data-span="4"]  { grid-column: span 4; }
  .bento > [data-span="6"]  { grid-column: span 6; }
  .bento > [data-span="8"]  { grid-column: span 8; }
  .bento > [data-span="12"] { grid-column: span 12; }
}

/* ---- Card — one radius per card, never mixed --------------------------- */
.card {
  background: var(--bg-raised);
  border: 1px solid var(--border-hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--e-1);
  padding: var(--s-6);
  transition: transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
/* 2px lift. No colour change, no shadow bloom. */
@media (hover: hover) {
  a.card:hover, button.card:hover, .card--interactive:hover {
    transform: translateY(-2px);
    box-shadow: var(--e-2);
  }
}
a.card, button.card { display: block; text-align: left; text-decoration: none;
  color: inherit; font: inherit; width: 100%; cursor: pointer; }

/* ---- Eyebrow — keeps the existing Nova /* divider tic ------------------ */
.eyebrow {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--text-xs); line-height: 1;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: var(--s-4);
}
.eyebrow i { font-style: normal; color: var(--accent); }

/* ---- StatTile — accent on the figure only ------------------------------ */
.stat { display: flex; flex-direction: column; gap: var(--s-1); }
.stat__fig {
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  font-size: var(--text-xl); line-height: 1.05; letter-spacing: var(--tr-xl);
  font-weight: 640;
  color: var(--text-primary);
}
/* Exactly one figure per page may take the accent. */
.stat--key .stat__fig { color: var(--accent); }
.stat__label { font-size: var(--text-sm); line-height: var(--lh-sm); color: var(--text-secondary); }

/* ---- ProofChip — a quote that lives INSIDE a feature ------------------- */
.proof {
  display: flex; gap: var(--s-3); align-items: flex-start;
  margin-top: var(--s-4); padding-top: var(--s-4);
  border-top: 1px solid var(--border-hairline);
  font-size: var(--text-sm); line-height: var(--lh-sm);
}
.proof__q { color: var(--text-secondary); }
.proof__who { color: var(--text-tertiary); white-space: nowrap; }
/* A placeholder chip must never reach production — the build gate greps for
   this attribute (spec §1.6). */
[data-placeholder] { outline: 1px dashed var(--signal-warn); outline-offset: 3px; }

/* ============================================================================
   THE SIX STATES                                                    spec §2.5
   default · hover · focus-visible · active · disabled · loading
   ========================================================================= */

/* Focus is never removed. Custom ring on every focusable element, including
   cards and the skip link. */
:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text-primary);
  --btn-bd: var(--border-interactive);
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-2);
  /* 44px, comfortably past the 24x24 floor in WCAG 2.5.8 */
  min-height: 44px; padding: 0 var(--s-6);
  border: 1px solid var(--btn-bd);
  /* PILL, matching the app. The product's buttons are fully rounded, and a
     10px radius on the marketing site made the two read as different products
     the moment anyone signed up. */
  border-radius: 999px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit; font-size: var(--text-sm); font-weight: 560;
  letter-spacing: 0;
  text-decoration: none; cursor: pointer;
  transition: background var(--dur-instant) var(--ease-out),
              border-color var(--dur-instant) var(--ease-out),
              color var(--dur-instant) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
/* The app's primary button carries a soft accent glow. Reproduced with
   color-mix off --accent rather than a second hard-coded blue, so it follows
   the theme and cannot drift from the token.

   The COLOUR is deliberately not copied. The app uses #0A84FF, which measures
   about 3.1:1 under white text and cannot legally carry a label; --accent
   (#1D5BD6) was chosen by measurement to clear 4.5:1 both as link text and
   under white. So this matches the app's SHAPE and its glow, never its
   contrast failure. */
.btn--primary {
  --btn-bg: var(--accent); --btn-fg: var(--accent-contrast); --btn-bd: var(--accent);
  box-shadow: 0 0 28px color-mix(in srgb, var(--accent) 34%, transparent);
}
@media (hover: hover) {
  /* The app lifts its buttons 1px on hover. Transform only, so it stays on the
     compositor. */
  .btn:hover { transform: translateY(-1px); }
  .btn--primary:hover {
    box-shadow: 0 0 44px color-mix(in srgb, var(--accent) 46%, transparent);
  }
}
@media (prefers-reduced-motion: reduce) {
  .btn:hover { transform: none; }
}
.btn--quiet   { --btn-bd: transparent; --btn-fg: var(--text-secondary); }

@media (hover: hover) {
  .btn:hover { background: color-mix(in srgb, var(--text-primary) 6%, transparent); }
  .btn--primary:hover { --btn-bg: var(--accent-hover); --btn-bd: var(--accent-hover); background: var(--accent-hover); }
  .btn--quiet:hover { --btn-fg: var(--text-primary); background: transparent; }
}
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; transform: none;
}
/* Loading keeps the button's exact width so nothing shifts, hides the label
   from sight but not from assistive tech, and shows a sweeping bar rather than
   a spinner. */
.btn[aria-busy="true"] { pointer-events: none; }
.btn[aria-busy="true"] > * { visibility: hidden; }
.btn[aria-busy="true"]::after {
  content: ''; position: absolute; left: var(--s-4); right: var(--s-4);
  height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--btn-fg) 70%, transparent), transparent);
  animation: btn-sweep 900ms var(--ease-in-out) infinite;
}
@keyframes btn-sweep {
  0%   { transform: translateX(-60%) scaleX(.35); }
  50%  { transform: translateX(0)     scaleX(.6); }
  100% { transform: translateX(60%)  scaleX(.35); }
}

/* ---- Skeleton — matches the shape of what it replaces ------------------ */
.skel {
  background: color-mix(in srgb, var(--text-primary) 8%, transparent);
  border-radius: var(--r-sm);
  position: relative; overflow: hidden;
}
.skel::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--text-primary) 6%, transparent), transparent);
  animation: skel-sweep 1200ms var(--ease-in-out) infinite;
}
@keyframes skel-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.skel--line { height: 12px; }
.skel--line + .skel--line { margin-top: var(--s-2); }
.skel--fig  { height: 40px; width: 92px; }

/* ---- Empty and error are designed screens, not afterthoughts ----------- */
.state {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-8) var(--s-6);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-lg);
  background: var(--bg-subtle);
}
.state__title { font-size: var(--text-lg); line-height: var(--lh-lg); letter-spacing: var(--tr-lg); font-weight: 620; }
.state__body  { color: var(--text-secondary); font-size: var(--text-sm); line-height: var(--lh-sm); }
.state--error { border-style: solid; border-color: color-mix(in srgb, var(--signal-danger) 45%, transparent); }
.state--error .state__title { color: var(--signal-danger); }

/* ---- Signals ----------------------------------------------------------- */
.sig { display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--text-sm); line-height: 1; font-weight: 560;
  min-height: 24px; padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm); }
.sig::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.sig--strong { color: var(--signal-strong); background: color-mix(in srgb, var(--signal-strong) 10%, transparent); }
.sig--warn   { color: var(--signal-warn);   background: color-mix(in srgb, var(--signal-warn) 10%, transparent); }
.sig--danger { color: var(--signal-danger); background: color-mix(in srgb, var(--signal-danger) 10%, transparent); }

/* ---- Skip link --------------------------------------------------------- */
.skip {
  position: absolute; left: var(--s-4); top: var(--s-4); z-index: 100;
  padding: var(--s-3) var(--s-4);
  background: var(--bg-raised); color: var(--text-primary);
  border: 1px solid var(--border-interactive); border-radius: var(--r-md);
  text-decoration: none; font-size: var(--text-sm);
  transform: translateY(-200%);
}
.skip:focus-visible { transform: translateY(0); }

/* ---- Section reveal — staggered, opacity-only under reduced motion -----
   The hidden start state is gated behind .js, which chrome.js sets on <html>
   as its first act. Without that gate a reveal is content that only exists if
   JavaScript runs AND an IntersectionObserver fires: a script error, a blocked
   file, or an observer that never triggers leaves real copy permanently
   invisible. Default is visible; hiding is the enhancement. */
.js .reveal { opacity: 0; transform: translateY(var(--reveal-rise));
  transition: opacity var(--dur-reveal) var(--ease-reveal),
            transform var(--dur-reveal) var(--ease-reveal);
  /* The rise is the only thing moving; telling the compositor so keeps a
     1.4s transform off the main thread while the WebGL field is drawing. */
  will-change: opacity, transform; }
.js .reveal.is-in { opacity: 1; transform: none; will-change: auto; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; will-change: auto; }
}

.sr {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Links inside running text. Nothing styled them, so the legal pages showed
   the browser's default #0000EE on a near-black page — 2.1:1, close to
   invisible. The accent passes in both themes (#5B9DFF on #08090A 7.3:1,
   #1D5BD6 on #FCFCFD 5.8:1), and the underline keeps a link from being
   signalled by colour alone. */
main a:not([class]) { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
@media (hover: hover) { main a:not([class]):hover { color: var(--accent-hover); } }
