:root {
  color-scheme: dark;
  --ink: #f4f1e8;
  --ink-muted: #c8c4b8;
  --ink-faint: #9c988c;
  /* Tertiary text on the two surfaces that are NOT --paper, so --ink-faint is the
     wrong measurement there. Both replace hardcoded values that failed WCAG AA
     against the surface they actually sit on:
       --ink-screen was #777b84 -> 4.38:1 on .hero-stage #111319; now 5.30:1.
       --ink-cream  was #77736b -> 3.72:1 on .feature-list #e9e4d8; now 4.97:1.
     Cool for the device stage, warm for the cream panel, matching each surface. */
  /* #858992 measured 5.30:1 stand-alone but the hero stage layers a faint
     ambient-window silhouette (.hero-stage::after) under .stage-caption,
     which lightens the effective backdrop and drops the real composite to
     4.38:1 -- under the 4.5:1 AA floor for this 10px text. #98a0aa clears
     the composited worst case (~5.97:1) with margin, not just the flat swatch. */
  --ink-screen: #98a0aa;
  --ink-cream: #655f57;
  --paper: #090a0c;
  --paper-raised: #111317;
  --paper-soft: #171a1f;
  --line: rgba(244, 241, 232, 0.16);
  --line-strong: rgba(244, 241, 232, 0.28);
  --mint: #8ff2c6;
  --mint-deep: #1fbc83;
  --warning: #ffc77b;
  --danger: #ff8e8e;
  --shell: min(1180px, calc(100vw - 48px));
  --radius-sm: 12px;
  --radius-md: 22px;
  --radius-lg: 34px;
  /* Layered elevation. On a dark sheet, depth is not one shadow: it is a contact
     shadow (tight, grounds the edge to the surface), a key shadow (mid, carries
     the direction), and an ambient shadow (wide, soft, separates the plane).
     --edge-light is the inset hairline -- the top edge catching light -- which is
     the cue that actually makes a near-black card read as a physical object
     rather than a border with a smudge under it. */
  --elev-1: 0 1px 2px rgba(0, 0, 0, .5), 0 4px 12px rgba(0, 0, 0, .26);
  --elev-2: 0 1px 2px rgba(0, 0, 0, .55), 0 8px 20px rgba(0, 0, 0, .3), 0 26px 50px rgba(0, 0, 0, .32);
  --elev-3: 0 2px 4px rgba(0, 0, 0, .5), 0 12px 30px rgba(0, 0, 0, .36), 0 44px 90px rgba(0, 0, 0, .44);
  --edge-light: inset 0 1px 0 rgba(244, 241, 232, .055);
  --sans: "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono: "SFMono-Regular", ui-monospace, "Cascadia Mono", monospace;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button, input { font: inherit; }
img { display: block; max-width: 100%; }

.shell { width: var(--shell); margin-inline: auto; }

.skip-link {
  position: fixed;
  left: 16px;
  top: 12px;
  z-index: 100;
  transform: translateY(-150%);
  padding: 10px 14px;
  border-radius: 10px;
  background: var(--ink);
  color: var(--paper);
  font-weight: 700;
}

.skip-link:focus { transform: translateY(0); }

:focus-visible {
  outline: 3px solid var(--mint);
  outline-offset: 4px;
}

/* `outline-offset: 4px` draws the ring OUTSIDE the control, so the ring is measured
   against the PARENT surface, not the control's own background. .release-card is
   var(--mint) with padding clamp(34px, 6vw, 72px) -- far wider than the 4px offset --
   so the mint ring around its .button-light (index.html:247) landed on mint at
   1.00:1 and was invisible. --paper on mint is 14.79:1. This is the only light-
   surface container in the sheet that holds a focusable child; the other light
   surfaces (.pricing-badge, .detected-badge, .release-icon, .signal-dot,
   .feature-list) contain no focusable element, and .skip-link sits over the
   .site-header #090a0c where the mint ring is already 14.79:1. */
.release-card :focus-visible {
  outline-color: var(--paper);
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  min-height: 72px;
  padding: 0 max(24px, calc((100vw - 1180px) / 2));
  display: flex;
  align-items: center;
  gap: 28px;
  background: #090a0c;
  border-bottom: 1px solid var(--line);
}

.brand, .footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 17px;
  font-weight: 680;
  letter-spacing: -.02em;
}

.brand img, .footer-brand img { border-radius: 9px; }

.nav-links {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 26px;
  color: var(--ink-muted);
  font-size: 14px;
}

.nav-links a { transition: color .18s ease; }
.nav-links a:hover { color: var(--ink); }

.nav-links .nav-cta {
  padding: 9px 14px;
  color: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
}

.lang-switch {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  flex-shrink: 0;
  font-size: 12px;
  letter-spacing: .03em;
}

.lang-switch a {
  color: var(--ink-muted);
  text-decoration: none;
}

.lang-switch a:hover,
.lang-switch a[aria-current="page"] {
  color: var(--ink);
}

.lang-switch a[aria-current="page"] {
  font-weight: 680;
}

.menu-toggle {
  display: none;
  margin-left: auto;
  padding: 8px 12px;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: 10px;
  background: transparent;
}

.back-link {
  margin-left: auto;
  color: var(--ink-muted);
  font-size: 14px;
}

.hero {
  min-height: 720px;
  padding: 118px 0 92px;
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(440px, 1.1fr);
  align-items: center;
  gap: clamp(54px, 8vw, 110px);
}

.eyebrow {
  margin: 0 0 18px;
  color: var(--ink-muted);
  font: 600 12px/1.2 var(--mono);
  letter-spacing: .12em;
  text-transform: uppercase;
}

.signal-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--mint);
  box-shadow: 0 0 0 5px rgba(143,242,198,.09);
}

.hero h1, .download-hero h1 {
  margin: 0;
  max-width: 760px;
  font-size: var(--fs-hero, clamp(56px, 7.4vw, 102px));
  font-weight: 630;
  line-height: var(--lh-display, .92);
  letter-spacing: var(--ls-display, -.065em);
}

.hero h1 span, .download-hero h1 span { color: var(--ink-muted); }

.hero-lede, .download-hero > p:not(.eyebrow) {
  max-width: 600px;
  margin: 30px 0 0;
  color: var(--ink-muted);
  font-size: clamp(18px, 2vw, 21px);
  line-height: 1.55;
  letter-spacing: -.01em;
}

.hero-actions {
  margin-top: 34px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.button {
  min-height: 48px;
  padding: 0 19px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  border-radius: 13px;
  font-weight: 680;
  font-size: 14px;
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease, transform .12s ease;
}

.button:hover:not(:disabled) { border-color: var(--ink-muted); }

.button-primary {
  background: var(--ink);
  color: #101114;
}

.button-secondary {
  color: var(--ink);
  background: rgba(255,255,255,.03);
  border-color: var(--line-strong);
}

.button-light {
  background: #111317;
  color: var(--ink);
  align-self: center;
}

.button-full { width: 100%; }
.download-architecture-actions { display: grid; gap: 8px; }

.button:disabled {
  color: var(--ink-faint);
  background: #1d2025;
  border-color: var(--line);
  cursor: not-allowed;
}

.release-line {
  margin-top: 24px;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--ink-muted);
  font-size: 13px;
}

.hero-platforms {
  margin: 16px 0 0;
  color: var(--ink-faint);
  font: 600 12px/1.3 var(--mono);
  letter-spacing: .06em;
  text-transform: uppercase;
}

.release-line.prominent {
  width: fit-content;
  margin-top: 30px;
  padding: 12px 15px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255,255,255,.025);
}

.release-icon {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--warning);
  box-shadow: 0 0 0 5px rgba(255,199,123,.08);
}

/* The status dot.
   `[data-tone="ready"] + .release-icon` was DEAD: `+` is the adjacent-SIBLING
   combinator, so it needs .release-icon to FOLLOW the toned element, but the markup
   puts the icon first (index.html:78-79 -- icon, then `[data-release-state]`, which
   is where app.js:426 writes `dataset.tone`). The selector could never match on any
   page or locale, so the dot fell through to `--warning` amber.
   `promotable` had no rule at all, so at the exact moment app.js:102 says "Release
   checks passed; publication is pending" the dot still rendered as blocked -- the
   indicator contradicting its own label.
   Driven off `html[data-release-status]` (app.js:423) instead, which is a real
   ancestor of the icon and is set for every state. */
html[data-release-status="released"] .release-icon { background: var(--mint); }
html[data-release-status="promotable"] .release-icon {
  background: var(--mint-deep);
  box-shadow: 0 0 0 5px rgba(31,188,131,.10);
}
html[data-release-status="error"] .release-icon { background: var(--danger); }

/* Loading state: the status line breathes while the release descriptor is
   fetched — a quiet pulse, not a spinner, so the hero stays calm. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes release-checking {
    0%, 100% { opacity: .45; }
    50% { opacity: 1; }
  }
  html[data-release-status="loading"] .release-line [data-release-state] {
    animation: release-checking 1.7s ease-in-out infinite;
  }
  html[data-release-status="loading"] .release-icon {
    animation: release-checking 1.7s ease-in-out infinite;
  }

  /* -----------------------------------------------------------------------
     State-change motion: motion that means something.

     Two things on this site change because DATA changed, not because the user
     scrolled: the release status resolving from the live descriptor, and the
     platform-detection badge appearing. Each settles ONCE, at the moment the
     value actually changes. That distinction is the entire justification --
     the motion is what separates "this just changed" from "this is static
     text", so firing it on every render would turn information into
     decoration and make it a lie. Nothing here loops.
     ----------------------------------------------------------------------- */
  @keyframes status-settle {
    from { opacity: 0; transform: translateY(5px); }
    to { opacity: 1; transform: none; }
  }
  @keyframes status-dot-settle {
    from { transform: scale(.55); }
    to { transform: scale(1); }
  }
  @keyframes badge-settle {
    from { opacity: 0; transform: translateY(-4px) scale(.9); }
    to { opacity: 1; transform: none; }
  }
  html[data-status-settling="true"] .release-line [data-release-state] {
    animation: status-settle .4s cubic-bezier(.22, .61, .36, 1) both;
  }
  /* The dot may use the house spring: it is a 12px indicator settling into
     place, where a little overshoot reads as arrival rather than bounce. */
  html[data-status-settling="true"] .release-icon {
    animation: status-dot-settle .4s cubic-bezier(.22, 1.28, .36, 1) both;
  }
  /* The badge is the detection result itself, so the badge -- not the whole
     card -- is what settles in. */
  [data-detected-badge]:not([hidden]) {
    animation: badge-settle .38s cubic-bezier(.22, 1.28, .36, 1) both;
  }
}

.hero-stage {
  --tilt-x: 0;
  --tilt-y: 0;
  --scroll-depth: 0;
  position: relative;
  min-height: 470px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: 38px;
  background: #111319;
  box-shadow: var(--elev-3), var(--edge-light);
  overflow: hidden;
  perspective: 1400px;
}

/* Real layered 3D: the panel rotates in its own 3D space, driven by the same
   --tilt-x/-y contract the bento cards use (app.js is the single writer).
   Kept to a few degrees -- this is a premium product photograph responding to
   the cursor, not a gimmick spin. Its top edge stays attached to the hardware
   notch rather than drifting vertically as the page scrolls. The rotation is
   gated behind the same hover+fine-pointer / reduced-motion media query as the
   existing hover-expand mechanism below, so a touch or reduced-motion visitor
   gets the flat, always-legible presentation with zero transform cost. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* .notch-sim is the ONE element this rotation owns -- .notch-panel's own
     transform is already owned by the dormant/expand mechanism below (collapse
     to the pill vs. sit at rest), and stacking a second `transform` declaration
     on the same element is exactly the ownership collision the depth-system
     test polices (two rules fighting over one property on one element). Tilting
     the parent instead gives the whole sim real 3D depth without touching the
     property the expand animation needs for itself. */
  .notch-sim {
    transform-style: preserve-3d;
    transform-origin: top center;
    transition: transform .25s ease-out;
    transform:
      rotateX(calc(var(--tilt-y) * -6deg))
      rotateY(calc(var(--tilt-x) * 8deg));
  }
  .hero-stage.is-tilting .notch-sim { transition: transform .05s linear; }
  /* Specular sheen: a soft highlight that slides across the glass following
     the pointer, the same cue a real curved display gives under light. Pure
     presentation (aria-hidden via the stage's own decorative layers), so it
     costs nothing for assistive tech and nothing for reduced-motion users. */
  .notch-panel::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    border-radius: inherit;
    background: radial-gradient(
      220px 220px at calc(50% + var(--tilt-x) * 160%) calc(50% + var(--tilt-y) * 160%),
      rgba(255, 255, 255, .10),
      transparent 60%
    );
    opacity: 0;
    transition: opacity .3s ease;
    pointer-events: none;
  }
  .hero-stage:hover .notch-panel::before,
  .hero-stage:focus-within .notch-panel::before {
    opacity: 1;
  }
}

.screen-bar {
  position: relative;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline: 14px;
  color: var(--ink-screen);
  font: 600 10px var(--mono);
}

.camera-cutout {
  position: absolute;
  top: -20px;
  left: 50%;
  width: 154px;
  height: 46px;
  transform: translateX(-50%);
  border-radius: 0 0 18px 18px;
  background: #020203;
}

/* The app grows from the display's top edge. An explicitly positioned sim
   prevents its transform from rebasing the absolute pill onto the panel's
   normal-flow margin, which previously produced a second notch mid-screen. */
.notch-sim {
  position: absolute;
  top: 0;
  right: 20px;
  left: 20px;
  z-index: 1;
}

.notch-panel {
  position: relative;
  z-index: 1;
  width: min(92%, 500px);
  margin: 0 auto;
  padding: 18px;
  border: 1px solid rgba(255,255,255,.09);
  border-radius: 27px;
  background: rgba(3,4,6,.93);
  /* Sits a plane above .hero-stage, so it needs the tightest contact shadow on
     the page plus a brighter top edge -- otherwise it reads as pasted on. */
  box-shadow: 0 2px 4px rgba(0, 0, 0, .6), 0 14px 30px rgba(0, 0, 0, .5),
    0 40px 70px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .07);
}

.panel-topline, .panel-actions, .stage-caption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--ink-screen);
  font: 550 10px var(--mono);
  letter-spacing: .04em;
}

.panel-topline { margin-bottom: 14px; color: #c7c9ce; }
.panel-topline span:last-child { color: var(--mint); }
.notch-panel img { width: 100%; border-radius: 16px; }
.panel-actions { margin-top: 13px; }
/* z-index keeps the caption crisp above the ambient window silhouettes
   (::after, z-index 0) that now live inside the stage. */
.stage-caption { position: absolute; inset: auto 28px 24px; z-index: 1; }

/* Interactive notch simulation: the hero stage behaves like the real product —
   a dormant pill fused to the camera cutout that spring-expands into the full
   panel on hover/focus. Pill is hidden unless the interactive media query below
   matches, so touch devices and reduced-motion users keep the always-expanded
   panel (previous behaviour, fully accessible).

   `.notch-sim` carries tabindex="0", so it is a keyboard stop. It previously set
   `outline: none`, which at specificity (0,1,0) tied the global `:focus-visible`
   rule and — being later in the sheet — won, leaving the hero's one keyboard stop
   with no visible focus ring. Expansion is driven by `:focus-within` (below), not
   by the outline, so the ring costs nothing. Same defect class as the license
   input documented further down this sheet. */
.notch-pill { display: none; }

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* The interactive pill is the hardware notch in this mode. Keeping the
     decorative copy visible creates two black cutouts during handoff. */
  .camera-cutout { visibility: hidden; }
  .notch-pill {
    position: absolute;
    top: 0;
    left: 50%;
    z-index: 3;
    width: 154px;
    height: 46px;
    transform: translateX(-50%);
    border-radius: 0 0 18px 18px;
    background: #020203;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform .55s cubic-bezier(.22, 1.28, .36, 1), opacity .3s ease;
    pointer-events: none;
  }
  .notch-pill::after {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--mint);
    animation: notch-hint-pulse 2.4s ease-in-out infinite;
  }
  @keyframes notch-hint-pulse {
    0%, 100% { opacity: .35; box-shadow: 0 0 0 0 rgba(143, 242, 198, .45); }
    50% { opacity: 1; box-shadow: 0 0 0 7px rgba(143, 242, 198, 0); }
  }

  /* Dormant state: panel collapsed up into the pill. */
  .hero-stage:not(:hover):not(:focus-within) .notch-panel {
    animation: none;
    transform: translateY(-46px) scale(.34);
    opacity: 0;
    pointer-events: none;
  }
  .notch-panel {
    transform-origin: top center;
    transition: transform .55s cubic-bezier(.22, 1.28, .36, 1), opacity .38s ease;
  }

  /* Expansion: the pill stretches and hands off to the panel. */
  .hero-stage:hover .notch-pill,
  .hero-stage:focus-within .notch-pill {
    transform: translateX(-50%) scaleX(2.4) scaleY(1.35);
    opacity: 0;
  }

  /* One-shot self-demonstration. The sim only expands on hover/focus, which a
     first-time visitor has no way to discover -- the dormant stage reads as an
     empty card with a pulsing dot. app.js adds .sim-demo once per load (after
     the entrance rise, hold ~2.4s) and clears it the instant the visitor
     reaches for the sim, so it can never fight a real interaction. NO
     keyframes here: this reuses the panel's existing transform/opacity
     transition, so it cannot restart on hover-unhover and clearing the class
     mid-demo just transitions back from wherever it is. The .notch-sim hop in
     the selector is load-bearing -- the dormant rule scores 0,3,1, so this
     needs 0,4,0 to outrank it without !important. */
  .hero-stage.sim-demo .notch-sim .notch-panel {
    transform: none;
    opacity: 1;
    pointer-events: none;
  }
  .hero-stage.sim-demo .notch-sim .notch-pill {
    transform: translateX(-50%) scaleX(2.4) scaleY(1.35);
    opacity: 0;
  }
}

.proof-strip {
  width: min(1320px, calc(100vw - 32px));
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-block: 1px solid var(--line);
}

.proof-strip div { padding: 24px; border-right: 1px solid var(--line); }
.proof-strip div:last-child { border-right: 0; }
.proof-strip strong { display: block; font-size: 14px; }
.proof-strip span { display: block; margin-top: 3px; color: var(--ink-faint); font-size: 12px; }

.section { padding-block: 122px; }

.section-heading { max-width: 780px; margin-bottom: 54px; }
.section-heading.compact { max-width: 700px; }

.section-heading h2, .release-card h2, .integrity-copy h2, .blockers-section h2, .help-panel h2 {
  margin: 0;
  font-size: clamp(38px, 5vw, 66px);
  font-weight: 610;
  line-height: var(--lh-headline, 1.02);
  letter-spacing: var(--ls-headline, -.05em);
}

.section-heading > p:last-child {
  max-width: 610px;
  margin: 20px 0 0;
  color: var(--ink-muted);
  font-size: 18px;
}

.bento-grid {
  display: grid;
  grid-template-columns: 1.18fr .82fr;
  gap: 18px;
}

.feature-card {
  position: relative;
  min-height: 360px;
  padding: 30px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--elev-2), var(--edge-light);
  overflow: hidden;
}

/* Specular sheen that tracks the pointer. Kept under .feature-copy (z-index 2)
   so it lights the card surface, not the text on top of it -- at 6% white it
   must read as a highlight crossing a surface, never as a wash over copy. */
.feature-card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
  background: radial-gradient(460px circle at
    calc(50% + var(--tilt-x, 0) * 100%) calc(50% + var(--tilt-y, 0) * 100%),
    rgba(244, 241, 232, .06), rgba(244, 241, 232, 0) 62%);
}

.feature-wide { min-height: 470px; }
.feature-tall { grid-row: span 2; min-height: 740px; }
/* The tall AI card stretches to two grid rows, but its copy + screenshot only
   fill ~70% of that height, which left a dead band under the screenshot. A flex
   column pins the screenshot to the card's bottom edge, matching how the media
   card's screenshot bleeds off its edge. */
.feature-ai { display: flex; flex-direction: column; }
.feature-ai .feature-copy { margin-bottom: 38px; }
/* 01 wide + 02 tall fill rows 1-2 and 03 fills row 2, so 04 used to sit alone in
   row 3 beside an empty ~490px column. Spanning the full row closes the void; the
   list runs in two columns at this width so the card does not turn into a strip. */
.feature-list { grid-column: 1 / -1; }

.feature-copy { position: relative; z-index: 2; max-width: 500px; }
.feature-number { margin: 0 0 15px; color: var(--ink-faint); font: 600 11px var(--mono); letter-spacing: .1em; }
.feature-card h3 { margin: 0; max-width: 520px; font-size: clamp(27px, 3vw, 41px); line-height: var(--lh-headline, 1.06); letter-spacing: var(--ls-headline, -.035em); }
.feature-card p:not(.feature-number) { max-width: 480px; margin: 13px 0 0; color: var(--ink-muted); }

.feature-card img { border: 1px solid var(--line); box-shadow: 0 24px 60px rgba(0,0,0,.36); }
.feature-media img { position: absolute; width: 74%; right: -6%; bottom: -8%; border-radius: 20px 0 0 0; }
.feature-ai img { width: 112%; max-width: none; margin: auto -6% -34px; border-radius: 20px; }
.feature-window img { position: absolute; width: 38%; right: 5%; bottom: 8%; border-radius: 16px; }
.feature-window .feature-copy { max-width: 54%; }
.feature-list { background: #e9e4d8; color: #151619; }
.feature-list .feature-number { color: var(--ink-cream); }
.feature-list-thumb { position: absolute; top: 30px; right: 30px; display: block; width: 132px; height: auto; border-radius: 10px; box-shadow: var(--elev-2); }
.feature-list:has(.feature-list-thumb) h3 { max-width: calc(100% - 156px); }

.capability-list { margin: 30px 0 0; padding: 0; list-style: none; display: grid; grid-template-columns: 1fr 1fr; column-gap: 40px; }
.capability-list li { padding: 15px 0; display: grid; grid-template-columns: 120px 1fr; gap: 20px; border-top: 1px solid rgba(21,22,25,.14); }
.capability-list span { font-weight: 720; }
/* Was a hardcoded #5e5b55, which computes to 2.93:1 against --paper #090a0c --
   failing WCAG AA for body text (4.5:1) and even large text (3:1) at 13px. These are
   the automation feature descriptions (index.html:145-148), i.e. the lines that
   explain what each feature does. --ink-faint is 6.87:1 on the same background and
   is the token the rest of the sheet already uses for tertiary text. */
/* .capability-list only ever renders inside .feature-list (the cream card):
   --ink-faint is tuned for the near-black surfaces (2.27:1 here) -- use the
   cream-tuned token, matching .feature-list .feature-number just above. */
.capability-list small { color: var(--ink-cream); font-size: 13px; }

/* Workflow proof: a compact product ritual that makes the interaction model
   tangible before a visitor reaches the feature grid. The console is CSS-only on
   purpose: it stays crisp at every density, loads with the page, and has no
   invented telemetry beyond the clearly labelled preview values. */
.workflow-section { border-top: 1px solid var(--line); }
.workflow-heading {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, .62fr);
  gap: clamp(42px, 8vw, 112px);
  align-items: end;
  margin-bottom: 52px;
}
.workflow-heading h2 { margin: 0; max-width: 720px; font-size: clamp(42px, 5.3vw, 72px); font-weight: 610; line-height: 1.01; letter-spacing: -.055em; }
.workflow-heading > div:first-child > p:last-child { max-width: 600px; margin: 20px 0 0; color: var(--ink-muted); font-size: 18px; }
.workflow-console {
  min-height: 218px;
  padding: 20px;
  border: 1px solid rgba(143,242,198,.25);
  border-radius: 22px;
  background: radial-gradient(circle at 86% 8%, rgba(143,242,198,.15), transparent 38%), linear-gradient(145deg, #161d1d, #0d1012 72%);
  box-shadow: var(--elev-2), inset 0 1px 0 rgba(255,255,255,.07);
  color: var(--ink-muted);
  font-family: var(--mono);
}
.console-top { display: flex; align-items: center; gap: 8px; color: var(--ink-faint); font-size: 10px; letter-spacing: .08em; }
.console-top span:last-child { margin-left: auto; }
.console-led { width: 6px; height: 6px; border-radius: 50%; background: var(--mint); box-shadow: 0 0 0 5px rgba(143,242,198,.08); }
.console-readout { display: flex; align-items: baseline; gap: 10px; margin-top: 25px; }
.console-readout strong { color: var(--ink); font: 500 clamp(42px, 5vw, 58px)/.9 var(--sans); letter-spacing: -.06em; }
.console-readout strong span { color: var(--mint); font-size: .42em; letter-spacing: -.02em; }
.console-readout small { color: var(--ink-faint); font-size: 10px; letter-spacing: .08em; text-transform: uppercase; }
.console-bars { height: 34px; margin-top: 18px; display: flex; align-items: end; gap: 5px; }
.console-bars i { flex: 1; height: var(--bar); min-height: 6px; border-radius: 3px 3px 1px 1px; background: linear-gradient(180deg, var(--mint), rgba(143,242,198,.22)); opacity: .8; }
.console-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 17px; }
.console-chips span { padding: 5px 7px; border: 1px solid rgba(244,241,232,.13); border-radius: 6px; color: var(--ink-faint); font-size: 8px; letter-spacing: .05em; }
.workflow-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.workflow-step { min-height: 190px; padding: 24px; display: grid; grid-template-columns: auto 1fr; gap: 20px; border: 1px solid var(--line); border-radius: var(--radius-md); background: linear-gradient(150deg, rgba(255,255,255,.045), rgba(255,255,255,.012)); box-shadow: var(--elev-1), var(--edge-light); }
.workflow-index { color: var(--mint); font: 600 11px var(--mono); letter-spacing: .08em; }
.workflow-step h3 { margin: 0; font-size: 23px; letter-spacing: -.025em; }
.workflow-step p { margin: 12px 0 0; color: var(--ink-muted); font-size: 14px; line-height: 1.55; }

/* Pricing */
#pricing { border-top: 1px solid var(--line); }

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.pricing-card {
  padding: 32px 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
  box-shadow: var(--elev-1), var(--edge-light);
  display: flex;
  flex-direction: column;
}

/* The featured tier used to REPLACE --elev-1 with a bare inset ring, so the card
   meant to stand out sat flatter than its siblings. It now sits one step higher on
   the elevation scale, with a faint mint wash at the top edge instead of a glow. */
.pricing-recommended {
  border-color: rgba(143,242,198,.42);
  background: linear-gradient(180deg, rgba(143,242,198,.055), rgba(143,242,198,0) 38%), var(--paper-raised);
  box-shadow: var(--elev-2), inset 0 0 0 1px rgba(143,242,198,.08), var(--edge-light);
  position: relative;
}

.pricing-badge {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--mint);
  color: #06140f;
  font: 700 10px var(--mono);
  text-transform: uppercase;
  letter-spacing: .06em;
  /* left:50% shrink-to-fits the pill to half the card, which wrapped
     "APPROVED LAUNCH / SHAPE" onto two lines at 1440px. */
  white-space: nowrap;
}

.pricing-header {
  text-align: center;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--line);
}

.pricing-tier {
  margin: 0 0 4px;
  color: var(--ink-muted);
  font: 600 13px var(--mono);
  text-transform: uppercase;
  letter-spacing: .08em;
}

.pricing-price {
  margin: 0;
  font-size: 48px;
  font-weight: 650;
  letter-spacing: -.04em;
  line-height: 1.1;
}

.pricing-cadence {
  margin: 2px 0 0;
  color: var(--ink-faint);
  font-size: 13px;
}

.pricing-features {
  margin: 24px 0;
  padding: 0;
  list-style: none;
  flex: 1;
}

.pricing-features li {
  padding: 8px 0;
  padding-left: 24px;
  position: relative;
  color: var(--ink-muted);
  font-size: 14px;
  line-height: 1.4;
}

.pricing-features li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 13px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(143,242,198,.18);
  border: 1px solid rgba(143,242,198,.35);
}

.pricing-footer {
  margin: 0;
  color: var(--ink-faint);
  font-size: 12px;
  text-align: center;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.pricing-note {
  margin: 28px 0 0;
  color: var(--ink-faint);
  font-size: 13px;
  text-align: center;
  max-width: 620px;
  margin-inline: auto;
}

/* Principles */
.principles { border-top: 1px solid var(--line); }

.principle-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; }
.principle-grid article { padding-top: 18px; border-top: 1px solid var(--line-strong); }
.principle-grid article > span { color: var(--mint); font: 600 11px var(--mono); }
.principle-grid h3 { margin: 48px 0 10px; font-size: 24px; letter-spacing: -.025em; }
.principle-grid p { margin: 0; color: var(--ink-muted); }


.release-card {
  padding: clamp(34px, 6vw, 72px);
  display: grid;
  grid-template-columns: 1fr minmax(240px, .55fr);
  gap: 38px 64px;
  color: #111317;
  background: var(--mint);
  border-radius: var(--radius-lg);
}

.release-card .eyebrow { color: rgba(17,19,23,.62); }
.release-card h2 { font-size: clamp(42px, 5.5vw, 72px); }
.release-card p:not(.eyebrow) { max-width: 660px; color: rgba(17,19,23,.7); font-size: 18px; }
.release-card .button { grid-column: 2; width: fit-content; }

.platform-summary { align-self: end; border-top: 1px solid rgba(17,19,23,.22); }
.platform-summary > div { padding: 14px 0; display: flex; justify-content: space-between; gap: 20px; border-bottom: 1px solid rgba(17,19,23,.22); }
.platform-summary span { font: 600 11px var(--mono); text-transform: uppercase; }
.platform-summary strong { font-size: 13px; text-align: right; }

/* Section rhythm: the previous section's padding-bottom already gives the gap, so
   stacking another top padding on the release card and FAQ made the gaps between
   two unrelated gaps: 152px (pricing->release) and 206px (release->FAQ). Both now
   sit on the single 122px .section step (82px mobile). Written as .section.x so
   the mobile `.section { padding-block }` override cannot re-add the top padding. */
.section.release-section, .section.faq { padding-top: 0; }
.faq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
/* FAQ rows were the only cards on the page with no elevation: a 1.8% white fill
   and a hairline, so they read as outlined boxes next to the layered bento and
   pricing cards. They now sit on the same surface + --elev-1 as pricing, and the
   summary takes the full row as its hit area. */
.faq details { padding: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--paper-raised); box-shadow: var(--elev-1), var(--edge-light); }
.faq summary { cursor: pointer; font-weight: 650; padding: 21px 22px; border-radius: inherit; }
.faq details p { margin: -7px 0 0; padding: 0 22px 21px; color: var(--ink-muted); font-size: 14px; }

.site-footer {
  width: var(--shell);
  margin: 40px auto 0;
  padding: 34px 0 48px;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 24px;
  border-top: 1px solid var(--line);
  color: var(--ink-muted);
  font-size: 13px;
}

.site-footer p { margin: 0; }
.site-footer nav { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 18px; }
.site-footer a:hover { color: var(--ink); }

.breadcrumbs {
  margin: 0 0 28px;
  color: var(--ink-faint);
  font-size: 13px;
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.breadcrumbs li:not(:last-child)::after {
  content: "/";
  margin-left: 8px;
  color: var(--ink-faint);
}

.breadcrumbs a { color: var(--ink-muted); }
.breadcrumbs [aria-current="page"] { color: var(--ink); }

/* WCAG 2.5.8 target size: the language switch, footer nav, breadcrumbs and
   compact nav rendered as bare text boxes 14-34px wide and 16-20px tall, so on
   a phone "ES" was a 14x17 target sitting 8px from "日本語". Give every one a
   24x24 minimum hit area without changing how the text itself is set. */
.lang-switch a,
.site-footer nav a,
.breadcrumbs a,
.compact-nav a,
.back-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
  min-width: 24px;
}

.missing-page {
  min-height: calc(100vh - 180px);
  padding: 108px 0 72px;
}

.missing-page h1 {
  margin: 0;
  font-size: clamp(46px, 7vw, 84px);
  letter-spacing: var(--ls-display, -.05em);
}

.missing-page p {
  max-width: 560px;
  margin: 22px 0 0;
  color: var(--ink-muted);
  font-size: 18px;
}

.missing-page .hero-actions { margin-top: 28px; }

/* Download route */
.download-hero { padding: 108px 0 64px; }
.download-hero h1 { max-width: 900px; font-size: clamp(50px, 7vw, 92px); }

.platform-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

.platform-card {
  position: relative;
  min-height: 500px;
  padding: clamp(26px, 4vw, 44px);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.platform-card[data-detected="true"] { border-color: rgba(143,242,198,.55); box-shadow: inset 0 0 0 1px rgba(143,242,198,.12); }
.platform-card-head { display: flex; justify-content: space-between; gap: 20px; }
.platform-kicker { margin: 0 0 6px; color: var(--ink-faint); font: 600 11px var(--mono); text-transform: uppercase; letter-spacing: .08em; }
.platform-card h2 { margin: 0; font-size: 46px; letter-spacing: -.045em; }
.detected-badge { height: fit-content; padding: 5px 9px; border-radius: 999px; color: #06140f; background: var(--mint); font: 700 10px var(--mono); text-transform: uppercase; }
.platform-version { margin: 26px 0 0; color: var(--mint); font: 600 13px var(--mono); }

.evidence-list { margin: 28px 0 30px; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.evidence-list li { padding: 13px 0; display: flex; justify-content: space-between; gap: 22px; border-bottom: 1px solid var(--line); }
.evidence-list span { color: var(--ink-faint); font-size: 13px; }
.evidence-list strong { max-width: 62%; font-size: 13px; text-align: right; }
.download-note { margin: 13px 0 0; color: var(--ink-faint); font-size: 12px; }

.integrity-section {
  display: grid;
  grid-template-columns: .75fr 1.25fr;
  gap: 80px;
  border-bottom: 1px solid var(--line);
}
.integrity-copy h2 { font-size: clamp(38px, 5vw, 62px); }
.integrity-copy > p:last-child { color: var(--ink-muted); }

.release-steps { margin: 0; padding: 0; list-style: none; }
.release-steps li { padding: 20px 0; display: grid; grid-template-columns: 45px 1fr; gap: 18px; border-top: 1px solid var(--line); }
.release-steps li > span { color: var(--mint); font: 600 11px var(--mono); }
.release-steps strong { font-size: 17px; }
.release-steps p { margin: 4px 0 0; color: var(--ink-muted); font-size: 14px; }

.blockers-section { display: grid; grid-template-columns: .8fr 1.2fr; gap: 80px; }
.blockers-section h2 { font-size: clamp(36px, 4.6vw, 58px); }
.blocker-list { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.blocker-list li { position: relative; padding: 17px 0 17px 28px; color: var(--ink-muted); border-bottom: 1px solid var(--line); }
.blocker-list li::before { content: ""; position: absolute; left: 0; top: 24px; width: 8px; height: 8px; border-radius: 50%; background: var(--warning); }

.help-panel {
  margin-bottom: 80px;
  padding: 38px;
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 30px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-soft);
}
.help-panel h2 { font-size: 30px; }
.help-panel p { color: var(--ink-muted); }

/* Login / Activation route */
.login-hero {
  padding: 108px 0 48px;
  text-align: center;
}

.login-hero h1 {
  margin: 0;
  font-size: clamp(50px, 7vw, 88px);
  font-weight: 620;
  line-height: var(--lh-display, .96);
  letter-spacing: var(--ls-display, -.06em);
}

.login-lede {
  max-width: 620px;
  margin: 28px auto 0;
  color: var(--ink-muted);
  font-size: 18px;
}

.login-section {
  padding-top: 0;
  padding-bottom: 100px;
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: 48px;
  align-items: start;
}

.login-card {
  padding: clamp(28px, 4vw, 48px);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper-raised);
}

.login-card h2 {
  margin: 0 0 8px;
  font-size: 28px;
  letter-spacing: -.03em;
}

.login-description {
  margin: 0 0 32px;
  color: var(--ink-muted);
  font-size: 14px;
  line-height: 1.55;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.field-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field-group label {
  font-size: 13px;
  font-weight: 650;
  color: var(--ink);
}

.field-group input {
  min-height: 48px;
  padding: 0 14px;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: rgba(255,255,255,.04);
  color: var(--ink);
  font-size: 15px;
  transition: border-color .18s ease, box-shadow .18s ease;
}

/* Was `outline: none` plus a rgba(143,242,198,.14) ring. Two defects: the ring
   computed to 1.33:1 against --paper, under the 3:1 WCAG 1.4.11 non-text minimum,
   and `outline: none` (specificity 0,2,1) beat the global `:focus-visible` rule
   (0,1,0), so the ONE control on the activation page lost its keyboard focus ring.
   The outline is no longer suppressed; the ring is raised to .45 -> 3.61:1. */
.field-group input:focus {
  border-color: var(--mint);
  box-shadow: 0 0 0 3px rgba(143,242,198,.45);
}

/* Invalid is carried by aria-invalid, so the visual state and the state exposed
   to assistive tech are the same attribute — they cannot drift apart. The ring
   alpha is .55, not the .45 used for mint: measured over --paper-raised, danger
   at .45 composites to #7c4a4d = 2.61:1, under the 3:1 WCAG 1.4.11 non-text
   minimum. At .55 it is #945758 = 3.33:1. */
.field-group input[aria-invalid="true"] {
  border-color: var(--danger);
}

.field-group input[aria-invalid="true"]:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(255,142,142,.55);
}

.field-group input::placeholder {
  color: var(--ink-faint);
}

.field-hint {
  margin: 0;
  color: var(--ink-faint);
  font-size: 12px;
}

.field-error {
  margin: 2px 0 0;
  color: var(--danger);
  font-size: 12px;
  font-weight: 600;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 4px;
}

/* Tactile press. The sheet had zero :active rules, so every button released with
   no acknowledgement. transform is used (not margin/top) so the press cannot
   reflow neighbours, and it is paired with the reduced-motion rule below. */
.button:active:not(:disabled) {
  transform: translateY(1px) scale(.994);
}

.activation-result {
  margin-top: 24px;
  padding: 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
}

.activation-result[data-status="success"] {
  border-color: rgba(143,242,198,.35);
  background: rgba(143,242,198,.06);
}

.activation-result[data-status="error"] {
  border-color: rgba(255,142,142,.35);
  background: rgba(255,142,142,.06);
}

.activation-message {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
}

.login-sidebar {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.login-info-card {
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,.018);
}

.login-info-card h3 {
  margin: 0 0 6px;
  font-size: 16px;
  letter-spacing: -.02em;
}

.login-info-card p {
  margin: 0;
  color: var(--ink-muted);
  font-size: 13px;
  line-height: 1.5;
}

/* Release ledger */
.compact-nav { margin-left: auto; display: flex; gap: 22px; color: var(--ink-muted); font-size: 14px; }
.compact-nav a:hover { color: var(--ink); }
.ledger-hero { padding: 108px 0 54px; }
.ledger-hero h1 {
  max-width: 920px;
  margin: 0;
  font-size: clamp(52px, 7vw, 92px);
  font-weight: 620;
  line-height: var(--lh-display, .96);
  letter-spacing: var(--ls-display, -.06em);
}
.ledger-hero > p:not(.eyebrow) { max-width: 700px; margin: 28px 0 0; color: var(--ink-muted); font-size: 20px; }
.ledger-grid { padding-top: 42px; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.ledger-card { padding: clamp(28px, 4vw, 46px); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--paper-raised); }
.ledger-current { border-color: rgba(143,242,198,.42); }
.ledger-generation { margin: 0 0 36px; padding: 18px 0; display: flex; justify-content: space-between; gap: 24px; border-block: 1px solid var(--line); }
.ledger-generation span, .ledger-platform span { color: var(--ink-faint); font-size: 13px; }
.ledger-generation strong { max-width: 68%; color: var(--mint); font: 600 12px/1.4 var(--mono); text-align: right; overflow-wrap: anywhere; }
.ledger-platform { padding: 23px 0; border-top: 1px solid var(--line); }
.ledger-platform > div { display: flex; align-items: baseline; justify-content: space-between; gap: 22px; }
.ledger-platform strong { text-align: right; }
.ledger-platform p { margin: 10px 0 0; color: var(--ink-muted); font-size: 14px; }
.ledger-note { margin-bottom: 80px; padding: clamp(28px, 5vw, 50px); border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--paper-soft); }
.ledger-note h2 { max-width: 760px; margin: 0; font-size: clamp(34px, 5vw, 56px); line-height: 1.02; letter-spacing: -.045em; }
.ledger-note > p:not(.eyebrow) { max-width: 760px; margin: 20px 0 28px; color: var(--ink-muted); }

/* Legal pages (Privacy / Terms) */
.legal-hero { padding: 108px 0 40px; }
.legal-hero h1 {
  max-width: 820px;
  margin: 0;
  font-size: clamp(46px, 6vw, 74px);
  font-weight: 620;
  line-height: var(--lh-display, 1.02);
  letter-spacing: var(--ls-display, -.05em);
}
.legal-hero > p:not(.eyebrow) { max-width: 700px; margin: 24px 0 0; color: var(--ink-muted); font-size: 18px; }
.legal-meta { margin: 22px 0 0; color: var(--ink-faint); font: 500 13px/1.5 var(--mono); }
.draft-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 -1px;
  padding: 14px clamp(20px, 4vw, 28px);
  border: 1px solid rgba(255, 199, 123, .38);
  border-radius: var(--radius-sm);
  background: rgba(255, 199, 123, .1);
  color: var(--warning);
  font: 600 13px/1.4 var(--mono);
  letter-spacing: .02em;
}
.draft-banner::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warning);
  box-shadow: 0 0 0 5px rgba(255,199,123,.14);
}
.legal-content { max-width: 760px; padding-bottom: 90px; color: var(--ink-muted); font-size: 16px; line-height: 1.7; }
.legal-content h2 {
  margin: 52px 0 16px;
  color: var(--ink);
  font-size: clamp(24px, 3vw, 30px);
  font-weight: 600;
  letter-spacing: -.02em;
}
.legal-content h2:first-of-type { margin-top: 0; }
.legal-content h3 { margin: 30px 0 12px; color: var(--ink); font-size: 18px; font-weight: 600; }
.legal-content p { margin: 0 0 16px; }
.legal-content ul, .legal-content ol { margin: 0 0 16px; padding-left: 22px; }
.legal-content li { margin-bottom: 8px; }
.legal-content a { color: var(--mint); text-decoration: underline; text-decoration-color: rgba(143,242,198,.4); }
.legal-content a:hover { text-decoration-color: var(--mint); }
.legal-content strong { color: var(--ink); }
.legal-content code { padding: .15em .4em; border-radius: 6px; background: var(--paper-raised); font: 500 .9em var(--mono); }
.legal-content table { width: 100%; margin: 8px 0 24px; border-collapse: collapse; font-size: 14px; }
.legal-content th, .legal-content td { padding: 10px 14px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
.legal-content th { color: var(--ink); font-weight: 600; background: var(--paper-raised); }
.legal-content .needs-han { color: var(--danger); font-weight: 600; }
.legal-toc { margin: 0 0 40px; padding: 20px 24px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--paper-raised); }
.legal-toc p { margin: 0 0 10px; }
.legal-toc ol { margin: 0; padding-left: 20px; columns: 2; column-gap: 28px; }
/* At 375px the retention table's min-content width (3 columns x 28px padding plus
   words like "opportunistically") was 377px in a 347px column, so /privacy/ scrolled
   sideways on every phone. Tighter padding and in-word wrapping keep it inside. */
@media (max-width: 720px) {
  .legal-toc ol { columns: 1; }
  .legal-content table { font-size: 13px; }
  .legal-content th, .legal-content td { padding: 8px 9px; overflow-wrap: anywhere; hyphens: auto; }
}

[hidden] { display: none !important; }

/* ---------------------------------------------------------------------------
   CJK display typography. Latin display sizes are tuned with very tight
   tracking (.92 line-height, -.065em letter-spacing), which makes zh-Hans and
   ja headlines crowd and collide -- CJK glyphs fill their full em box. The
   display rules above read these custom properties; the CJK locales override
   them at the root so every display/headline rule relaxes together without
   touching the Latin tuning the English pages were designed around. */
:root:lang(zh-Hans), :root:lang(ja) {
  --ls-display: -.02em;
  --lh-display: 1.14;
  --ls-headline: -.01em;
  --lh-headline: 1.16;
  /* CJK glyphs are a full em wide, so the Latin 102px hero headline cannot fit
     its six- or seven-glyph second line in the ~482px hero column and orphans the last
     glyph (live: 终于有用 / 了。). A smaller ceiling keeps each line whole. */
  --fs-hero: clamp(40px, 4.6vw, 66px);
  --fs-hero-sm: clamp(36px, 10.5vw, 56px);
}

/* Japanese has no spaces, so the browser breaks headlines at any glyph: at 375px
   /ja/ rendered 4 つのティア。ひ / とつの正直な製 / 品。 and インストール前 / に、... / を。,
   splitting words and orphaning the final glyph. auto-phrase breaks at phrase
   (bunsetsu) boundaries where supported; balance evens the line lengths so the
   last line is not a lone character. Unsupported browsers keep the old wrapping.
   overflow-wrap:anywhere is load-bearing: a single phrase such as
   ふたつのネイティブビルド。 is ~370px at the 375px release-card h2 size, and
   without a break opportunity inside it the 1fr grid track grew to fit it and
   /ja/ scrolled sideways (404px). anywhere lets that phrase split only when it
   cannot fit, and it also lowers min-content so the track stays in bounds. */
:lang(ja) :is(h2, h3) {
  word-break: auto-phrase;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

/* ---------------------------------------------------------------------------
   Motion. Three layers, all gated to prefers-reduced-motion: no-preference;
   the global reduce rule at the end of this sheet also flattens durations.

   1. Page entrance: hero copy and stage rise once on load, staggered.
   2. Scroll reveal: [data-reveal] elements fade/rise into view. The hiding
      styles only exist under html.js-reveal (added by app.js), so no-JS
      visitors and search crawlers see fully rendered content; if
      IntersectionObserver is missing app.js reveals everything immediately.
   3. Micro-interactions: hover lifts on buttons/cards and a slow float on
      the hero notch panel. */

@media (prefers-reduced-motion: no-preference) {
  @keyframes rise {
    from { opacity: 0; transform: translateY(26px); }
    to { opacity: 1; transform: none; }
  }

  .hero-copy > *, .download-hero > *, .login-hero > *, .ledger-hero > * {
    animation: rise .8s cubic-bezier(.22, .61, .36, 1) both;
  }
  .hero-copy > *:nth-child(2), .download-hero > *:nth-child(2),
  .login-hero > *:nth-child(2), .ledger-hero > *:nth-child(2) { animation-delay: .07s; }
  .hero-copy > *:nth-child(3), .download-hero > *:nth-child(3),
  .login-hero > *:nth-child(3), .ledger-hero > *:nth-child(3) { animation-delay: .14s; }
  .hero-copy > *:nth-child(4), .download-hero > *:nth-child(4),
  .login-hero > *:nth-child(4), .ledger-hero > *:nth-child(4) { animation-delay: .21s; }
  .hero-copy > *:nth-child(5) { animation-delay: .28s; }

  .hero-stage { animation: rise .95s cubic-bezier(.22, .61, .36, 1) .16s both; }

  @keyframes panel-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-7px); }
  }
  /* The panel remains attached to the hardware edge; an ambient vertical
     float would separate it from that physical anchor. */

  .button { transition: border-color .18s ease, background .18s ease,
    transform .22s cubic-bezier(.22, .61, .36, 1), box-shadow .22s ease; }
  .button:hover:not(:disabled) { transform: translateY(-1px); }
  .button:active:not(:disabled) { transform: translateY(0); }
  .button-primary:hover:not(:disabled) { box-shadow: 0 10px 28px rgba(244, 241, 232, .13); }
  .button-secondary:hover:not(:disabled) { background: rgba(255, 255, 255, .055); }

  .feature-card, .pricing-card, .platform-card, .login-info-card {
    transition: transform .35s cubic-bezier(.22, .61, .36, 1), border-color .35s ease;
  }
  /* .feature-card is deliberately absent: every feature card lives inside a
     [data-reveal-children] group, whose `.is-revealed > *` rule (0,3,1) outranks
     this selector (0,2,0) — listing it here was dead weight. The feature-card
     transform has one owner: the pointer-depth tilt rules further down, which
     fold this same -4px lift in. Two rules must never fight over one property. */
  .pricing-card:hover, .platform-card:hover, .login-info-card:hover {
    transform: translateY(-4px);
    border-color: var(--line-strong);
  }
  .pricing-recommended:hover { border-color: rgba(143, 242, 198, .6); }
  .platform-card[data-detected="true"]:hover { border-color: rgba(143, 242, 198, .7); }

  .feature-card img { transition: transform .55s cubic-bezier(.22, .61, .36, 1); }
  .feature-card:hover img { transform: scale(1.025); }

  .faq details { transition: border-color .25s ease, background .25s ease; }
  .faq details:hover, .faq details[open] { border-color: var(--line-strong); }
  .faq details[open] { background: rgba(255, 255, 255, .035); }
}

html.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .7s ease, transform .7s cubic-bezier(.22, .61, .36, 1);
  transition-delay: var(--reveal-delay, 0s);
}
html.js-reveal [data-reveal].is-revealed { opacity: 1; transform: none; }

/* Staggered groups: one observed container reveals its children in sequence.
   The container attribute is locale-independent, so all six locales get the
   same motion from a single attribute on the grid. */
html.js-reveal [data-reveal-children] > * {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .7s ease, transform .7s cubic-bezier(.22, .61, .36, 1);
}
html.js-reveal [data-reveal-children] > *:nth-child(2) { transition-delay: .07s; }
html.js-reveal [data-reveal-children] > *:nth-child(3) { transition-delay: .14s; }
html.js-reveal [data-reveal-children] > *:nth-child(4) { transition-delay: .21s; }
html.js-reveal [data-reveal-children] > *:nth-child(5) { transition-delay: .28s; }
html.js-reveal [data-reveal-children] > *:nth-child(6) { transition-delay: .35s; }
html.js-reveal [data-reveal-children].is-revealed > * { opacity: 1; transform: none; }

/* ---------------------------------------------------------------------------
   Pointer-tracked depth on the bento cards.

   app.js publishes exactly two numbers -- --tilt-x/--tilt-y, the pointer's offset
   from card centre normalised to -0.5..0.5 -- and toggles .is-tilting /
   .is-settling. Everything visual (tilt angle, the counter-moving shadow, the
   sheen position) is derived from those two numbers here, so the geometry has
   one owner. The card leaning right must cast its shadow left, or the tilt reads
   as a sticker being rotated instead of an object being lit.

   The reveal rule above (`...is-revealed > *` at 0,3,1) outranks a plain
   `.feature-card:hover` (0,2,0), which silently killed the hover lift on every
   card inside a stagger group. These selectors are deliberately written at 0,4,1
   so hover and tilt win, and they share ONE transform so the two effects can
   never fight over the same property.
   --------------------------------------------------------------------------- */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  /* Restore the lift for stagger-group children, at a specificity the reveal
     rule cannot outrank.
     .feature-card is deliberately NOT listed: the tilt rules below own the
     feature-card transform outright and already fold the -4px lift into it.
     Listing it here too would win on specificity (.feature-card:hover inside
     .is-revealed is 0,5,1 against the tilt rule's 0,4,1) and silently cancel
     the rotation -- two rules must never fight over one property. */
  html.js-reveal [data-reveal-children].is-revealed > .pricing-card:hover {
    transform: translateY(-4px);
  }

  html.js-reveal [data-reveal-children] > .feature-card.is-tilting,
  html.js-reveal [data-reveal-children] > .feature-card.is-settling {
    /* Short ease while tracking so the card keeps up with the pointer; the
       settle-back below swaps to the house spring. A 340ms ease during tracking
       reads as lag, not premium. */
    transition: transform .12s cubic-bezier(.33, 1, .68, 1),
      box-shadow .12s ease, border-color .18s ease;
    transition-delay: 0s;
    will-change: transform;
  }
  html.js-reveal [data-reveal-children] > .feature-card.is-settling {
    transition: transform .38s cubic-bezier(.22, 1.28, .36, 1), box-shadow .38s ease;
  }

  html.js-reveal [data-reveal-children] > .feature-card.is-tilting {
    transform: translateY(-4px) perspective(900px)
      rotateX(calc(var(--tilt-y, 0) * -3.2deg))
      rotateY(calc(var(--tilt-x, 0) * 3.2deg));
    box-shadow:
      0 1px 2px rgba(0, 0, 0, .5),
      calc(var(--tilt-x, 0) * -12px) calc(var(--tilt-y, 0) * -12px + 8px) 22px rgba(0, 0, 0, .32),
      calc(var(--tilt-x, 0) * -24px) calc(var(--tilt-y, 0) * -24px + 28px) 58px rgba(0, 0, 0, .44),
      var(--edge-light);
  }
  /* Settling keeps the tilt maths but drops the lift -- the pointer is gone, so
     the card must fall back onto the surface, not hover above it. */
  html.js-reveal [data-reveal-children] > .feature-card.is-settling {
    transform: perspective(900px)
      rotateX(calc(var(--tilt-y, 0) * -3.2deg))
      rotateY(calc(var(--tilt-x, 0) * 3.2deg));
    box-shadow: var(--elev-2), var(--edge-light);
  }

  .feature-card.is-tilting::after { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  html.js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
  html.js-reveal [data-reveal-children] > * { opacity: 1; transform: none; transition: none; }
}

/* Print / Save as PDF never scrolls, so the observer never fires and every
   section below the first screen printed as a blank page (features, pricing and
   the whole FAQ were missing from the PDF). The skip link also printed over the
   page because its off-screen transform is a screen-only idea. */
@media print {
  html.js-reveal [data-reveal],
  html.js-reveal [data-reveal-children] > * { opacity: 1 !important; transform: none !important; transition: none !important; }
  .skip-link { display: none; }
}

/* Soft light behind the hero stage. rgba() values, never a hardcoded text
   colour, so the contrast gate has nothing to measure here. */
.hero-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(52% 42% at 50% 0%, rgba(143, 242, 198, .11), transparent 70%),
    radial-gradient(60% 55% at 78% 108%, rgba(143, 242, 198, .07), transparent 72%);
  pointer-events: none;
}

/* Ambient desktop behind the sim: two blurred window silhouettes. Without
   them the dormant stage reads as an empty dark card, and the sim's whole
   story -- a surface living ON TOP of a working desktop -- has no desktop in
   it. Kept faint and heavily blurred (background windows, not content); pure
   presentation with zero motion, so it costs nothing in the reduced-motion
   path and the contrast gate has nothing to measure. */
.hero-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    linear-gradient(rgba(255, 255, 255, .06), rgba(255, 255, 255, .028)) 8% 32% / 52% 36% no-repeat,
    linear-gradient(rgba(255, 255, 255, .045), rgba(255, 255, 255, .022)) 64% 62% / 38% 42% no-repeat;
  border-radius: inherit;
  filter: blur(5px);
  pointer-events: none;
}

/* Sticky header reads as floating glass instead of a hard slab. */
.site-header {
  background: rgba(9, 10, 12, .78);
  -webkit-backdrop-filter: blur(14px) saturate(1.35);
  backdrop-filter: blur(14px) saturate(1.35);
}

@media (max-width: 980px) {
  :root { --shell: min(100% - 36px, 820px); }
  .hero { min-height: auto; grid-template-columns: 1fr; padding-top: 88px; }
  .hero-copy { max-width: 720px; }
  .hero-stage { min-height: 430px; }
  .proof-strip { grid-template-columns: 1fr 1fr; }
  .proof-strip div:nth-child(2) { border-right: 0; }
  .proof-strip div:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
  .bento-grid { grid-template-columns: 1fr; }
  .feature-tall { grid-row: auto; min-height: 620px; }
  .pricing-grid { grid-template-columns: 1fr 1fr; }
  .principle-grid { grid-template-columns: 1fr; }
  .workflow-heading { grid-template-columns: 1fr; gap: 34px; }
  .workflow-grid { grid-template-columns: 1fr; }
  .principle-grid h3 { margin-top: 24px; }
  .release-card { grid-template-columns: 1fr; }
  .release-card .button { grid-column: 1; }
  .platform-grid { grid-template-columns: 1fr; }
  .integrity-section, .blockers-section { grid-template-columns: 1fr; gap: 46px; }
  .help-panel { grid-template-columns: 1fr; }
  .login-section { grid-template-columns: 1fr; }
  .ledger-grid { grid-template-columns: 1fr; }
  .help-panel .button { width: fit-content; }
  .site-footer { grid-template-columns: 1fr; }
  .site-footer nav { justify-content: flex-start; }
}

@media (max-width: 720px) {
  :root { --shell: calc(100% - 28px); }
  .site-header { min-height: 64px; padding-inline: 14px; }
  .menu-toggle { display: inline-flex; }
  .nav-links {
    display: none;
    position: absolute;
    top: calc(100% + 1px);
    left: 14px;
    right: 14px;
    padding: 14px;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: #101216;
    box-shadow: 0 22px 50px rgba(0,0,0,.45);
  }
  .nav-links[data-open="true"] { display: flex; }
  .nav-links a { padding: 11px; }
  .nav-links .nav-cta { border: 0; }
  /* No-JS fallback: menu-toggle only opens .nav-links via app.js, so without
     script the toggle button renders but does nothing and the primary nav
     links become unreachable at this width. This twin nav is never targeted
     by [data-open], so it stays visible whether or not app.js runs. */
  .nav-links-noscript { display: flex !important; position: static; box-shadow: none; }
  /* Column gap 0: each link is already a >=24px box (target-size rule above),
     so the box itself spaces the labels. Keeping the 8px gap on top of the
     wider boxes pushed the landing header to 410px at a 375px viewport. */
  .lang-switch { gap: 4px 0; font-size: 11px; }
  .hero { padding: 76px 0 66px; gap: 48px; }
  .hero h1, .download-hero h1 { font-size: var(--fs-hero-sm, clamp(48px, 15vw, 72px)); }
  .hero-stage { min-height: 360px; padding: 12px; border-radius: 26px; }
  .camera-cutout { top: -12px; }
  .notch-sim { right: 12px; left: 12px; }
  .notch-panel { width: 96%; padding: 12px; border-radius: 20px; }
  .stage-caption { inset: auto 16px 15px; }
  .proof-strip { width: 100%; grid-template-columns: 1fr; }
  .proof-strip div { border-right: 0; border-bottom: 1px solid var(--line); }
  .section { padding-block: 82px; }
  .section-heading { margin-bottom: 36px; }
  .section-heading h2, .release-card h2 { font-size: clamp(36px, 11vw, 54px); }
  .feature-card { min-height: 420px; padding: 22px; }
  .feature-wide { min-height: 430px; }
  .feature-tall { min-height: 550px; }
  .feature-media img { width: 104%; right: -26%; }
  .feature-window .feature-copy { max-width: none; }
  .feature-window img { width: 54%; opacity: .72; }
  .capability-list { grid-template-columns: 1fr; }
  .capability-list li { grid-template-columns: 1fr; gap: 3px; }
  .feature-list-thumb { position: static; width: 160px; margin: 18px 0 0; }
  .feature-list:has(.feature-list-thumb) h3 { max-width: none; }
  .pricing-grid { grid-template-columns: 1fr; }
  .pricing-card { padding: 24px 20px; }
  .release-card { padding: 30px 22px; border-radius: 24px; }
  .faq-grid { grid-template-columns: 1fr; }
  .download-hero { padding: 76px 0 48px; }
  .login-hero { padding: 76px 0 36px; }
  .login-hero h1 { font-size: clamp(46px, 14vw, 64px); }
  .login-section { padding-bottom: 64px; }
  .login-card { padding: 24px 20px; }
  .ledger-hero { padding: 76px 0 42px; }
  .ledger-hero h1 { font-size: clamp(46px, 14vw, 68px); }
  .compact-nav { gap: 12px; font-size: 12px; }
  .platform-card { min-height: 0; padding: 25px 20px; }
  .evidence-list li { display: grid; gap: 3px; }
  .evidence-list strong { max-width: none; text-align: left; }
  .help-panel { padding: 25px 20px; }
  /* download.html / changelog.html carry .compact-nav + .lang-switch (6 links)
     in the header with no .menu-toggle fallback (that pattern belongs to the
     landing header only). At <=720px the two groups plus the brand no longer
     fit one row: .lang-switch had flex-shrink:0 so it doesn't compress, and
     .site-header has no flex-wrap so the overflow pushed FR/DE past the
     viewport edge (measured scrollWidth 425-450px at a 390px viewport across
     de/es/fr/en changelog). Wrap the header itself and let the language row
     take the full second line instead of fighting compact-nav for space. */
  .download-page .site-header,
  .ledger-page .site-header {
    flex-wrap: wrap;
    row-gap: 10px;
    min-height: 0;
    padding-block: 12px;
  }
  .download-page .lang-switch,
  .ledger-page .lang-switch {
    flex-shrink: 1;
    flex-basis: 100%;
    justify-content: flex-start;
    margin-left: 0;
  }
  .download-page .back-link,
  .ledger-page .compact-nav {
    margin-left: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
  /* Zeroing the duration only makes a transform instant, it does not remove the
     displacement, so the press still MOVES the button. Reduce Motion asks for no
     movement, so the transform itself is dropped and the press falls back to the
     border-colour feedback the button already has. */
  .button:active:not(:disabled) { transform: none !important; }
}
