/* ============================================================================
   Wave 1 API landing system — implements 28_API_COLOR_TYPOGRAPHY_TRUST_SYSTEM
   and 29_API_LANDING_PAGE_RESPONSIVE_QA.

   Per-product hue is injected by overriding the --brand-* tokens only. Every
   other token, and all layout, is shared so the three sites read as one
   disciplined organisation (22_FRONTEND §Core visual principles).

   Neutral foundation is slate with a slight blue bias — chosen, not inherited.
   ========================================================================== */

:root {
  /* Spacing scale — fixed: 4 8 12 16 24 32 48 64 96 */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  /* Type scale — fixed: 12 14 16 18 20 24 32 40-48 */
  --t-xs: 12px; --t-sm: 14px; --t-base: 16px; --t-md: 18px;
  --t-lg: 20px; --t-xl: 24px; --t-2xl: 32px; --t-3xl: clamp(32px, 5vw, 48px);

  /* Widths */
  --w-app: 1240px;      /* application/API surface: 1100-1400 */
  --w-commerce: 1120px; /* pricing/commercial: 1000-1200 */
  --w-read: 720px;      /* reading column: 680-760 */

  /* Neutral foundation */
  --bg: #FFFFFF;
  --bg-sunken: #F6F8FB;
  --surface: #FFFFFF;
  --border: #DDE3EC;
  --border-strong: #C3CCDA;
  --ink: #0F172A;        /* 16.9:1 on white */
  --ink-2: #3F4C60;      /* 8.6:1  on white */
  --ink-3: #5C6880;      /* 5.8:1  on white — lightest text used */

  /* Semantic — never the sole carrier of meaning (28 §Semantic rules) */
  /* Status hues sit at the MIDPOINTS of the twelve-spoke brand wheel — exactly
     15 degrees from either neighbouring product hue, the most an even wheel
     allows. Status therefore never reads as some product's brand colour. */
  --ok: #137464;         /* 5.7:1 on white */
  --ok-bg: #ECF6F4;
  --warn: #786714;       /* 5.6:1 on white */
  --warn-bg: #F6F4EC;
  --crit: #C8213D;       /* 5.6:1 on white */
  --crit-bg: #F6ECEE;

  --radius: 8px;
  --shadow: 0 1px 2px rgba(15,23,42,.06), 0 1px 3px rgba(15,23,42,.04);
  --focus: var(--brand-600);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--t-base)/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden; /* belt and braces against unintended horizontal scroll */
}
code, kbd, pre, .mono { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }

h1, h2, h3 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; font-weight: 700; text-wrap: balance; }
h1 { font-size: var(--t-3xl); letter-spacing: -0.03em; }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-lg); letter-spacing: -0.01em; }
p  { margin: 0; }
a  { color: var(--brand-700); text-decoration-thickness: 1px; text-underline-offset: 2px; }

:where(a, button, input, textarea, select, summary):focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px;
}

.wrap      { width: 100%; max-width: var(--w-app);      margin-inline: auto; padding-inline: var(--s5); }
.wrap-cm   { width: 100%; max-width: var(--w-commerce); margin-inline: auto; padding-inline: var(--s5); }
.read      { max-width: var(--w-read); }               /* 45-80 char line length */
.stack     { display: flex; flex-direction: column; }
.eyebrow {
  font: 600 var(--t-xs)/1.3 ui-sans-serif, system-ui, sans-serif;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-3);
}
section { padding-block: var(--s8); }
section + section { border-top: 1px solid var(--border); }
.sunken { background: var(--bg-sunken); }

/* ---- header ---- */
header.site {
  position: sticky; top: 0; z-index: 20;
  background: rgba(255,255,255,.92); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
header.site .wrap { display: flex; align-items: center; gap: var(--s5); min-height: 60px; }
.brand { display: flex; align-items: center; gap: var(--s2); white-space: nowrap; font-weight: 700; font-size: var(--t-md); letter-spacing: -.02em; color: var(--ink); text-decoration: none; }
.brand .dot { width: 10px; height: 10px; border-radius: 3px; background: var(--brand-600); flex: none; }
header.site nav { margin-left: auto; display: flex; align-items: center; gap: var(--s5); }
/* :not(.btn) matters. `header.site nav a` scores (0,1,3) and would otherwise
   beat `.btn-primary` at (0,1,0) — which painted the header CTA's label in
   --ink-2 slate on the brand background, making the most important button on
   the page almost unreadable. Found by sampling the rendered pixels. */
header.site nav a:not(.btn) { color: var(--ink-2); text-decoration: none; font-size: var(--t-sm); font-weight: 500; }
header.site nav a:not(.btn):hover { color: var(--ink); text-decoration: underline; }

/* ---- buttons: exactly one primary per decision area (28 §CTA) ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 44px; padding: 0 var(--s5); border-radius: var(--radius);
  font: 600 var(--t-base)/1 ui-sans-serif, system-ui, sans-serif;
  text-decoration: none; cursor: pointer; border: 1px solid transparent; white-space: nowrap;
  transition: background-color .12s ease, border-color .12s ease;
}
.btn-primary { background: var(--brand-700); color: #fff; }
.btn-primary:hover { background: var(--brand-800); }
.btn-primary:active { background: var(--brand-900); }
.btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--border-strong); }
.btn-secondary:hover { border-color: var(--ink-3); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

/* ---- hero ---- */
.hero { padding-block: var(--s8) var(--s7); }
.hero-grid { display: grid; gap: var(--s7); align-items: start; }
.hero h1 { margin-bottom: var(--s4); }
.hero .lede { font-size: var(--t-md); color: var(--ink-2); margin-bottom: var(--s5); }
.cta-row { display: flex; flex-wrap: wrap; gap: var(--s3); }
.hero-meta { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin-top: var(--s5); font-size: var(--t-sm); color: var(--ink-3); }
.hero-meta b { color: var(--ink-2); font-weight: 600; }

/* ---- live proof panel ---- */
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden; }
.panel-head { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); background: var(--bg-sunken); }
.panel-head .label { font: 600 var(--t-xs)/1 ui-sans-serif, system-ui, sans-serif; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
.panel-body { padding: var(--s4); }
label.field { display: block; font: 600 var(--t-sm)/1.4 ui-sans-serif, system-ui, sans-serif; color: var(--ink-2); margin-bottom: var(--s2); }
textarea, input[type=text] {
  width: 100%; font: var(--t-sm)/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--border-strong); border-radius: 6px; padding: var(--s3);
  resize: vertical;
}
pre.out {
  margin: 0; padding: var(--s4); background: #0F172A; color: #E6EAF2;
  border-radius: 6px; font-size: var(--t-sm); line-height: 1.6;
  overflow-x: auto; /* code scrolls INSIDE its container (29 §Mobile) */
  max-height: 420px; overflow-y: auto;
}
pre.out .k { color: #93C5FD; } pre.out .s { color: #86EFAC; } pre.out .n { color: #FDBA74; }
.hint { font-size: var(--t-sm); color: var(--ink-3); margin-top: var(--s3); }

/* status chips — icon + text, never colour alone */
.chip { display: inline-flex; align-items: center; align-self: start; gap: 6px; padding: 3px 9px; border-radius: 999px;
        font: 600 var(--t-xs)/1.5 ui-sans-serif, system-ui, sans-serif; border: 1px solid; }
.chip-ok   { color: var(--ok);   background: var(--ok-bg);   border-color: var(--ok); }
.chip-warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); }
.chip-crit { color: var(--crit); background: var(--crit-bg); border-color: var(--crit); }
.chip-info { color: var(--brand-800); background: var(--brand-50); border-color: var(--brand-300); }

/* ---- generic cards ---- */
.grid { display: grid; gap: var(--s4); }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--s5); }
.card h3 { margin-bottom: var(--s2); }
.card p { color: var(--ink-2); font-size: var(--t-sm); }
.steps { counter-reset: step; display: grid; gap: var(--s4); }
.steps li { counter-increment: step; list-style: none; display: grid; grid-template-columns: 32px 1fr; gap: var(--s4); align-items: start; }
.steps li::before {
  content: counter(step); display: grid; place-items: center; align-self: start;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--brand-50); color: var(--brand-800);
  border: 1px solid var(--brand-300);
  font: 600 var(--t-sm)/1 ui-sans-serif, system-ui, sans-serif;
}
.steps ul { margin: 0; padding: 0; }

/* ---- endpoints ---- */
.ep { display: grid; grid-template-columns: 64px 1fr; gap: var(--s3); align-items: baseline;
      padding: var(--s3) 0; border-bottom: 1px solid var(--border); }
.ep:last-child { border-bottom: 0; }
.ep .m { font: 600 var(--t-xs)/1.4 ui-monospace, monospace; color: var(--brand-800); background: var(--brand-50);
         border: 1px solid var(--brand-300); border-radius: 4px; padding: 2px 6px; text-align: center; }
.ep code { font-size: var(--t-sm); color: var(--ink); word-break: break-all; }
.ep .d { grid-column: 2; font-size: var(--t-sm); color: var(--ink-3); }

/* ---- pricing ---- */
.tiers { display: grid; gap: var(--s4); }
.tier { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
        padding: var(--s5); display: flex; flex-direction: column; gap: var(--s3); }
.tier.featured { border-color: var(--brand-600); box-shadow: 0 0 0 1px var(--brand-600); }
.tier .name { font-weight: 700; font-size: var(--t-md); }
.tier .price { font-size: var(--t-2xl); font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.tier .price span { font-size: var(--t-sm); font-weight: 500; color: var(--ink-3); letter-spacing: 0; }
.tier ul { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--s2); font-size: var(--t-sm); color: var(--ink-2); }
/* NOT a grid. `<li><strong>250</strong> monitored entities included</li>` is
   three grid items — the ::before marker, the <strong>, and the trailing text
   node — so with two tracks the text wrapped into the 18px marker column and
   rendered one character per line. A positioned marker keeps the whole li a
   single inline flow, however many elements the copy happens to contain. */
.tier ul li { position: relative; padding-left: 26px; }
.tier ul li::before { content: "✓"; position: absolute; left: 0; top: 0;
                      color: var(--ok); font-weight: 700; }
.tier .btn { margin-top: auto; }
.tier-note { font-size: var(--t-xs); color: var(--ink-3); }

/* ---- faq ---- */
details.faq { border-bottom: 1px solid var(--border); }
details.faq summary { cursor: pointer; padding: var(--s4) 0; font-weight: 600; font-size: var(--t-md); list-style: none; display: flex; gap: var(--s3); align-items: center; }
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after { content: "+"; margin-left: auto; color: var(--ink-3); font-weight: 400; font-size: var(--t-lg); }
details.faq[open] summary::after { content: "−"; }
details.faq .body { padding-bottom: var(--s4); color: var(--ink-2); }

footer.site { background: var(--bg-sunken); border-top: 1px solid var(--border); padding-block: var(--s7); }
footer.site .cols { display: grid; gap: var(--s5); }
footer.site a { color: var(--ink-2); text-decoration: none; font-size: var(--t-sm); display: block; padding: 3px 0; }
footer.site a:hover { color: var(--ink); text-decoration: underline; }
footer.site .legal { margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--border); font-size: var(--t-xs); color: var(--ink-3); }

/* ---- responsive: 320 / 375 / 390 / 430 → 1280 / 1440 / 1920 ---- */
@media (min-width: 640px) { .grid-2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s7); align-items: center; }
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .tiers  { grid-template-columns: repeat(2, 1fr); }
  footer.site .cols { grid-template-columns: 2fr 1fr 1fr 1fr; }
}
@media (min-width: 1120px) { .tiers { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 899px) {
  header.site nav a.nav-hide { display: none; } /* collapse cleanly, keep the CTA reachable */
  section { padding-block: var(--s7); }
}
@media (max-width: 380px) { :root { --s5: 16px; } .btn { padding-inline: var(--s4); } }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }


/* ---- stretch, wrap and overflow (29 §Mobile) ------------------------------
   A grid or flex child stretches to its track unless told otherwise. With
   border-radius:999px that turns a small pill into a tall ellipse — which is
   exactly what the eyebrow tags and step markers were rendering as. Anything
   pill-shaped that sits in a track gets align-self:start.

   Wide content is the other half: tables and code will not reflow, so they get
   their own scroll container and the PAGE never scrolls sideways. Verified with
   a headless probe that reports every element wider than the viewport at
   320/375/390/430. */
.cost .tag, .chain .step, .badges .chip, .tier .chip { align-self: start; }

.scroller, .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.scroller > table, .table-scroll > table { min-width: 100%; border-collapse: collapse; }

/* A long unbroken token — a URL, a hash, an sku — must not set the page width.
   `break-word`, NOT `anywhere`: `anywhere` also shrinks the box's min-content
   width, so inside a `1fr` grid column it collapses the track to one character
   and breaks ordinary prose down the page a letter at a time. `break-word`
   breaks a long token only when it would actually overflow, and leaves
   intrinsic sizing alone. */
p, li, td, dd, .desc, .hint, .tier-note, code { overflow-wrap: break-word; }

@media (max-width: 420px) {
  /* The header must still fit on one line at the narrowest widths: shrink the
     gaps and the CTA rather than letting either wrap and clip. */
  header.site .wrap { gap: var(--s3); padding-inline: var(--s4); }
  header.site nav   { gap: var(--s3); }
  .brand            { font-size: var(--t-base); }
  header.site nav .btn { padding-inline: var(--s3); font-size: 13px; }
}

@media (max-width: 400px) {
  /* Below 400 the brand, a text link and the CTA no longer fit together and the
     button pushed 26px past the edge at 320. The CTA is the reason the header
     exists, so the remaining text links go and it keeps its full label. */
  header.site nav a:not(.btn) { display: none; }
  header.site nav .btn { padding-inline: var(--s4); }
}

/* ---- the header row is allowed to wrap (29 §Mobile) ----------------------
   The rules above try to keep the header on ONE line at every width by
   shrinking gaps, the brand and the CTA. That works only while the product
   name is short. `.brand` and `.btn` are both `white-space: nowrap`, so
   "Regulation to Control" or "Commerce Exception" plus "Get free API key"
   cannot be squeezed any further — at 320px they ran 43px past the edge, and
   because `body` sets `overflow-x: hidden` the result was not a scrollbar but
   a CTA clipped off the screen entirely. The most important button on the page
   was unreachable on the narrowest phone, silently, on five of the twelve
   products.

   Wrapping is the fix that keeps both halves: the brand stays legible and the
   CTA stays reachable, which is what 29 §Mobile asks for — "nav collapses
   cleanly" and "CTA reachable without covering content", not "one line". No
   breakpoint, because there is no width at which clipping is preferable to
   wrapping; above 640px the row never needs a second line and nothing changes.
   test/responsive-qa.test.ts measures this at 320/375/390/430/1280/1440/1920. */
header.site .wrap  { flex-wrap: wrap; row-gap: var(--s2); }
header.site .brand { min-width: 0; }

/* ---- hero waves: folded sheets (owner reference, 2026-09-02) --------------
   Four sheets, each filling downward from its own top edge to the base of the
   viewBox. Where they overlap the tint accumulates, so the stepped tone of
   folded paper falls out of the geometry instead of being hand-assigned per
   band. Each top edge carries two strokes: a soft accent one nudged down (the
   shadow the fold casts) and a crisp near-white one on the edge itself (the
   lit crease). That pairing is what reads as a crease rather than a line.

   One system for twelve products: the only per-site value is var(--brand-700),
   the exact colour each favicon is filled with, so the tint is product-accurate
   by construction. No hex appears in the markup.

   The entrance is declared ONLY under `prefers-reduced-motion: no-preference`,
   so the reduced-motion resting state is the element's own static opacity: the
   finished composition, visible, with nothing to un-hide.

   Absolutely positioned and out of flow, so it cannot move the hero or add
   layout shift; the container clips; only opacity and transform animate. */
.hero { position: relative; }
.hero > .wrap { position: relative; z-index: 1; }
.hero-waves {
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden;        /* a sheet running past the edge is clipped, never scrollable */
  pointer-events: none;    /* decoration must never intercept a CTA click */
}
.hero-waves svg { display: block; width: 100%; height: 100%; }
/* The one colour decision, as a CSS rule because `fill`/`stroke` as presentation
   ATTRIBUTES do not resolve var(). */
.hero-waves .sheet  { fill: var(--brand-700); fill-opacity: .022; stroke: none; }
.hero-waves .fold   { fill: none; stroke: var(--brand-700); stroke-opacity: .05; stroke-width: 7; transform: translateY(3px); }
.hero-waves .crease { fill: none; stroke: var(--surface); stroke-opacity: .95; stroke-width: 2; }
.hero-waves .hw { opacity: var(--o); }          /* resting = final composition */
.hw1 { --o: 1;   --d: 120ms; --t: 9s;    }
.hw2 { --o: .95; --d: 240ms; --t: 11s;   }
.hw3 { --o: .9;  --d: 360ms; --t: 12.5s; }
.hw4 { --o: .85; --d: 480ms; --t: 10s;   }
@media (prefers-reduced-motion: no-preference) {
  .hero-waves .hw {
    opacity: 0;
    animation: hw-in 1s cubic-bezier(.16, .84, .44, 1) var(--d) forwards;
  }
  /* Drift lives on an inner group so the entrance transform and the ambient
     transform never overwrite one another. `alternate` reverses the cycle
     rather than snapping back, so there is no visible reset point. */
  .hero-waves .hw-d {
    animation: hw-drift var(--t) ease-in-out var(--d) infinite alternate;
  }
}
/* Both keyframes move RIGHT and DOWN from zero, never left of it.
   The sheets already begin at the hero's left edge, so a leftward transform
   puts their geometry outside the viewport for the length of the animation.
   The container clips it, so nothing is visible and nothing scrolls — but the
   boxes genuinely extend past the edge, and the viewport QA measures boxes.
   It failed at -36px (a -24px entrance plus -13px of drift) and had been
   passing only when the measurement happened to land after the animation
   settled. Entering on the Y axis keeps the same restrained arrival with no
   horizontal excursion at all. */
@keyframes hw-in {
  from { opacity: 0;         transform: translateY(10px); }
  to   { opacity: var(--o);  transform: translateY(0); }
}
@keyframes hw-drift {
  from { transform: translate3d(0, 5px, 0); }
  to   { transform: translate3d(15px, -5px, 0); }
}
/* Recomposed, not shrunk: the lowest sheet goes first, then the next, so a
   phone keeps generous white space and fewer creases cross the copy. */
@media (max-width: 640px) { .hw4 { display: none; } }
@media (max-width: 400px) { .hw3 { display: none; } }
