/* CloudCastle marketing — component layer.
 *
 * Consumes /brand/brand.css and the marketing theme layer below, and nothing
 * else. No component rule authors a colour, spacing, radius, shadow, or motion
 * value: every one of them is a token.
 *
 * The marketing site is LIGHT. The operator console (`admin.css`) and the
 * remote-session surfaces (`web.html`, `web-session.html`) link brand.css
 * WITHOUT this file and stay dark — which is exactly why the light palette is
 * overridden here instead of in brand.css. Reserved dark bands opt in with
 * `.on-dark`.
 *
 * This file has exactly one author. Need a new shared class? Ask for it;
 * do not fork a parallel style into a page-local <style> block.
 *
 * Order: local constants · theme · base · layout · chrome · controls · forms ·
 *        content · hero · page shapes · breakpoints (768 / 1100 / 1440).
 */

/* ── Local layout constants ───────────────────────────────────────────────
 * Structural dimensions that are not design tokens: container widths, the
 * icon box, the derived focus ring, the mono stack, the shimmer wash.
 * Declared once so no literal is ever repeated in a rule below.
 */
:root {
  --wrap-max: 1200px;
  --hero-copy-max: 560px;
  --measure: 640px;
  --headline: 896px;
  --grid-min: 320px;
  /* Three-up grid floor. At --grid-min three tracks plus gaps outgrow a
   * laptop wrap and the third card wraps below as an orphan. Still wide
   * enough that a showcase copy column (~552px) keeps a single track. */
  --grid-min-3: 288px;
  --footer-col-min: 160px;
  --icon-sz: 20px;
  --icon-lg: 20px;
  --rail: 2px;
  --glow-blur: 120px;
  --brand-crest: 112px;
  --focus-ring: 0 0 0 3px var(--accent-ring);
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --shimmer: rgba(15, 23, 42, 0.05);
  --t-shimmer: 1.4s;
  /* Height of the feathered fade at the dark landing hero's top/bottom edge. */
  --band-fade: clamp(64px, 10vh, 120px);
  /* Closing callout width — a full-wrap CTA plate reads as a banner, not a card. */
  --cta-band-max: 880px;
}

/* ── Marketing theme ──────────────────────────────────────────────────────
 * The D1 palette, elevation and scrim tokens, re-pointed for a light surface.
 * Same token NAMES as brand.css, so every rule in brand.css and below keeps
 * resolving through tokens and nothing hardcodes a colour. Deep-slate ink on
 * white/near-white gives the contrast the dark build could not, and magenta
 * carries CTAs, active states and accents.
 *
 * On light, emphasis goes DARKER, not brighter: --accent-hi is a deeper
 * magenta than --accent, the inverse of the dark build.
 */
:root {
  --canvas: #f8fafc;
  /* The light canvas under its own name so .on-dark subtrees (which remap
   * --canvas) can still feather their edges INTO the light page. */
  --page-canvas: #f8fafc;
  --canvas-2: #ffffff;
  --surface-1: #ffffff;
  --surface-2: #f8fafc;
  --surface-3: #edf1f7;
  --ink: #0f172a;
  --ink-2: #1e293b;
  --muted: #475569;
  --line: rgba(15, 23, 42, 0.1);
  --line-strong: rgba(15, 23, 42, 0.22);
  --accent: #d24f87;
  --accent-hi: #b03a6c;
  --brand-grad: linear-gradient(135deg, #d24f87 0%, #b03a6c 100%);
  --accent-tint: rgba(210, 79, 135, 0.1);
  --accent-ring: rgba(210, 79, 135, 0.28);

  /* Text that sits ON --accent / --brand-grad. --ink would fail contrast. */
  --on-accent: #ffffff;

  /* Status hues darkened for AA on white; tints keep the 12% recipe. */
  --ok: #0a7a3e;
  --warn: #b45309;
  --bad: #b91c1c;
  --info: #0369a1;
  --ok-tint: rgba(10, 122, 62, 0.12);
  --warn-tint: rgba(180, 83, 9, 0.12);
  --bad-tint: rgba(185, 28, 28, 0.12);
  --info-tint: rgba(3, 105, 161, 0.12);

  /* Light surfaces need drop shadow, not the dark build's inset key light. */
  --e1: 0 1px 2px rgba(15, 23, 42, 0.06);
  --e2: 0 8px 24px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.06);
  --e3: 0 24px 64px rgba(15, 23, 42, 0.12), 0 1px 2px rgba(15, 23, 42, 0.06);
  --scrim: rgba(15, 23, 42, 0.55);
}

/* Reserved dark band — executive hero banners, platform demo cards, footer
 * callouts. Remaps the same tokens for its own subtree, so every component
 * inside inverts without a single dark-variant rule. Use sparingly: the point
 * of the light build is that body copy is never on a dark surface.
 *
 * The dark palette IS the light theme's ink ramp (#0f172a / #1e293b / #334155
 * slate), so a dark band reads as the page's own ink colour made ambient
 * instead of an unrelated near-black. --band grades top-to-bottom from canvas
 * into surface-1, so the edge that meets light content is the lighter slate —
 * a gradual handoff rather than a hard cut.
 */
.on-dark {
  --canvas: #0f172a;
  --canvas-2: #1e293b;
  --surface-1: #1e293b;
  --surface-2: #273549;
  --surface-3: #334155;
  --ink: #f5f7fb;
  --ink-2: #cbd5e1;
  --muted: #94a3b8;
  --band: linear-gradient(180deg, var(--canvas) 0%, var(--surface-1) 100%);
  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.22);
  /* Emphasis brightens again once the surface is dark. */
  --accent-hi: #f07cae;
  --e1: 0 1px 2px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.04);
  --e2: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --e3: 0 24px 64px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --scrim: rgba(4, 6, 12, 0.6);
  --shimmer: rgba(255, 255, 255, 0.05);
  background: var(--band);
  color: var(--ink);
}

/* The landing hero re-declares `background` later in this file at the same
 * specificity, which would flatten the band back to a solid fill. Two
 * classes outrank it and keep the canvas→surface-1 grade. */
.hero.on-dark {
  background: var(--band);
}

/* Edge feathering — the actual fix for the hard light/dark cut on the
 * landing hero (the only dark band left; inner page-heros and CTA bands
 * are light). These edge fades dissolve the band's top and bottom into
 * --page-canvas (the LIGHT canvas, declared at :root and deliberately NOT
 * remapped by .on-dark) so the hero hands off to whatever light section
 * follows with no visible line. The top fade also softens the junction
 * with the light sticky header.
 * z-index: above the hero's z-index:-2/-1 scene layers, below the copy. */
.hero.on-dark::before,
.hero.on-dark::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  height: var(--band-fade);
  pointer-events: none;
}

.hero.on-dark::before {
  top: 0;
  background: linear-gradient(180deg, var(--page-canvas), transparent);
}

.hero.on-dark::after {
  bottom: 0;
  background: linear-gradient(0deg, var(--page-canvas), transparent);
}

.hero-copy {
  position: relative;
  z-index: 1;
}

/* ── Base ─────────────────────────────────────────────────────────────── */
html {
  scroll-behavior: smooth;
  background: var(--canvas);
}

body {
  min-height: 100dvh;
}

img,
svg {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

/* DO NOT DELETE THIS RULE AS A DUPLICATE. Read this first.
 *
 * `[hidden]` is now also in brand.css, so that half IS redundant. `.hidden`
 * is NOT: this is the ONLY definition of the class anywhere under static/.
 * downloads.html, portal.html and accept-invite.html each carried a private
 * `.hidden { display: none !important }` and deleted it once this rule
 * landed, so 13 elements across those three pages now depend on it —
 * including `portal.html`'s `<section id="dashView" class="hidden">`. Drop
 * this rule and a signed-out visitor sees the login form and the dashboard
 * at once, and accept-invite.html shows its form before the invite is
 * validated. brand.css calling this copy "redundant but identical" is true
 * of the `[hidden]` half only.
 *
 * Why it is needed at all: this file sets `display` on 46 rules, and an
 * author `display` declaration beats the UA `[hidden] { display: none }`.
 * `<p class="notice" hidden>` on pricing.html rendered VISIBLE before this.
 * The `!important` is load-bearing too — without it this loses to
 * `.notice { display: flex }` and to `#loginView`/`#dashView`.
 *
 * Both forms are covered because the pages drive some elements by attribute
 * (`el.hidden = bool`) and others by class (`classList.toggle("hidden", …)`).
 * `.hidden` is an active mechanism, not legacy.
 *
 * Safe because no marketing page uses `data-hidden`, and the only element
 * revealed via `style.display` (pricing.html showNotice) also clears the
 * attribute. A reveal path that cleared just the inline style while leaving
 * `hidden` set would be permanently hidden by this rule — never add one.
 * Pair `hidden` with `el.hidden = false`, never with `style.display = ""`.
 */
[hidden],
.hidden {
  display: none !important;
}

::selection {
  background: var(--accent-tint);
  color: var(--ink);
}

/* Anything a header CTA jumps to clears the sticky header. */
:target,
.anchor-target {
  scroll-margin-top: var(--s8);
}

.muted {
  color: var(--muted);
}

/* Body copy never runs wider than a comfortable measure. */
.measure {
  max-width: var(--measure);
}

.icon {
  width: var(--icon-sz);
  height: var(--icon-sz);
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-sm {
  --icon-sz: var(--s4);
}

.icon-20 {
  --icon-sz: var(--icon-lg);
}

/* ── Layout ───────────────────────────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: var(--wrap-max);
  margin-inline: auto;
  padding-inline: var(--s5);
}

.section {
  background: var(--canvas);
  padding-block: var(--s8);
}

/* At most one alternation per page. */
.section-alt {
  background: var(--canvas-2);
  border-block: 1px solid var(--line);
}

.section-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  /* s5, not s6: the old gap read as a break between the head and its own
   * content instead of a pause. */
  margin-bottom: var(--s5);
}

.section-head .lead {
  max-width: var(--measure);
}

/* A 28px heading across the full 1200px wrap is one long line; cap it. */
.section-head h2 {
  max-width: var(--headline);
}

/* Inline text link. brand.css strips underlines globally, so prose links
 * need an explicit affordance or they read as bold body copy. */
.link {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: var(--s1);
  transition: text-decoration-color var(--t-micro) var(--ease);
}

.link:hover {
  text-decoration-color: var(--accent-hi);
}

.grid-2,
.grid-3 {
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min), 1fr));
}

.grid-2 {
  max-width: calc(var(--grid-min) * 3);
}

/* .grid-3 rows hold trios; the narrower floor seats all three tracks on a
 * laptop wrap instead of orphaning the third card below the first two. */
.grid-3 {
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min-3), 1fr));
}

.split {
  display: grid;
  grid-template-columns: 1fr minmax(var(--grid-min), 0.62fr);
  gap: var(--s7);
  align-items: start;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}

/* Section-level grouping. .stack's s3 is tighter than a grid's own s4 gap,
 * which reads as a mistake when a notice sits below a card grid. */
.stack-lg {
  gap: var(--s5);
}

.row {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2);
  padding-block: var(--s5) 0;
  font-size: var(--fs-caption);
  color: var(--muted);
}

.crumbs a {
  color: var(--muted);
  transition: color var(--t-micro) var(--ease);
}

.crumbs a:hover {
  color: var(--ink);
}

.crumbs .sep {
  color: var(--line-strong);
}

/* ── Chrome: header ───────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
}

.site-header-inner,
.site-header > .wrap {
  display: flex;
  align-items: center;
  gap: var(--s4);
  min-height: var(--s8);
  padding-block: var(--s2);
}

.site-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  flex: 0 0 auto;
  margin-right: auto;
}

.site-brand img {
  width: var(--brand-wordmark-w);
  height: auto;
  aspect-ratio: var(--brand-wordmark-ratio);
  object-fit: contain;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--s1);
}

.site-nav a {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  border-radius: var(--r-sm);
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--ink-2);
  transition: color var(--t-micro) var(--ease),
    background-color var(--t-micro) var(--ease);
}

.site-nav a:hover {
  color: var(--ink);
  background: var(--surface-1);
}

.site-nav a[data-cc-nav][aria-current="page"] {
  color: var(--ink);
  background: var(--accent-tint);
  box-shadow: inset 0 calc(-1 * var(--rail)) 0 var(--accent);
}

.site-nav a:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.nav-auth,
.nav-auth-content,
.nav-portals {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
}

.nav-auth {
  min-height: var(--s7);
  margin-left: var(--s2);
}

.nav-auth .btn {
  margin-left: 0;
  white-space: nowrap;
}

.nav-auth[data-cc-auth-state="pending"] .nav-auth-content {
  visibility: hidden;
}

.nav-auth[data-cc-auth-state="pending"]::before {
  content: "";
  width: calc(var(--s8) + var(--s8) + var(--s6));
  height: var(--s7);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
}

.site-nav .btn-primary,
.site-nav .btn-primary:hover {
  color: var(--on-accent);
}

.site-nav .btn-secondary:hover {
  color: var(--ink);
  background: var(--surface-3);
}

.nav-perspective {
  position: relative;
  display: inline-flex;
}

.nav-perspective-toggle::after {
  content: "";
  width: var(--s2);
  height: var(--s2);
  margin-left: var(--s1);
  border-right: var(--rail) solid currentColor;
  border-bottom: var(--rail) solid currentColor;
  transform: translateY(calc(-1 * var(--rail))) rotate(45deg);
  transition: transform var(--t-micro) var(--ease);
}

.nav-perspective.is-open .nav-perspective-toggle::after {
  transform: translateY(var(--rail)) rotate(225deg);
}

.nav-perspective-menu {
  position: absolute;
  top: calc(100% + var(--s2));
  right: 0;
  z-index: 60;
  display: none;
  min-width: calc(var(--s8) * 3);
  padding: var(--s1);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--e2);
}

.nav-perspective.is-open .nav-perspective-menu {
  display: flex;
  flex-direction: column;
}

.site-nav .nav-perspective-menu a {
  justify-content: flex-start;
  white-space: nowrap;
}

.site-nav .nav-perspective-menu a:hover,
.site-nav .nav-perspective-menu a:focus-visible {
  background: var(--accent-tint);
  color: var(--ink);
}

/* ≤768px only. */
.nav-toggle {
  display: none;
  flex: 0 0 auto;
  width: var(--s7);
  height: var(--s7);
  padding: 0;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: var(--s1);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  color: var(--ink);
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  width: var(--s4);
  height: var(--rail);
  border-radius: var(--r-pill);
  background: currentColor;
  transition: transform var(--t-micro) var(--ease),
    opacity var(--t-micro) var(--ease);
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(calc(var(--s1) + var(--rail))) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(calc(-1 * (var(--s1) + var(--rail)))) rotate(-45deg);
}

/* ── Chrome: footer ───────────────────────────────────────────────────── */
.site-footer {
  background: var(--canvas-2);
  border-top: 1px solid var(--line);
  padding-block: var(--s7) var(--s6);
}

.footer-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--footer-col-min), 1fr));
  gap: var(--s6);
  margin-bottom: var(--s6);
}

.footer-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s2);
}

.footer-col > img:first-child {
  width: var(--brand-wordmark-w);
  height: auto;
  aspect-ratio: var(--brand-wordmark-ratio);
  object-fit: contain;
}

.footer-col > .eyebrow {
  margin-bottom: var(--s1);
}

.footer-col a,
.footer-col p {
  font-size: var(--fs-compact);
  color: var(--ink-2);
}

.footer-col a {
  transition: color var(--t-micro) var(--ease);
}

.footer-col a:hover {
  color: var(--ink);
}

.social-links {
  display: flex;
  gap: var(--s2);
  margin-top: var(--s2);
}

.footer-col .social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--s7);
  height: var(--s7);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  color: var(--ink-2);
  transition:
    color var(--t-micro) var(--ease),
    border-color var(--t-micro) var(--ease),
    background-color var(--t-micro) var(--ease),
    transform var(--t-micro) var(--ease);
}

.footer-col .social-link:hover {
  border-color: var(--accent);
  background: var(--accent-tint);
  color: var(--accent-hi);
  transform: translateY(calc(-1 * var(--rail)));
}

.social-icon {
  --icon-sz: var(--s4);
  fill: currentColor;
  stroke: none;
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding-block-start: var(--s5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-caption);
  color: var(--muted);
}

/* ── Controls ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--s7);
  padding: var(--s3) var(--s4);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: var(--lh-label);
  color: var(--ink);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--t-micro) var(--ease),
    border-color var(--t-micro) var(--ease), color var(--t-micro) var(--ease),
    opacity var(--t-micro) var(--ease);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}

/* The only --brand-grad fill on a marketing page. */
.btn-primary {
  background-image: var(--brand-grad);
  background-size: 160% 160%;
  background-position: 0% 50%;
  border-color: transparent;
  color: var(--on-accent);
  transition: background-position var(--t-ui) var(--ease);
}

.btn-primary:hover {
  background-position: 100% 50%;
}

.btn-secondary {
  background: var(--surface-2);
  border-color: var(--line);
  color: var(--ink);
}

.btn-secondary:hover {
  border-color: var(--line-strong);
  background: var(--surface-3);
}

.btn-ghost {
  background: transparent;
  color: var(--ink-2);
}

.btn-ghost:hover {
  background: var(--surface-1);
  color: var(--ink);
}

.btn-lg {
  min-height: var(--s8);
  padding: var(--s4) var(--s5);
  font-size: var(--fs-lead);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
}

/* ── Forms ────────────────────────────────────────────────────────────── */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  min-width: 0;
}

/* Never a placeholder — always a real label above the control. */
.field-label {
  font-size: var(--fs-caption);
  font-weight: 500;
  line-height: var(--lh-label);
  color: var(--ink-2);
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: var(--s7);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  transition: border-color var(--t-micro) var(--ease),
    box-shadow var(--t-micro) var(--ease);
}

.textarea {
  min-height: calc(var(--s8) * 2);
  resize: vertical;
}

.input::placeholder,
.textarea::placeholder {
  color: var(--muted);
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}

.field-error {
  font-size: var(--fs-caption);
  color: var(--bad);
}

.field-hint {
  font-size: var(--fs-caption);
  color: var(--muted);
}

.form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s3);
}

.form-row .field {
  flex: 1 1 var(--footer-col-min);
}

/* ── Content ──────────────────────────────────────────────────────────── */
.lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-body);
  color: var(--ink-2);
}

/* Never nested. Subdivide the interior with .card-divide instead. */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--e1);
  transition: border-color var(--t-micro) var(--ease),
    transform var(--t-micro) var(--ease);
}

a.card:hover,
.card-link:hover {
  border-color: var(--line-strong);
}

.card-hero {
  border-radius: var(--r-hero);
  padding: var(--s6);
}

.card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s3);
}

.card-head h3 {
  min-width: 0;
}

.card-body {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  font-size: var(--fs-body);
  color: var(--ink-2);
}

/* Cards in one grid row stretch to equal height but their bodies do not:
 * a trailing link row (or bare .card-cta) floats at a different height in
 * each card. The auto margin pins it to the bottom edge so the links sit
 * on one line across the row. Inert on a lone card, whose height is its
 * content. */
.card > .row:last-child,
.card > .card-cta:last-child {
  margin-top: auto;
}

.card > .row:last-child {
  flex-wrap: wrap;
}

/* When that row overflows a narrow card, a whole link moves to the next
 * line; a label never breaks mid-word beside its icon. */
.card > .row:last-child .card-cta {
  white-space: nowrap;
}

/* Hairline divider inside a card — the alternative to nesting one. */
.card-divide {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0;
}

/* Small logo / icon tile. Replaces the retired .ico. */
.media {
  display: grid;
  place-items: center;
  width: var(--s7);
  height: var(--s7);
  flex: 0 0 auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--accent-hi);
  overflow: hidden;
}

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

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
  padding: var(--s1) var(--s2);
  border-radius: var(--r-pill);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  line-height: var(--lh-label);
  white-space: nowrap;
  background: var(--surface-2);
  color: var(--ink-2);
}

/* A dot inside a chip inherits the chip's hue — no second modifier needed. */
.chip .dot {
  background: currentColor;
}

/* A chip used as a card's status badge. `.card` is a flex column, so the chip's
 * default cross-axis stretch makes it span the full card width and stop reading
 * as a badge. Scoped to a direct child so the footer status pill, which lives in
 * a centred flex row, keeps its own alignment. */
.card > .chip {
  align-self: flex-start;
}

.chip-ok {
  background: var(--ok-tint);
  color: var(--ok);
}
.chip-warn {
  background: var(--warn-tint);
  color: var(--warn);
}
.chip-bad {
  background: var(--bad-tint);
  color: var(--bad);
}
.chip-info {
  background: var(--info-tint);
  color: var(--info);
}
.chip-accent {
  background: var(--accent-tint);
  color: var(--accent-hi);
}

.dot {
  width: var(--s2);
  height: var(--s2);
  flex: 0 0 auto;
  border-radius: var(--r-pill);
  background: var(--muted);
}

.dot-ok {
  background: var(--ok);
}
.dot-warn {
  background: var(--warn);
}
.dot-bad {
  background: var(--bad);
}
.dot-info {
  background: var(--info);
}
.dot-accent {
  background: var(--accent);
}

.stat {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}

.stat-value {
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: var(--lh-heading);
  /* Matches admin.css's .stat-value so the same named element is set the
   * same way on both surfaces. --track-tight arrived in brand.css for this. */
  letter-spacing: var(--track-tight);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.stat-label {
  font-size: var(--fs-caption);
  color: var(--muted);
}

.check-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.check-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  /* s2, not s3: a check-list is one related group and the wider rows read
   * as separate paragraphs rather than items of a list. */
  padding-block: var(--s2);
  border-top: 1px solid var(--line);
  font-size: var(--fs-body);
  color: var(--ink-2);
}

.check-list li:first-child {
  border-top: 0;
}

.check-list .dot,
.check-list .icon {
  margin-top: var(--s1);
}

.check-list .icon {
  color: var(--accent-hi);
}

.check-list strong {
  color: var(--ink);
  font-weight: 500;
}

.faq {
  display: flex;
  flex-direction: column;
}

.faq-item {
  padding-block: var(--s5);
  border-top: 1px solid var(--line);
}

.faq-item:first-child {
  border-top: 0;
}

.faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s2);
  font-size: var(--fs-lead);
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  list-style: none;
}

/* <details><summary class="faq-q"> — the default marker is removed. */
.faq-q::-webkit-details-marker {
  display: none;
}

.faq-q::marker {
  content: "";
}

details[open] > .faq-q {
  margin-bottom: var(--s3);
}

/* Chevron affordance in a <summary class="faq-q">. */
.faq-q .icon {
  color: var(--muted);
  transition: transform var(--t-ui) var(--ease);
}

details[open] > .faq-q .icon {
  transform: rotate(180deg);
}

.faq-a {
  font-size: var(--fs-body);
  color: var(--ink-2);
  max-width: var(--measure);
}

.notice {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s4);
  border: 1px solid var(--line);
  border-left: var(--rail) solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  font-size: var(--fs-body);
  color: var(--ink-2);
}

.notice .icon {
  margin-top: var(--s1);
}

.notice-ok {
  border-left-color: var(--ok);
  background: var(--ok-tint);
}

.notice-ok .icon {
  color: var(--ok);
}

.notice-warn {
  border-left-color: var(--warn);
  background: var(--warn-tint);
}

.notice-warn .icon {
  color: var(--warn);
}

.notice-bad {
  border-left-color: var(--bad);
  background: var(--bad-tint);
}

.notice-bad .icon {
  color: var(--bad);
}

/* <div class="empty"><svg class="icon"/><p class="empty-title"/><p class="empty-hint"/></div> */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s2);
  padding: var(--s7) var(--s5);
  color: var(--muted);
}

.empty .icon {
  color: var(--muted);
}

.empty-title {
  font-size: var(--fs-body);
  color: var(--ink-2);
}

.empty-hint {
  font-size: var(--fs-caption);
  color: var(--muted);
}

.skeleton {
  display: block;
  border-radius: var(--r-sm);
  background-color: var(--surface-2);
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    var(--shimmer) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  min-height: var(--s5);
}

/* Sized placeholders — a bare .skeleton has no intrinsic width. */
.skeleton-line {
  min-height: 0;
  height: var(--fs-body);
  width: 60%;
}

.skeleton-title {
  min-height: 0;
  height: var(--fs-h3);
  width: var(--footer-col-min);
}

.skeleton-price {
  min-height: 0;
  height: var(--fs-h1);
  width: 7ch;
}

.skeleton-row {
  min-height: 0;
  height: var(--row-h);
  width: 100%;
}

/* Copyable install command well.
 * <p class="cmd-label">Label</p>
 * <div class="cmd"><code>…</code><button class="btn btn-ghost cmd-copy">Copy</button></div>
 */
.cmd {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  font-family: var(--font-mono);
  font-size: var(--fs-compact);
  color: var(--ink-2);
}

.cmd code,
.cmd pre {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font: inherit;
  overflow-x: auto;
  white-space: pre;
}

.cmd-label {
  font-family: var(--font);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--s2);
}

.cmd-copy {
  flex: 0 0 auto;
  margin-left: auto;
  font-family: var(--font);
}

/* ── Hero (landing page only; inner pages use .page-hero) ─────────────── */
.hero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: calc(100svh - var(--s8));
  padding-block: var(--s8);
  background: var(--canvas);
  overflow: hidden;
  isolation: isolate;
}

.hero-canvas,
.hero-poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -2;
}

.hero-poster {
  object-fit: cover;
  opacity: 0.55;
}

/* When the poster is permanent — reduced motion, or WebGL refused a context —
 * it is the hero rather than a placeholder, so it carries full strength. */
.hero.poster-only .hero-poster {
  opacity: 1;
}

.hero-canvas {
  opacity: 0;
  transition: opacity var(--t-scene) var(--ease-enter);
}

.hero-canvas.is-live {
  opacity: 1;
}

/* Once WebGL owns the frame the poster stops competing with it. */
.hero.is-live .hero-poster {
  opacity: 0;
  transition: opacity var(--t-scene) var(--ease-enter);
}

/* Bottom feather for the LIGHT hero, the counterpart of the .on-dark edge
 * fades above: without it the WebGL grid and fog stop dead at the section
 * boundary. It dissolves exactly like the left-edge scrim does — over a
 * third of the band, through a half-strength middle stop, never as a white
 * strip — into --page-canvas (which the hero never remaps). z-index auto
 * stacks it above the z-index:-2/-1 scene layers and below the copy. */
.hero::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: clamp(120px, 22vh, 220px);
  pointer-events: none;
  background: linear-gradient(
    0deg,
    var(--page-canvas) 0%,
    color-mix(in srgb, var(--page-canvas) 55%, transparent) 55%,
    transparent 100%
  );
}

.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    var(--canvas) 0%,
    color-mix(in srgb, var(--canvas) 70%, transparent) 45%,
    transparent 75%
  );
}

/* The one --brand-grad glow allowed per marketing page. */
.hero-glow {
  position: absolute;
  z-index: -1;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: calc(var(--hero-copy-max) * 1.4);
  height: calc(var(--hero-copy-max) * 1.4);
  transform: translate(-35%, -35%);
  border-radius: var(--r-pill);
  background: var(--brand-grad);
  filter: blur(var(--glow-blur));
  opacity: 0.22;
  pointer-events: none;
}

.hero-copy {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s5);
  max-width: var(--hero-copy-max);
}

.hero-copy h1 {
  font-size: var(--fs-display);
  font-weight: 700;
  letter-spacing: var(--track-display);
  line-height: 1.05;
}

.hero-copy .lead {
  max-width: var(--hero-copy-max);
}

/* Inner-page header band: no WebGL, no poster, same rhythm. */
.page-hero {
  background: var(--canvas);
  padding-block: var(--s8) var(--s7);
  border-bottom: 1px solid var(--line);
}

.page-hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s4);
  max-width: var(--measure);
}

/* Downloads header: copy on the left, the visitor's own installer on the right.
 * Single column below the tablet breakpoint so the card never squeezes the
 * numbered steps. */
.page-hero-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s6);
  align-items: start;
}

@media (min-width: 60rem) {
  .page-hero-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
    gap: var(--s7);
  }
}

.dl-hero {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  border: 1px solid var(--accent-ring);
  border-radius: var(--r-lg);
  background: var(--surface-1);
  box-shadow: var(--e1);
}

.dl-hero .eyebrow {
  color: var(--accent-hi);
}

/* The hero reuses the grid's card markup; strip the nested plate so it reads as
 * one panel rather than a card inside a card. */
.dl-hero .dl-card {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

/* ── Page shapes ──────────────────────────────────────────────────────── */
/* Product-detail hero diagram. A strong central verdict plus compact evidence
 * chips stays legible in the narrow hero rail where full illustrations become
 * too small to read. */
.product-map {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
}

.product-map-core {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s2);
  padding: var(--s5);
  border: 1px solid var(--accent-ring);
  border-radius: var(--r-lg);
  background: var(--accent-tint);
}

.product-map-icon {
  width: var(--s8);
  height: var(--s8);
  object-fit: contain;
}

.product-map-signals {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s2);
}

.product-map-signals .chip {
  white-space: normal;
}

/* Home · Business · MSP trio. Used as `<a class="card audience-card">`. */
.audience-card {
  gap: var(--s4);
}

.audience-card .eyebrow {
  color: var(--accent-hi);
}

.audience-card p {
  flex: 1 1 auto;
}

.card-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--ink);
}

/* Product-detail links are intentionally button-sized so visitors do not miss
 * the deeper explanation behind each overview card. */
.product-page-link {
  justify-content: space-between;
  width: 100%;
  min-height: var(--s7);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--accent-ring);
  border-radius: var(--r-md);
  background: var(--accent-tint);
  box-shadow: var(--e1);
  font-size: var(--fs-lead);
  color: var(--accent-hi);
  transition:
    border-color var(--t-micro) var(--ease),
    background var(--t-micro) var(--ease),
    transform var(--t-micro) var(--ease),
    box-shadow var(--t-micro) var(--ease);
}

a.product-page-link:hover {
  border-color: var(--accent);
  background: var(--surface-1);
  box-shadow: var(--e2);
  transform: translateY(calc(-1 * var(--rail)));
}

.card-cta .icon {
  transition: transform var(--t-micro) var(--ease);
}

.card:hover .card-cta .icon,
a.card-cta:hover .icon {
  transform: translateX(var(--s1));
}

/* Pricing. Used as `<article class="card price-card">`. */
.price-card {
  gap: var(--s4);
}

/* Pricing keeps the included/add-on support distinction visible without
 * overloading the homepage. Matching card bodies hold both actions level. */
.support-pricing-grid {
  margin-inline: auto;
}

.support-price-card .card-body {
  flex: 1 1 auto;
}

.price-card.is-featured {
  border-color: var(--accent);
}

.price-value {
  font-size: var(--fs-h1);
  font-weight: 600;
  letter-spacing: var(--track-display);
  line-height: var(--lh-heading);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.price-period {
  font-size: var(--fs-body);
  font-weight: 400;
  color: var(--muted);
}

.price-features {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  flex: 1 1 auto;
  font-size: var(--fs-body);
  color: var(--ink-2);
}

.price-features li {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
}

.price-features .icon {
  color: var(--accent-hi);
  margin-top: var(--s1);
}

/* Two stacked actions: the plan CTA, then the optional "see what you get". */
.price-cta {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.price-cta .btn {
  width: 100%;
}

.btn-sm {
  min-height: var(--s6);
  padding: var(--s2) var(--s3);
  font-size: var(--fs-compact);
}

/* The tier a visitor picked, while its pictures are open. */
.tier-card.is-selected {
  border-color: var(--accent);
  box-shadow: var(--e2);
}

/* Per-tier illustration panel. Hidden until a tier is chosen, so the page does
 * not open on a wall of art that belongs to no plan in particular. */
.tier-visuals {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-2);
}

.tier-visuals-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
}

.tier-visuals-head h3 {
  margin: 0;
}

.tier-visuals .illo {
  background: var(--surface-1);
}

/* ── Pricing matrix (track tabs, tier cards, pooled-storage estimator,
 * capability comparison table). Appended rather than folded into the local-
 * constants :root at the top of this file so that block stays untouched;
 * this is the one dimension the token set has no name for. ────────────── */
:root {
  /* The 1px/-1px "clip a 1x1 box" idiom used by .sr-only below — not a
     spacing decision, just the mechanics of the visually-hidden technique. */
  --sr-only-sz: 1px;
}

.sr-only {
  position: absolute;
  width: var(--sr-only-sz);
  height: var(--sr-only-sz);
  padding: 0;
  margin: calc(var(--sr-only-sz) * -1);
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Track selector. Same underline-tab convention as the console's .tabs/.tab
 * (same --tab-h token from brand.css), reimplemented here because admin.css
 * never loads on a marketing page. */
.track-tabs {
  display: flex;
  gap: var(--s2);
  overflow-x: auto;
  border-bottom: 1px solid var(--line);
}

.track-tab {
  appearance: none;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  height: var(--tab-h);
  padding-inline: var(--s4);
  border: 0;
  border-bottom: 1px solid transparent;
  background: transparent;
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--t-micro) var(--ease), border-color var(--t-micro) var(--ease);
}

.track-tab:hover {
  color: var(--ink);
}

.track-tab[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

.track-tab:focus-visible {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: var(--focus-ring);
}

.track-blurb {
  max-width: var(--measure);
}

/* Pooled-storage estimator. Built from .card + .field + .input; these three
 * classes only add the calculator's own layout and JS hooks. */
.pool-calc {
  align-items: flex-start;
}

.pool-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3);
}

.pool-input {
  max-width: var(--footer-col-min);
}

.pool-out {
  font-weight: 600;
  color: var(--accent-hi);
  font-variant-numeric: tabular-nums;
}

/* Tier cards. `<article class="card price-card tier-card">` — price-card's
 * gap/is-featured rules above already apply; these add the matrix-specific
 * pieces (level eyebrow, "Most popular" badge, stack line, storage line). */
.tier-grid {
  display: grid;
  gap: var(--s4);
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min), 1fr));
  align-items: stretch;
}

.tier-card {
  min-width: 0;
}

.tier-level {
  color: var(--accent-hi);
}

.tier-name {
  min-width: 0;
}

/* The stack line ("AI-EDR + email defense + operations RMM") is product prose,
 * not a command or a checksum — the mono stack is reserved for things a reader
 * might copy. A tracked caption instead. */
.tier-stack {
  font-size: var(--fs-caption);
  letter-spacing: var(--track-tight);
}

/* A level that no catalog plan maps to. Same slot and rhythm as a real price,
 * without pretending to be a figure. */
.tier-quoted {
  color: var(--muted);
  font-size: var(--fs-h3);
}

.tier-summary {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--ink-2);
}

.tier-badge {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: var(--s1) var(--s3);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
}

.tier-storage {
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-caption);
  color: var(--muted);
}

/* Capability comparison matrix. No `position: sticky` on `thead th` — the
 * site header is already sticky and the two collide. */
.compare-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

.compare {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-body);
}

.compare th,
.compare td {
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
  text-align: center;
  vertical-align: middle;
}

.compare thead th {
  background: var(--surface-2);
  border-bottom: 1px solid var(--line-strong);
}

.compare tbody th[scope="row"] {
  text-align: left;
  font-weight: 500;
  color: var(--ink);
  background: var(--surface-1);
}

.compare tbody tr:last-child th,
.compare tbody tr:last-child td {
  border-bottom: 0;
}

.ch-level {
  min-width: 0;
}

.ch-name {
  display: block;
  font-size: var(--fs-caption);
  font-weight: 600;
  color: var(--ink);
}

.cap-label {
  display: block;
  color: var(--ink);
}

.cap-note {
  display: block;
  margin-top: var(--s1);
  font-size: var(--fs-caption);
  font-weight: 400;
  color: var(--muted);
}

.tick {
  color: var(--accent-hi);
}

.dash {
  color: var(--muted);
}

.quota-row th,
.quota-row td {
  background: var(--surface-2);
  font-weight: 600;
  color: var(--ink);
}

/* Downloads. Used as `<article class="card dl-card">`. */
.dl-card {
  gap: var(--s4);
}

.dl-os {
  display: flex;
  align-items: center;
  gap: var(--s3);
}

.dl-os h3 {
  min-width: 0;
}

.dl-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  font-size: var(--fs-caption);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.dl-meta code {
  font-family: var(--font-mono);
  font-size: var(--fs-eyebrow);
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* The ONLY region on any marketing page where copy is centred. Now that the
 * dark build is retired here, the closing callout stands out with an accent
 * tint and ring instead of an inverted surface — and stays card-sized rather
 * than spanning the full wrap. */
.cta-band {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s5);
  max-width: var(--cta-band-max);
  margin-inline: auto;
  padding: var(--s8) var(--s5);
  border: 1px solid var(--accent-ring);
  border-radius: var(--r-hero);
  background: var(--accent-tint);
  box-shadow: var(--e1);
}

.brand-crest {
  width: var(--brand-crest);
  height: var(--brand-crest);
  object-fit: contain;
}

.cta-band p {
  max-width: var(--hero-copy-max);
}

/* Partner / customer logo strip. */
.logo-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s6);
}

.logo-strip img {
  height: var(--s6);
  width: auto;
  opacity: 0.7;
  transition: opacity var(--t-micro) var(--ease);
}

.logo-strip img:hover {
  opacity: 1;
}

/* Third-party marks are dark artwork on transparency, so a light --surface-1
 * plate reads them correctly. Kept as a plate rather than dropped so the strip
 * stays visually even when a vendor does ship a light-background mark. */
.logo-plate {
  display: grid;
  place-items: center;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
}

/* Keep dark vendor wordmarks on the light plate when the page palette inverts. */
:root[data-theme="dark"] #partners .logo-plate {
  border-color: var(--line-strong);
  background: var(--ink);
}

/* Dimming a mark that already sits on a light plate only looks faded. */
.logo-plate img,
.logo-plate img:hover {
  opacity: 1;
}

/* ── 1440: display type earns its extra size ──────────────────────────── */
@media (min-width: 1440px) {
  .hero-copy h1 {
    font-size: var(--fs-display-lg);
  }
}

/* ── 1100: split stacks, the hero restacks copy-over-scene ────────────── */
@media (max-width: 1100px) {
  .split {
    grid-template-columns: 1fr;
    gap: var(--s6);
  }

  /* There is no room for a 560px column beside the scene, so the copy moves
   * to the top of the hero and the scene keeps the space below it. The scrim
   * turns vertical to match: opaque behind the copy, clear over the scene. */
  .hero {
    align-items: flex-start;
  }

  .hero-scrim {
    background: linear-gradient(
      180deg,
      var(--canvas) 0%,
      color-mix(in srgb, var(--canvas) 82%, transparent) 48%,
      transparent 100%
    );
  }
}

/* The account actions can grow to one portal per purchased product. Collapse
 * the header before those controls or the primary links can be clipped. */
@media (max-width: 1180px) {
  .site-header-inner,
  .site-header > .wrap {
    flex-wrap: wrap;
  }

  .nav-toggle {
    display: inline-flex;
  }

  .site-nav {
    display: none;
    width: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s1);
    padding-block: var(--s3);
    border-top: 1px solid var(--line);
  }

  .site-nav.is-open {
    display: flex;
  }

  .nav-auth,
  .nav-auth-content,
  .nav-portals {
    width: 100%;
    flex-direction: column;
    align-items: stretch;
    margin-left: 0;
  }

  .site-nav .btn {
    width: 100%;
    margin-left: 0;
  }

  .nav-perspective {
    width: 100%;
    flex-direction: column;
    align-items: stretch;
  }

  .nav-perspective-menu {
    position: static;
    width: 100%;
    min-width: 0;
    margin-top: var(--s1);
    box-shadow: none;
    box-sizing: border-box;
  }

  .site-nav .nav-perspective-menu a {
    width: 100%;
  }
}

/* ── 768: single-column content ──────────────────────────────────────── */
@media (max-width: 768px) {
  .wrap {
    padding-inline: var(--s4);
  }

  .section {
    padding-block: var(--s7);
  }

  .hero-copy h1 {
    font-size: var(--fs-h1);
  }

  .hero-copy {
    gap: var(--s4);
  }


  .cta-row .btn {
    flex: 1 1 auto;
  }

  .cta-band {
    padding: var(--s7) var(--s4);
  }

  .footer-cols {
    gap: var(--s5);
  }

  .grid-2 {
    max-width: none;
  }

  /* Trios stack; auto-fit would seat two tracks here and orphan the third. */
  .grid-3 {
    grid-template-columns: 1fr;
  }
}

/* ── Motion, gated on the user's preference ───────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .skeleton {
    animation: cc-shimmer var(--t-shimmer) linear infinite;
  }

  @keyframes cc-shimmer {
    from {
      background-position: 200% 0;
    }
    to {
      background-position: -200% 0;
    }
  }

  a.card:hover,
  .card-link:hover {
    transform: translateY(-1px);
  }
}

/* ── Explainer illustrations ───────────────────────────────────────────────
 * The animated SVGs at /brand/illo-*.svg. They are loaded through <img> so the
 * page cannot restyle them; these rules only frame and size them. Each one is
 * 640x400, so `aspect-ratio` reserves the box and the page never reflows when
 * the image lands.
 */
.illo {
  display: block;
  width: 100%;
  aspect-ratio: 8 / 5;
  border-radius: var(--r-lg);
  background: var(--surface-1);
  border: 1px solid var(--line);
}

/* Inside a card the frame is already drawn by the card itself. */
.card > .illo,
.card > .figure > .illo {
  border: 0;
  border-radius: var(--r-md);
  background: var(--surface-2);
}

.figure {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  margin: 0;
}

.figure figcaption {
  font-size: var(--fs-compact);
  color: var(--muted);
  max-width: var(--measure);
}

/* A capability: illustration above, short claim below. Three across, one down
 * at 768. */
.explain {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min), 1fr));
  gap: var(--s5);
}

.explain h3 {
  margin-bottom: var(--s1);
}

/* The public capability grid groups all product actions into one row. Equal
 * flex heights keep their labels, descriptions, and buttons aligned. */
.capability-item {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.capability-item h3 {
  margin: 0;
}

.capability-item .product-page-link {
  margin-top: auto;
}

/* Alternating illustration-beside-copy row. `.flip` puts the art first on wide
 * screens; both orders collapse to art-then-copy when the row stacks. */
.showcase {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s7);
  /* start, not center: centering floated the art panel between empty bands
   * whenever the copy column ran taller, and the two columns never began on
   * the same line. Top-aligned, the panel hugs its artwork. */
  align-items: start;
}

.showcase + .showcase {
  margin-top: var(--s7);
}

.showcase-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s4);
  max-width: var(--measure);
}

@media (min-width: 769px) {
  .showcase.flip .showcase-art {
    order: -1;
  }
}

@media (max-width: 768px) {
  .showcase {
    grid-template-columns: 1fr;
    gap: var(--s5);
  }

  .showcase .showcase-art {
    order: -1;
  }

  .showcase + .showcase {
    margin-top: var(--s6);
  }
}

/* Numbered "how it works" steps. The number is decorative; the heading carries
 * the meaning for a screen reader. */
.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min), 1fr));
  gap: var(--s5);
  counter-reset: step;
}

.step {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  counter-increment: step;
}

.step::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: var(--s6);
  height: var(--s6);
  border-radius: var(--r-pill);
  background: var(--accent-tint);
  color: var(--accent-hi);
  font-size: var(--fs-lead);
  font-weight: 700;
}

/* Advanced EDR trust path. Ordered HTML keeps the architecture readable when
 * layout or colour is unavailable; the rail is only a visual guide. */
.trust-path {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s4);
  margin: 0;
  padding: 0;
  list-style: none;
}

.trust-layer {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-top: var(--rail) solid var(--accent-hi);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--e1);
  color: var(--ink-2);
}

.trust-layer-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
}

.trust-layer-head > div {
  display: flex;
  min-width: 0;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s2);
}

.trust-index {
  font-family: var(--font-mono);
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  color: var(--accent-hi);
}

.trust-extensions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s4);
  margin-top: var(--s5);
}

@media (max-width: 900px) {
  .trust-path {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 768px) {
  .trust-path,
  .trust-extensions {
    grid-template-columns: 1fr;
  }
}

/* Trust Center evidence lists. These are flatter than marketing cards because
 * each row is a record from a public source of truth, not a promotional tile. */
.trust-principles {
  align-self: stretch;
}

.trust-capability-list,
.trust-proof-list {
  display: grid;
  gap: var(--s3);
}

.trust-capability {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(var(--grid-min), 0.55fr);
  gap: var(--s5);
  align-items: center;
  padding: var(--s4) var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--e1);
}

.trust-capability-copy {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.trust-capability-copy h3,
.trust-capability-copy p,
.trust-proof p {
  margin: 0;
}

.trust-platforms {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.trust-platforms li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  color: var(--ink-2);
  font-size: var(--fs-caption);
}

.trust-support-supported {
  background: var(--ok-tint);
  color: var(--ok);
}

.trust-support-limited {
  background: var(--warn-tint);
  color: var(--warn);
}

.trust-support-unsupported {
  background: var(--surface-3);
  color: var(--muted);
}

.trust-proof-list {
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min), 1fr));
}

.trust-proof {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  padding: var(--s5);
  border-left: var(--rail) solid var(--ok);
  background: var(--surface-1);
  box-shadow: var(--e1);
}

.trust-proof-metric {
  color: var(--ink-2);
  font-size: var(--fs-lead);
}

@media (max-width: 768px) {
  .trust-capability {
    grid-template-columns: 1fr;
    gap: var(--s4);
    padding: var(--s4);
  }

  .trust-platforms {
    grid-template-columns: 1fr;
  }
}

/* ── "What's included" plan cards (product page) ───────────────────────────
 * Rendered from pricing-catalog.json, grouped by the audience each plan is sold
 * to, so the copy cannot drift from the plans the price book bills. The row
 * wraps instead of dividing into fixed columns: one audience sells a single plan
 * and another sells two, and a lone card squeezed into a third of the viewport
 * reads as two cards that failed to load. Cards stop growing past 420px so a
 * single-plan audience does not stretch six bullets across the whole page, and
 * wrap below 320px because a card with seven bullets is unreadable narrower.
 */
.pkg-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4);
  align-items: stretch;
}

.pkg-card {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  flex: 1 1 320px;
  max-width: 420px;
}

.pkg-card .check-list {
  margin-top: var(--s2);
}

.pkg-card .tier-storage {
  margin-top: var(--s3);
}

.pkg-cta {
  margin-top: auto;
  padding-top: var(--s4);
}

/* ── "We can run it for you" callout ──────────────────────────────────────
 * A managed-service offer that has to stand out mid-page without being a third
 * dark band. An accent-tinted plate does that: the pages already spend their one
 * dark region on the hero and their closing CTA, and stacking another would
 * break the one-dark-band-per-viewport rule the light build is built on.
 */
.managed {
  display: grid;
  grid-template-columns: 1fr minmax(var(--grid-min), 0.8fr);
  gap: var(--s6);
  align-items: center;
  padding: var(--s6);
  border: 1px solid var(--accent);
  border-radius: var(--r-lg);
  background: var(--accent-tint);
}

@media (max-width: 900px) {
  .managed {
    grid-template-columns: 1fr;
    gap: var(--s5);
    padding: var(--s5);
  }

  .managed-art {
    order: -1;
  }
}

.managed-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s3);
  max-width: var(--measure);
}

.managed-copy h2,
.managed-copy h3 {
  margin: 0;
}

.managed-copy .cta-row {
  margin-top: var(--s2);
}

.managed .illo {
  background: var(--surface-1);
}

/* ── Old-way / our-way contrast ───────────────────────────────────────────
 * Two columns that only work read together, so they share a container rather
 * than living in a generic card grid where a reader might take one alone.
 */
.versus {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s4);
}

@media (max-width: 768px) {
  .versus {
    grid-template-columns: 1fr;
  }
}

.versus-then,
.versus-now {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.versus-then {
  background: var(--surface-2);
}

.versus-now {
  background: var(--surface-1);
  border-color: var(--accent);
  box-shadow: var(--e1);
}

.versus h3 {
  margin: 0;
}

.versus-then h3 {
  color: var(--muted);
}

.versus-now h3 {
  color: var(--accent-hi);
}

.versus ul {
  margin: 0;
  padding-left: var(--s5);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.versus-then ul {
  color: var(--muted);
}

.versus-now ul {
  color: var(--ink-2);
}

/* A trailing note under a card grid or table — the fact that did not earn a card
 * of its own. Sits after the grid, not inside it, so it never orphans a cell. */
.section-foot {
  margin-top: var(--s5);
  max-width: var(--measure);
}


/* ── Customer portal + browser remote: handheld operator surfaces ─────── */
@media (max-width: 768px) {
  .customer-portal {
    display: block;
    min-width: 0;
    overflow-x: hidden;
    padding:
      max(var(--s2), env(safe-area-inset-top))
      max(var(--s2), env(safe-area-inset-right))
      max(var(--s2), env(safe-area-inset-bottom))
      max(var(--s2), env(safe-area-inset-left));
  }

  .customer-portal .portal-card,
  .customer-portal .portal-card.is-wide {
    width: 100%;
    max-width: none;
    margin-inline: auto;
    padding: var(--s4);
  }

  .customer-portal .portal-head {
    flex-direction: column;
    align-items: stretch;
  }

  .customer-portal .portal-head > * {
    min-width: 0;
  }

  .customer-portal .portal-head > .btn {
    width: 100%;
    min-height: var(--s7);
  }

  .customer-portal .portal-tabs {
    flex-wrap: nowrap;
    gap: var(--s3);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .customer-portal .portal-tabs::-webkit-scrollbar {
    display: none;
  }

  .customer-portal .portal-tab {
    min-height: var(--s7);
    padding-inline: var(--s1);
  }

  .customer-portal .plist {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s3);
  }

  .customer-portal .plist-item {
    min-width: 0;
    padding: var(--s3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-1);
  }

  .customer-portal .plist-item:last-child {
    border-bottom: 1px solid var(--line);
  }

  .customer-portal .plist-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
    gap: var(--s2);
  }

  .customer-portal .plist-name,
  .customer-portal .plist-sub,
  .customer-portal .thread-msg,
  .customer-portal .kv > span {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .customer-portal .kv {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s1);
  }

  .customer-portal .plist-actions,
  .customer-portal .tab-lead {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    width: 100%;
  }

  .customer-portal .plist-actions > .btn,
  .customer-portal .tab-lead > .btn,
  .customer-portal .tab-lead > a,
  .customer-portal .rename-btn {
    width: 100%;
    min-height: var(--s7);
  }

  .customer-portal .back-link {
    min-height: var(--s7);
  }

  .browser-remote {
    display: block;
    min-width: 0;
    overflow-x: hidden;
    padding:
      max(var(--s2), env(safe-area-inset-top))
      max(var(--s2), env(safe-area-inset-right))
      max(var(--s2), env(safe-area-inset-bottom))
      max(var(--s2), env(safe-area-inset-left));
  }

  .browser-remote .card {
    width: 100%;
    min-width: 0;
    margin-inline: auto;
    padding: var(--s4);
    box-sizing: border-box;
  }

  .browser-remote .row,
  .browser-remote .actions {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }

  .browser-remote .fact {
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
  }

  .browser-remote .peer,
  .browser-remote .fact .v {
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: normal;
  }

  .browser-remote .peer {
    font-size: var(--fs-h2);
  }

  .browser-remote .btn,
  .browser-remote input,
  .browser-remote select,
  .browser-remote textarea {
    width: 100%;
    min-height: var(--s7);
  }

  .browser-remote .steps {
    display: block;
    padding-left: var(--s5);
  }

  .browser-remote .toast {
    bottom: max(var(--s3), env(safe-area-inset-bottom));
    width: calc(100% - (var(--s4) * 2));
    box-sizing: border-box;
  }
}/* ── Investor relations (/investors) ───────────────────────────────────────
 * The hero scene and its painted fallback, the scroll reveal, the pointer
 * tilt, the platform stack, the roadmap timeline, the funding chart and its
 * round cards, and the compute-partner grid. Only this page uses them.
 * Every value resolves through a token from brand.css or the theme layer in
 * 01-theme.css — no colour, spacing, radius, shadow or motion literal is
 * authored here. The handful of structural numbers this page needs that the
 * token set does not name are derived from tokens by arithmetic, once, in
 * the block below.
 */

/* ── Page-local constants ─────────────────────────────────────────────────
 * Derived, never authored. --ir-persp is the 900px of `.tilt-scene` in
 * brand.css expressed as --wrap-max × 0.75, so the page tilts on the same
 * focal length as the rest of the design language. --ir-hairline is the 1px
 * rule every surface draws, as half of --rail.
 */
.ir-page {
  --ir-persp: calc(var(--wrap-max) * 0.75);
  --ir-hairline: calc(var(--rail) / 2);
  /* Pitch of the hero poster's perspective grid, and the gap between two
   * slabs of the platform stack. */
  --ir-grid-cell: var(--s7);
  --ir-slab-lift: var(--s5);
  /* Plot height of the funding chart: three KPI rows tall. */
  --ir-chart-h: calc(var(--kpi-h) * 3);
}

/* ── Hero band ────────────────────────────────────────────────────────────
 * Applied together with `page-hero on-dark`, so --band and the dark ink ramp
 * are already remapped by .on-dark; this only adds the glow behind the copy.
 */
.ir-band {
  position: relative;
  overflow: hidden;
  background: var(--band);
}

.ir-band::after {
  content: "";
  position: absolute;
  inset-inline-end: 0;
  top: 0;
  width: var(--glow-blur);
  height: var(--glow-blur);
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  filter: blur(var(--glow-blur));
  pointer-events: none;
}

.ir-band > .wrap {
  position: relative;
}

/* ── Hero: the scene sits behind the copy ─────────────────────────────────
 * `.ir-band` above already owns position and overflow; this adds the height
 * the scene needs and the stacking context that keeps its negative z-index
 * local — above the band's own background, below `.ir-band > .wrap`.
 */
.ir-hero {
  display: flex;
  align-items: center;
  min-height: 78svh;
  isolation: isolate;
}

/* The scene is out of flow, so .wrap is the only flex item; let it own the
 * row so its auto margins still centre it at --wrap-max. */
.ir-hero > .wrap {
  flex: 1 1 auto;
}

.ir-scene {
  position: relative;
  overflow: hidden;
}

.ir-hero > .ir-scene {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
}

/* Painted fallback: no image, no WebGL, no script. An on-brand wash that is
 * finished before three.js loads — and stays finished when it never does,
 * which is the reduced-motion and no-WebGL path. */
.ir-scene-poster {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
  transition: opacity var(--t-scene) var(--ease-enter);
  background:
    radial-gradient(
      ellipse at 72% 18%,
      color-mix(in srgb, var(--accent) 28%, transparent) 0%,
      transparent 62%
    ),
    radial-gradient(
      ellipse at 12% 82%,
      color-mix(in srgb, var(--violet) 34%, transparent) 0%,
      transparent 58%
    ),
    radial-gradient(
      circle at 50% 118%,
      color-mix(in srgb, var(--accent-hi) 16%, transparent) 0%,
      transparent 55%
    );
}

/* The floor. Two repeating hairlines laid flat by a real 3D rotation, then
 * masked out as they recede, so the grid dissolves into the wash instead of
 * ending on a hard line. The mask reads alpha only, so --ink is a stand-in
 * for "opaque" and paints nothing.
 *
 * The bottom edge has to sit ON the poster's bottom edge: the rotation pivots
 * there, so anything below it folds up out of the band's clip and the whole
 * floor lands outside the visible box. */
.ir-hero .ir-scene-poster::before {
  content: "";
  position: absolute;
  inset: 34% -60% 0;
  transform: perspective(var(--ir-persp)) rotateX(66deg);
  transform-origin: 50% 100%;
  opacity: 0.8;
  background-image:
    repeating-linear-gradient(
      to right,
      var(--line-strong) 0 var(--ir-hairline),
      transparent var(--ir-hairline) var(--ir-grid-cell)
    ),
    repeating-linear-gradient(
      to bottom,
      var(--line-strong) 0 var(--ir-hairline),
      transparent var(--ir-hairline) var(--ir-grid-cell)
    );
  -webkit-mask-image: linear-gradient(to top, var(--ink) 0%, transparent 100%);
  mask-image: linear-gradient(to top, var(--ink) 0%, transparent 100%);
}

.ir-scene-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  opacity: 0;
  transition: opacity var(--t-scene) var(--ease-enter);
}

.ir-scene.is-live .ir-scene-canvas {
  opacity: 1;
}

/* Once WebGL owns the frame the poster stops competing with it. */
.ir-scene.is-live .ir-scene-poster {
  opacity: 0;
}

/* ── Hero stat row ─────────────────────────────────────────────────────── */
.ir-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--hero-stat-min), 1fr));
  gap: var(--s4);
  margin-top: var(--s6);
  /* .page-hero-copy is a start-aligned flex column, which would shrink-wrap
   * an auto-fit grid to one track; stretch so the four stats read as a row. */
  align-self: stretch;
  /* Four stats across the hero copy column (--hero-copy-max): a stat is a
   * number and a caption, not a card, so it needs well under --grid-min-3. */
  --hero-stat-min: calc(var(--grid-min-3) / 2.4);
}

/* ── Reveal on scroll ─────────────────────────────────────────────────────
 * Gated on the script: `is-js` lands on <body> before the first observer
 * fires, so a reader without JavaScript never meets a hidden element. The
 * gate is wrapped in `:where()` to keep it at zero specificity — that is
 * what lets the tilt rule below own `transform` on the cards that do both,
 * and it is why the offset travels as a custom property rather than as a
 * second, competing transform.
 */
:where(body.is-js) [data-ir-reveal] {
  --ir-reveal-y: var(--s4);
  opacity: 0;
  transform: translate3d(0, var(--ir-reveal-y), 0);
  transition: opacity calc(var(--t-scene) * 1.5) var(--ease-enter),
    transform calc(var(--t-scene) * 1.5) var(--ease-enter);
}

:where(body.is-js) [data-ir-reveal].is-visible {
  --ir-reveal-y: 0;
  opacity: 1;
  transform: none;
}

/* ── Pointer tilt ─────────────────────────────────────────────────────────
 * One transform carries both jobs: the reveal offset the rules above set,
 * and the two angles the script writes on pointermove. Declared apart, the
 * later rule would win and one of the two would silently be lost.
 */
.ir-page .ir-tilt {
  position: relative;
  transform-style: preserve-3d;
  transform: perspective(var(--ir-persp))
    translate3d(0, var(--ir-reveal-y, 0), 0) rotateX(var(--tilt-x, 0deg))
    rotateY(var(--tilt-y, 0deg));
  transition: transform var(--t-view) var(--ease),
    opacity calc(var(--t-scene) * 1.5) var(--ease-enter),
    border-color var(--t-ui) var(--ease), box-shadow var(--t-ui) var(--ease);
}

.ir-tilt:hover {
  border-color: var(--accent-ring);
  box-shadow: var(--e2), 0 0 0 var(--ir-hairline) var(--accent-ring);
}

/* Specular highlight that follows the pointer. The transform above makes the
 * card a stacking context, so z-index -1 puts the glow over the card's own
 * background and under its copy without a wrapper. */
.ir-tilt-glow {
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-ui) var(--ease);
  background: radial-gradient(
    circle at var(--glow-x, 50%) var(--glow-y, 50%),
    var(--accent-tint) 0%,
    transparent 60%
  );
}

.ir-tilt:hover .ir-tilt-glow,
.ir-tilt:focus-within .ir-tilt-glow {
  opacity: 1;
}

/* ── Thesis pillars ────────────────────────────────────────────────────── */
.ir-pillars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min-3), 1fr));
  gap: var(--s4);
}

.ir-pillar-icon {
  width: calc(var(--icon-sz) * 2.2);
  height: calc(var(--icon-sz) * 2.2);
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-hi);
}

/* ── Platform: three products on one model ────────────────────────────────
 * Scene on one side, the layer tabs on the other. The tabs are the control
 * surface for the WebGL stack and the whole story on their own when there
 * is no WebGL, so they never depend on it.
 */
.ir-platform-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(var(--grid-min-3), 0.9fr);
  gap: var(--s6);
  align-items: center;
}

/* `overflow` stays visible: any other value forces `transform-style` to
 * flat, which would collapse the slab stack below into four flat rectangles.
 * The canvas rounds its own corners instead (border-radius: inherit). */
.ir-scene-stack {
  aspect-ratio: 4 / 3;
  overflow: visible;
  border: var(--ir-hairline) solid var(--line);
  border-radius: var(--r-hero);
  background:
    radial-gradient(
      circle at 50% 45%,
      color-mix(in srgb, var(--accent) 16%, transparent) 0%,
      transparent 65%
    ),
    var(--surface-2);
  box-shadow: var(--e2);
  perspective: var(--ir-persp);
  touch-action: none;
}

.ir-scene-stack.is-live {
  cursor: grab;
}

/* Fallback stack, pure CSS: four slabs in one shared 3D space. Two pseudo
 * elements on the container plus two on the poster is exactly four layers
 * without asking the HTML for filler divs; `preserve-3d` on the poster keeps
 * its pair inside the container's perspective, so all four sort by depth
 * like real geometry rather than by DOM order. */
.ir-scene-stack .ir-scene-poster {
  overflow: visible;
  background: none;
  transform-style: preserve-3d;
}

.ir-scene-stack::before,
.ir-scene-stack::after,
.ir-scene-stack .ir-scene-poster::before,
.ir-scene-stack .ir-scene-poster::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 58%;
  height: 40%;
  border: var(--ir-hairline) solid var(--line-strong);
  border-radius: var(--r-lg);
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--accent-hi) 26%, transparent) 0%,
    color-mix(in srgb, var(--violet) 38%, transparent) 100%
  );
  box-shadow: var(--e2);
  transition: opacity var(--t-scene) var(--ease-enter);
  /* translateZ comes last, so each slab lifts along its own normal — the
   * stack direction — instead of straight up the screen. */
  transform: translate(-50%, -50%) rotateX(58deg) rotateZ(-32deg)
    translateZ(var(--ir-slab-z, 0));
}

/* Bottom slab: the shared security model everything else rests on, so it
 * reads as substrate rather than as a fourth product. */
.ir-scene-stack::before {
  --ir-slab-z: 0;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--ink) 14%, transparent) 0%,
    color-mix(in srgb, var(--violet) 26%, transparent) 100%
  );
}

.ir-scene-stack .ir-scene-poster::before {
  --ir-slab-z: var(--ir-slab-lift);
}

.ir-scene-stack .ir-scene-poster::after {
  --ir-slab-z: calc(var(--ir-slab-lift) * 2);
}

.ir-scene-stack::after {
  --ir-slab-z: calc(var(--ir-slab-lift) * 3);
}

/* The poster's own pair fades with the poster; these two are the container's
 * children and need saying so themselves. */
.ir-scene-stack.is-live::before,
.ir-scene-stack.is-live::after {
  opacity: 0;
}

.ir-layers {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ir-layer {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s1) var(--s4);
  width: 100%;
  padding: var(--s4);
  text-align: start;
  font: inherit;
  color: var(--ink);
  cursor: pointer;
  border: var(--ir-hairline) solid var(--line);
  /* The active rule lives on the inline edge; reserve it on every tab so
   * activating one never shifts the column. */
  border-inline-start: var(--rail) solid transparent;
  border-radius: var(--r-md);
  background: var(--surface-1);
  transition: background-color var(--t-ui) var(--ease),
    border-color var(--t-ui) var(--ease), box-shadow var(--t-ui) var(--ease);
}

.ir-layer:hover {
  border-color: var(--line-strong);
  border-inline-start-color: var(--accent-ring);
}

.ir-layer.is-active {
  border-color: var(--accent-ring);
  border-inline-start-color: var(--accent);
  background: var(--accent-tint);
  box-shadow: var(--e1);
}

.ir-layer:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ir-layer-idx {
  grid-column: 1;
  grid-row: 1 / span 3;
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--track-eyebrow);
  color: var(--accent-hi);
}

.ir-layer-name {
  grid-column: 2;
  font-size: var(--fs-lead);
  font-weight: 600;
  line-height: var(--lh-heading);
}

.ir-layer-when {
  grid-column: 2;
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--muted);
}

.ir-layer-desc {
  grid-column: 2;
  font-size: var(--fs-compact);
  line-height: var(--lh-body);
  color: var(--ink-2);
}

/* ── Roadmap: a horizontal, scroll-snapping track ──────────────────────────
 * Milestones are cards on one row; the reader scrolls sideways (wheel, touch,
 * arrow keys on the focused track, or the two buttons). A rail runs above the
 * cards, drawn segment by segment as each card enters the viewport. Every
 * card is fully visible without JavaScript and under reduced motion: the
 * reveal only applies once the script has tagged the wrapper `.is-js`.
 */
.ir-roadmap {
  position: relative;
  /* Card width: a touch over the 3-up grid floor on desktop; most of a
   * handheld screen so the next card peeks in as a scroll affordance. */
  --ir-card-w: min(calc(var(--grid-min-3) * 1.15), 80vw);
  /* Vertical room above the cards for the rail and its dots. */
  --ir-rail-gap: var(--s6);
}

.ir-roadmap-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  margin-bottom: var(--s3);
}

.ir-roadmap-nav {
  display: flex;
  gap: var(--s2);
}

.ir-roadmap-btn {
  width: calc(var(--icon-sz) * 2.2);
  height: calc(var(--icon-sz) * 2.2);
  padding: 0;
  border-radius: 50%;
}

.ir-roadmap-btn[aria-disabled="true"] {
  opacity: 0.4;
  pointer-events: none;
}

.ir-timeline {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--ir-card-w);
  gap: var(--s4);
  padding: var(--ir-rail-gap) var(--s1) var(--s3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--s1);
  scrollbar-width: none;
  border-radius: var(--r-md);
}

.ir-timeline::-webkit-scrollbar {
  display: none;
}

.ir-timeline:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ir-milestone {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  box-shadow: var(--e1);
  scroll-snap-align: start;
  transition: border-color var(--t-ui) var(--ease),
    box-shadow var(--t-ui) var(--ease);
}

.ir-milestone.is-active {
  border-color: var(--accent);
  box-shadow: var(--e2);
}

/* Rail segment: from the previous card's right edge (across the gap) to this
 * card's right edge, centred in the space above the cards. */
.ir-milestone::after {
  content: "";
  position: absolute;
  left: calc(-1 * var(--s4));
  right: 0;
  top: calc(-1 * var(--ir-rail-gap) / 2 - var(--rail) / 2);
  height: var(--rail);
  background: var(--line-strong);
  transform-origin: left center;
}

.ir-milestone:first-child::after {
  left: 0;
}

/* The dot sits on the rail, aligned with the card's left edge. */
.ir-milestone::before {
  content: "";
  position: absolute;
  z-index: 1;
  left: 0;
  top: calc(-1 * var(--ir-rail-gap) / 2 - var(--s3) / 2);
  width: var(--s3);
  height: var(--s3);
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent-tint);
}

.ir-milestone.is-now::before {
  background: var(--accent-hi);
}

.ir-milestone h3 {
  margin: 0;
}

.ir-milestone-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
}

.ir-when {
  margin: 0;
  font-family: var(--font-mono);
  color: var(--accent-hi);
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.ir-roadmap-progress {
  height: var(--rail);
  margin-top: var(--s3);
  background: var(--line);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.ir-roadmap-progress > span {
  display: block;
  height: 100%;
  width: 100%;
  background: var(--brand-grad);
  transform: scaleX(var(--ir-progress, 0));
  transform-origin: left center;
  transition: transform var(--t-micro) linear;
}

/* ── Motion, gated on the user's preference and on the script ──────────── */
@media (prefers-reduced-motion: no-preference) {
  .ir-roadmap.is-js .ir-milestone {
    opacity: 0;
    transform: translateY(var(--s3));
    transition: opacity var(--t-scene) var(--ease-enter),
      transform var(--t-scene) var(--ease-enter),
      border-color var(--t-ui) var(--ease),
      box-shadow var(--t-ui) var(--ease);
  }

  .ir-roadmap.is-js .ir-milestone::after {
    transform: scaleX(0);
    transition: transform calc(var(--t-scene) * 1.5) var(--ease);
  }

  .ir-roadmap.is-js .ir-milestone.is-seen {
    opacity: 1;
    transform: translateY(0);
  }

  .ir-roadmap.is-js .ir-milestone.is-seen::after {
    transform: scaleX(1);
  }

  .ir-milestone.is-now::before {
    animation: ir-pulse calc(var(--t-scene) * 5) var(--ease) infinite;
  }

  @keyframes ir-pulse {
    0% {
      box-shadow: 0 0 0 0 var(--accent-ring);
    }
    70% {
      box-shadow: 0 0 0 var(--s3) transparent;
    }
    100% {
      box-shadow: 0 0 0 0 transparent;
    }
  }
}

/* ── Funding rounds ────────────────────────────────────────────────────── */
.ir-rounds {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--grid-min-3), 1fr));
  gap: var(--s4);
}

.ir-round-amount {
  font-size: var(--fs-h2);
  font-weight: 700;
  line-height: var(--lh-heading);
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ── Compute partner programs ──────────────────────────────────────────── */
.ir-programs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--ir-program-min), 1fr));
  gap: var(--s4);
  --ir-program-min: calc(var(--grid-min-3) / 1.2);
}

/* The amount carries a gradient hairline instead of a divider element. */
.ir-program-amount {
  position: relative;
  padding-block-end: var(--s3);
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--accent-hi);
}

.ir-program-amount::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: var(--ir-hairline);
  border-radius: var(--r-pill);
  background: var(--brand-grad);
  opacity: 0.6;
}

.ir-program-req {
  font-size: var(--fs-caption);
  color: var(--muted);
}

/* ── Round targets, as a chart ────────────────────────────────────────────
 * Three bars sized by --ir-bar (the round's share of the largest target,
 * written inline by the HTML). Without the script the bars are simply drawn
 * at full value; with it they grow from the baseline, staggered, once the
 * chart is on screen.
 */
.ir-chart {
  display: flex;
  align-items: flex-end;
  gap: var(--s5);
  padding: var(--s5);
  border: var(--ir-hairline) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-2);
}

.ir-bar {
  display: grid;
  grid-template-rows: auto var(--ir-chart-h) auto;
  gap: var(--s2);
  flex: 1 1 0;
  justify-items: center;
  text-align: center;
}

/* The empty track behind the fill: same cell, painted first. The cap keeps a
 * bar reading as a bar — a third of the plate wide, it reads as a panel. */
.ir-bar::before {
  content: "";
  grid-column: 1;
  grid-row: 2;
  width: 100%;
  max-width: calc(var(--kpi-h) * 2);
  height: 100%;
  border-radius: var(--r-sm);
  background: var(--surface-3);
}

.ir-bar-fill {
  grid-column: 1;
  grid-row: 2;
  width: 100%;
  max-width: calc(var(--kpi-h) * 2);
  height: 100%;
  border-radius: var(--r-sm);
  background: var(--brand-grad);
  transform: scaleY(var(--ir-bar, 1));
  transform-origin: 50% 100%;
  transition: transform calc(var(--t-scene) * 2) var(--ease-enter);
}

:where(body.is-js) .ir-chart:not(.is-visible) .ir-bar-fill {
  transform: scaleY(0);
}

.ir-bar:nth-child(2) .ir-bar-fill {
  transition-delay: var(--t-ui);
}

.ir-bar:nth-child(3) .ir-bar-fill {
  transition-delay: calc(var(--t-ui) * 2);
}

.ir-bar-value {
  grid-column: 1;
  grid-row: 1;
  font-size: var(--fs-h3);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  font-variant-numeric: tabular-nums;
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.ir-bar-label {
  grid-column: 1;
  grid-row: 3;
  font-size: var(--fs-caption);
  color: var(--muted);
}

/* ── Investor cadence ──────────────────────────────────────────────────── */
.ir-cadence {
  border-color: var(--accent-ring);
  background: linear-gradient(160deg, var(--accent-tint) 0%, transparent 60%),
    var(--surface-1);
}

.ir-cadence ul {
  display: grid;
  gap: var(--s2);
  margin: 0;
  padding-inline-start: var(--s5);
  color: var(--ink-2);
}

.ir-cadence li::marker {
  color: var(--accent-hi);
}

/* ── Stacked total ─────────────────────────────────────────────────────── */
.ir-total {
  border-color: var(--accent-ring);
  background: linear-gradient(180deg, var(--accent-tint) 0%, transparent 70%),
    var(--surface-1);
}

.ir-total .stat-value {
  font-size: var(--fs-h1);
  font-weight: 700;
  letter-spacing: var(--track-display);
  font-variant-numeric: tabular-nums;
  background: var(--brand-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ── Closing callout ───────────────────────────────────────────────────── */
.ir-cta {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.ir-cta::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: 0;
  inset-inline-start: 50%;
  width: var(--glow-blur);
  height: var(--glow-blur);
  transform: translate(-50%, -50%);
  border-radius: var(--r-pill);
  background: var(--brand-grad);
  filter: blur(var(--glow-blur));
  opacity: 0.35;
  pointer-events: none;
}

/* ── Reduced motion ───────────────────────────────────────────────────────
 * Everything that moves is switched off: the reveal, the tilt, the pointer
 * glow, the growing bars. The STATIC 3D stays — the hero's floor and the
 * platform slabs are geometry, not animation, and removing their transforms
 * would leave a flat pile of rectangles where the fallback is supposed to
 * look finished.
 */
@media (prefers-reduced-motion: reduce) {
  :where(body.is-js) [data-ir-reveal],
  :where(body.is-js) [data-ir-reveal].is-visible {
    --ir-reveal-y: 0;
    opacity: 1;
    transform: none;
    transition: none;
  }

  .ir-page .ir-tilt {
    transform: none;
    transition: none;
  }

  .ir-tilt-glow {
    display: none;
  }

  .ir-scene-canvas,
  .ir-scene-poster,
  .ir-scene-stack::before,
  .ir-scene-stack::after,
  .ir-scene-stack .ir-scene-poster::before,
  .ir-scene-stack .ir-scene-poster::after,
  .ir-layer {
    transition: none;
  }

  .ir-bar-fill,
  :where(body.is-js) .ir-chart:not(.is-visible) .ir-bar-fill {
    transform: scaleY(var(--ir-bar, 1));
    transition: none;
  }
}

/* ── Breakpoints, at the widths 09-breakpoints-and-motion.css already uses ─ */
@media (max-width: 1100px) {
  /* Same width at which the site's other split layouts stack: below it the
   * scene and a --grid-min-3 tab column cannot both hold their proportions. */
  .ir-platform-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 768px) {
  .ir-hero {
    min-height: auto;
  }

  .ir-scene-stack {
    aspect-ratio: 1 / 1;
  }

  .ir-chart {
    --ir-chart-h: calc(var(--kpi-h) * 2);
    gap: var(--s3);
    padding: var(--s4);
  }

  .ir-bar-value {
    font-size: var(--fs-lead);
  }

  .ir-layer {
    padding: var(--s3);
  }
}

