@layer reset, tokens, base, utilities, components, page;

/* ============================================================
   DESIGN TOKENS — black studio / neon ink
   Colours are declared in oklch() (source of truth — Prompt 3/4
   read these) with an sRGB fallback for browsers predating
   oklch() support. Same hues as the original hex literals below;
   not re-picked — see the fallback block for the exact hex.
   ============================================================ */
@layer tokens{
:root{
  /* Near-black ground, warm-white ink, three neons at FULL saturation —
     no white dilution, they should read like lit signage. Lime is the
     brand accent; cyan and magenta only appear in assigned roles (indices,
     sparks, monograms) so the colour reads deliberate, not random. */
  --bg0: oklch(14.5% 0.002 286.1);
  --bg1: oklch(18.4% 0.008 285.6);
  --bg2: oklch(22.0% 0.012 285.4);
  --fg:   oklch(95.9% 0.008 106.5);
  --fg-2: oklch(73.4% 0.011 286.1);
  --fg-3: oklch(60.0% 0.017 285.9);   /* ≈5.0:1 on bg0 / ≈4.7:1 on bg1 — clears WCAG AA for the small text it colours */

  --lime:    oklch(93.1% 0.229 123.1);
  --cyan:    oklch(87.0% 0.148 202.9);
  --magenta: oklch(68.6% 0.293 337.2);
  --on-lime: oklch(17.5% 0.029 115.5);  /* ink used on lime surfaces */
  --ok:  oklch(81.0% 0.214 151.8);
  --err: oklch(67.9% 0.209 24.7);
  --sun: oklch(81.2% 0.148 81.0);      /* demo-internal highlight — client-site world only */

  --line:   rgba(242,242,236,.10);
  --line-2: rgba(242,242,236,.22);

  /* --color-* semantic aliases (required token family). The primitives
     above already ARE this page's colour roles under their own names
     (bg0/bg1/bg2/fg/lime/…), used in ~900 places throughout this
     stylesheet — aliased here rather than mechanically renamed, so the
     semantic --color-* vocabulary exists for Prompt 3/4 without a
     high-risk find/replace across the whole file. */
  --color-surface: var(--bg0);
  --color-surface-2: var(--bg1);
  --color-surface-3: var(--bg2);
  --color-on-surface: var(--fg);
  --color-on-surface-muted: var(--fg-2);
  --color-on-surface-subtle: var(--fg-3);
  --color-primary: var(--lime);
  --color-on-primary: var(--on-lime);
  --color-accent-cyan: var(--cyan);
  --color-accent-magenta: var(--magenta);
  --color-success: var(--ok);
  --color-danger: var(--err);
  --color-highlight: var(--sun);
  --color-border: var(--line);
  --color-border-strong: var(--line-2);

  /* Type */
  --font-display:'Archivo', ui-sans-serif, system-ui, sans-serif;
  --font-serif:'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
  --font-mono:'Spline Sans Mono', ui-monospace, Menlo, monospace;

  /* Radius */
  --radius-sm:8px; --radius-md:12px; --radius-lg:20px; --radius-xl:28px;
  --radius-pill:999px; --radius-full:9999px;

  /* Space — 4px step */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px; --space-5:24px;
  --space-6:32px; --space-7:48px; --space-8:64px; --space-9:96px; --space-10:128px;

  /* Shadow — deep neutral blacks; lift comes from borders + surface steps */
  --shadow-sm:0 1px 2px rgba(0,0,0,.5);
  --shadow-md:0 16px 40px -16px rgba(0,0,0,.65);
  --shadow-lg:0 40px 90px -30px rgba(0,0,0,.8);

  /* Motion — the page's own tuned vocabulary (used ~40+ places below),
     kept verbatim, PLUS the prompt's mandatory-minimum named vocabulary
     alongside it (new infrastructure for Prompt 3/4 and for tokens
     introduced this pass — rings, shimmer). Both co-exist deliberately;
     see the CHANGELOG "Tokenisation" note. */
  --ease-out:cubic-bezier(.2,.7,.2,1);
  --ease-in-out:cubic-bezier(.65,.05,.36,1);
  --dur-1:140ms; --dur-2:260ms; --dur-3:550ms;

  --ease-out-quad:   cubic-bezier(0.5, 1, 0.89, 1);
  --ease-out-expo:   cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring:     cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
  --duration-fast: 120ms;
  --duration-base: 240ms;
  --duration-slow: 480ms;

  /* Z-index — page-level roles only (a handful of small, purely
     LOCAL z-indices elsewhere — e.g. the compare-slider's internal
     layers, the hero pins/orbit — stay as literals: they order
     siblings inside their own component's stacking context and have
     no relationship to this global scale; forcing them onto it would
     be organisational noise, not a real fix). --z-chrome/--z-skip
     extend the mandatory minimum upward for the two fixed, page-wide
     layers (film-grain overlay, skip-link) that must win over
     everything, including the mandatory scale's own --z-tooltip. */
  --z-base:     0;
  --z-dropdown: 100;
  --z-sticky:   200;
  --z-overlay:  300;
  --z-modal:    400;
  --z-toast:    500;
  --z-tooltip:  600;
  --z-chrome:   2000;
  --z-skip:     2100;

  /* Blur */
  --blur-sm: 6px;
  --blur-md: 12px;
  --blur-lg: 24px;

  /* Gradients — built from the page's own original literal values
     (never re-picked); --gradient-shimmer is new infrastructure for
     any future async/skeleton state (this page's regions render their
     final markup directly, so none currently need it, but the rule
     ships per the prompt's mandatory pattern). */
  --gradient-hero: radial-gradient(58% 46% at 68% 0%, rgba(204,255,0,.06), transparent 68%);
  --gradient-shimmer: linear-gradient(90deg, var(--bg1) 0%, color-mix(in oklch, var(--bg1) 60%, white) 50%, var(--bg1) 100%);
  --gradient-aura-lime:    radial-gradient(56% 300px at 50% 0%, rgba(204,255,0,.04), transparent 70%);
  --gradient-aura-cyan:    radial-gradient(56% 300px at 50% 0%, rgba(0,240,255,.05), transparent 70%);
  --gradient-aura-magenta: radial-gradient(56% 300px at 50% 0%, rgba(255,26,217,.05), transparent 70%);
  --gradient-sheen-strong: linear-gradient(105deg, transparent 42%, rgba(255,255,255,.28) 50%, transparent 58%);
  --gradient-sheen-soft:   linear-gradient(105deg, transparent 43%, rgba(255,255,255,.18) 50%, transparent 57%);

  /* Focus rings — colour kept fully opaque (not the spec example's
     50%-transparent color-mix) to match this page's own already-
     audited high-contrast focus treatment (2026-07-04 a11y pass). */
  --ring-color: var(--lime);
  --ring-color-inverse: var(--on-lime); /* the two lime surfaces need the dark ink, not lime-on-lime */
  --ring-offset: 3px;
  --ring-width: 2px;

  --measure:64ch;
  --maxw:1200px;
  --gutter:clamp(20px, 5vw, 56px);

  color-scheme: dark;
}

/* This register is dark-native by construction (color-scheme:dark; true
   black surfaces are the signature, not an incomplete light mode) — see
   the CHANGELOG "Tokenisation" note for why a full alternate light
   palette is deliberately NOT introduced here. The override block below
   is declared (mirroring :root) so [data-theme="dark"] is real,
   reachable infrastructure for a future theme toggle. */
[data-theme="dark"]{
  --bg0: oklch(14.5% 0.002 286.1); --bg1: oklch(18.4% 0.008 285.6); --bg2: oklch(22.0% 0.012 285.4);
  --fg: oklch(95.9% 0.008 106.5); --fg-2: oklch(73.4% 0.011 286.1); --fg-3: oklch(60.0% 0.017 285.9);
  --lime: oklch(93.1% 0.229 123.1); --cyan: oklch(87.0% 0.148 202.9); --magenta: oklch(68.6% 0.293 337.2);
  --on-lime: oklch(17.5% 0.029 115.5); --ok: oklch(81.0% 0.214 151.8); --err: oklch(67.9% 0.209 24.7);
  --sun: oklch(81.2% 0.148 81.0);
}

/* sRGB fallback for browsers without oklch() support (pre Safari 15.4 /
   Chrome 111 / Firefox 113) — the ORIGINAL hex literals, verbatim. */
@supports not (color: oklch(0 0 0)){
  :root{
    --bg0:#0a0a0b; --bg1:#121216; --bg2:#1a1a20;
    --fg:#f2f2ec; --fg-2:#a8a8b0; --fg-3:#7f7f8a;
    --lime:#ccff00; --cyan:#00f0ff; --magenta:#ff1ad9;
    --on-lime:#101204; --ok:#00e676; --err:#ff5252; --sun:#f2b63c;
  }
  [data-theme="dark"]{
    --bg0:#0a0a0b; --bg1:#121216; --bg2:#1a1a20;
    --fg:#f2f2ec; --fg-2:#a8a8b0; --fg-3:#7f7f8a;
    --lime:#ccff00; --cyan:#00f0ff; --magenta:#ff1ad9;
    --on-lime:#101204; --ok:#00e676; --err:#ff5252; --sun:#f2b63c;
  }
}
} /* /@layer tokens */

/* ============================================================
   RESET
   ============================================================ */
@layer reset{
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0}
}

/* ============================================================
   BASE — global element defaults, skeleton-shimmer infrastructure,
   and every site-wide @keyframes definition (KEYFRAME PLACEMENT:
   ALL @keyframes live here regardless of which constituent uses
   them, so component/page CSS only ever references animation names).
   ============================================================ */
@layer base{
html{scroll-behavior:smooth; scroll-padding-top:96px; background:var(--bg0)}
body{
  margin:0;
  font-family:var(--font-body);
  background:var(--bg0);
  color:var(--fg);
  font-size:16px; line-height:1.65;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;
}
/* Film grain — white noise at 6%, so the black never reads like a dead #000 fill */
body::after{
  content:""; position:fixed; inset:0; z-index:var(--z-chrome); pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.06 0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
h1,h2,h3,h4{
  font-family:var(--font-display); font-stretch:106%;
  line-height:1.05; letter-spacing:-0.024em; margin:0; font-weight:760;
  text-wrap:balance;
}
p{margin:0; text-wrap:pretty}
a{color:inherit; text-decoration:none}
img,svg{display:block; max-width:100%}
button{font-family:inherit; cursor:pointer; border:none; background:none; color:inherit}
ul{margin:0; padding:0; list-style:none}
::selection{background:var(--lime); color:var(--on-lime)}
:focus-visible{outline:var(--ring-width) solid var(--ring-color); outline-offset:var(--ring-offset); border-radius:4px}
/* on the two lime surfaces a lime ring is invisible — swap it for the dark ink */
.plan.featured :focus-visible,
.cta-show :focus-visible{outline-color:var(--ring-color-inverse)}

/* Skeleton shimmer — mandatory infrastructure for any island that ever
   renders a generic loading placeholder. This page has zero islands, so
   nothing currently uses it, but the rule ships as site-wide
   infrastructure per the prompt's mandatory pattern. */
@keyframes shimmer{ from{background-position:200% 0} to{background-position:-200% 0} }
.is-skeleton,
[class$="-skeleton"] > *{
  background-image:var(--gradient-shimmer);
  background-size:200% 100%;
  background-repeat:no-repeat;
  animation:shimmer var(--duration-slow) linear infinite;
  border-radius:var(--radius-sm);
  color:transparent;
}

/* Hero swash-in underline (hero.html) */
@keyframes swash-in{to{background-size:100% .16em}}
/* Hero drafting-orbit viz rings/ping/twinkle (hero.html) */
@keyframes viz-spin{to{transform:rotate(360deg)}}
@keyframes viz-ping{
  0%{transform:scale(.42); opacity:.5}
  70%{opacity:0}
  100%{transform:scale(2); opacity:0}
}
@keyframes viz-twinkle{
  0%,100%{opacity:.12; transform:scale(.8) rotate(0deg)}
  50%{opacity:.9; transform:scale(1.15) rotate(45deg)}
}
/* Hero signature sticker rotation (hero.html) */
@keyframes orbit-spin{to{transform:rotate(360deg)}}
/* Proof marquee scroll (components.css: proof-bar) */
@keyframes proof-marquee{from{transform:translateX(0)} to{transform:translateX(-50%)}}

/* Ambient-tier keyframes (item 9 idle loops) — each runs on an element
   carrying .is-ambient, paused offscreen by .is-ambient.is-paused below. */
@keyframes amb-spin{to{transform:rotate(360deg)}}
@keyframes amb-caret{0%,54%{opacity:.85} 55%,100%{opacity:.12}}
@keyframes amb-scan{0%{transform:translateX(-280px)} 58%,100%{transform:translateX(calc(100vw + 40px))}}
@keyframes amb-ink{0%,100%{-webkit-text-stroke-color:var(--line-2)} 50%{-webkit-text-stroke-color:var(--ink,var(--line-2))}}
@keyframes amb-ping{0%{transform:scale(.55); opacity:0} 8%{opacity:.5} 26%,100%{transform:scale(1.8); opacity:0}}
@keyframes amb-drift{0%,100%{translate:0 0} 50%{translate:0 -5px}}
@keyframes amb-bob{0%,100%{transform:rotate(-2.6deg) translateY(0)} 50%{transform:rotate(-1.1deg) translateY(-4px)}}
@keyframes amb-star{0%,100%{opacity:.65; transform:scale(.94)} 50%{opacity:1; transform:scale(1.12)}}
@keyframes amb-sheen{0%{background-position:200% 0} 24%,100%{background-position:-80% 0}}
@keyframes amb-pulse{0%,100%{transform:scale(1)} 50%{transform:scale(1.08)}}
@keyframes amb-nudge{0%,58%,86%,100%{translate:0 0} 72%{translate:4px 0}}
.is-ambient.is-paused{
  animation-play-state: paused;
}
}

/* ============================================================
   scroll-reveal infrastructure (animation-timeline path is
   progressive enhancement — the IntersectionObserver in
   00_base.html drives the fallback for older browsers).
   ALL pre-reveal hiding is scoped to `html.js` — the inline
   <script> in 00_base.html adds that class, so with JS disabled
   nothing is hidden and the page reads in full. Mandatory
   site-wide infrastructure (see CHANGELOG "data-reveal note"):
   this page's own reveal system is .reveal/.reveal.in/[data-delay]
   (see components.css / <NN_page>.css + site-interactions.js);
   no block on this page carries [data-reveal], so these rules are
   currently unused but ship per the prompt's own "always emit"
   instruction.
   ============================================================ */
@layer base{
html.js [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--duration-slow, 600ms) var(--ease-out-expo, ease-out),
    transform var(--duration-slow, 600ms) var(--ease-out-expo, ease-out),
    filter var(--duration-slow, 600ms) var(--ease-out-expo, ease-out);
  will-change: opacity, transform, filter;
  /* honour stagger index when nested inside a stagger parent */
  transition-delay: calc(var(--reveal-index, 0) * 80ms);
}

html.js [data-reveal],
html.js [data-reveal="fade-up"] {
  transform: translateY(16px);
}
html.js [data-reveal="fade"] {
  transform: none;
}
html.js [data-reveal="scale-in"] {
  transform: scale(0.96);
  transform-origin: center;
}
html.js [data-reveal="slide-right"] {
  transform: translateX(-24px);
}
html.js [data-reveal="blur-in"] {
  transform: translateY(8px);
  filter: blur(8px);
}

html.js [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  filter: none;
}

[data-reveal-stagger] > [data-reveal] {
  transition-delay: calc(var(--reveal-index, 0) * 80ms);
}

@supports (animation-timeline: view()) {
  [data-reveal][data-reveal-repeat]:not(.is-revealed) {
    animation: reveal-from-view linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }
  @keyframes reveal-from-view {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}
}

/* ============================================================
   UTILITIES — a11y helpers + layout primitives used site-wide
   ============================================================ */
@layer utilities{
.skip{position:absolute; left:-999px; top:8px; background:var(--lime); color:var(--on-lime);
  padding:10px 16px; border-radius:8px; z-index:var(--z-skip); font-weight:600}
.skip:focus{left:12px}
/* real screen-reader-only utility (was: abusing .skip for the newsletter label) */
.sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0}

.wrap{max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gutter)}
.section{padding-block:var(--space-9); border-top:1px solid var(--line)}
/* ambient section auras — one whisper of each section's index neon bleeding
   down from its top hairline, so the page carries colour between the inks */
#how{background:var(--gradient-aura-lime)}
#included{background:var(--gradient-aura-cyan)}
#features{background:var(--gradient-aura-magenta)}
#work{background:var(--gradient-aura-lime)}
#pricing{background:var(--gradient-aura-cyan)}
#testimonials{background:var(--gradient-aura-magenta)}
#faq{background:var(--gradient-aura-lime)}
.eyebrow{--sx:var(--lime); font-family:var(--font-mono); font-size:.72rem; letter-spacing:.18em; text-transform:uppercase;
  color:var(--fg-3); display:inline-flex; align-items:center; gap:.9em; font-weight:600}
.eyebrow i{font-style:normal; color:var(--sx); text-shadow:0 0 12px}  /* colourless shadow = currentColor, so each section's neon glows its own hue */
/* every section index ticks like a terminal caret, in its section's neon —
   ambient trigger for the .eyebrow utility above (amb-caret defined in
   @layer base); co-located here since it is part of the same utility class */
.eyebrow i::after{content:""; display:inline-block; width:.5em; height:.95em;
  margin-left:.45em; vertical-align:-.12em; background:currentColor;
  animation:amb-caret 1.25s linear infinite}
/* section indices cycle the three neons: 01 lime · 02 cyan · 03 magenta · …
   (--sx also tints the eyebrow's leading dash) */
#included .eyebrow{--sx:var(--cyan)}
#features .eyebrow{--sx:var(--magenta)}
#pricing .eyebrow{--sx:var(--cyan)}
#testimonials .eyebrow{--sx:var(--magenta)}
.eyebrow::before{content:""; width:26px; height:1px;
  background:color-mix(in srgb, var(--sx) 60%, transparent)}
.section-head{max-width:62ch}
.section-head h2{font-size:clamp(2.1rem, 4.2vw, 3.3rem); margin-top:var(--space-5)}
.section-head p{color:var(--fg-2); font-size:1.12rem; margin-top:var(--space-4); max-width:56ch}
.lede{color:var(--fg-2)}
} /* /@layer utilities */

/* ============================================================
   REDUCED MOTION — top-level, OUTSIDE any layer, so its
   !important rules win unconditionally regardless of layer order.
   Preserves opacity/colour transitions (reduces cognitive load)
   while killing every spatial-motion property; the belt-and-braces
   block additionally resets transform/translate/scale/rotate to
   none on anything still carrying a reveal/animation/ambient class
   (pseudo-element ambient loops — ::after/::before sheens, carets,
   pings — are already caught by the universal *,*::before,*::after
   rule above, since generated content can't carry a class of its own).
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, background-color, color, border-color,
                         box-shadow, fill, stroke !important;
    transition-duration: var(--duration-fast, 120ms) !important;
    scroll-behavior: auto !important;
  }
  [data-reveal],
  .is-animated,
  .is-revealed,
  .is-ambient,
  .reveal {
    transform: none !important;
    translate: none !important;
    scale: none !important;
    rotate: none !important;
  }
  .reveal{opacity:1}
  .hero h1 .accent{background-size:100% .16em}
  .eyebrow i::after{display:none}  /* a frozen caret would read as a stray block */
}

