/* ==========================================================================
   Procurement Control Tower — public website
   ==========================================================================
   One stylesheet, no framework, no build step beyond a file copy.

   The palette, the typeface and the shapes are taken from the product itself
   (refs/brand/gen.py and src/styles/100-tokens-base.css) so that crossing
   from the website into /portal feels like one piece of software rather than
   a brochure bolted onto an app.

   Contents
     1.  Tokens
     2.  Reset and base
     3.  Typography
     4.  Layout primitives
     5.  Motion
     6.  Header
     7.  Buttons
     8.  Hero
     9.  Screenshot frame
     10. Cards and grids
     11. Comparison table
     12. Stats
     13. Footer
   ========================================================================== */


/* 1. Tokens ============================================================== */

:root {
  /* Brand — the exact five from refs/brand/gen.py. Do not invent new ones. */
  --navy:  #0B1F33;
  --blue:  #1C76BC;
  --sky:   #4FA3E3;
  --mist:  #E8EEF4;

  /* Semantic, light theme */
  --bg:         #ffffff;
  --bg-alt:     #F5F8FC;
  --surface:    #ffffff;
  --surface-2:  #EEF3F9;
  --text:       #0B1F33;
  --muted:      #5C6B7C;
  --accent:     #1C76BC;
  --accent-ink: #ffffff;
  --line:       rgba(11, 31, 51, 0.12);
  --line-soft:  rgba(11, 31, 51, 0.07);

  --shadow-sm: 0 1px 2px rgba(11, 31, 51, .06), 0 2px 8px rgba(11, 31, 51, .04);
  --shadow-md: 0 2px 6px rgba(11, 31, 51, .06), 0 12px 32px rgba(11, 31, 51, .10);
  --shadow-lg: 0 4px 12px rgba(11, 31, 51, .08), 0 32px 64px rgba(11, 31, 51, .16);

  /* Space — a 4px scale. Every gap on the site is one of these. */
  --s1: 4px;   --s2: 8px;   --s3: 12px;  --s4: 16px;  --s5: 24px;
  --s6: 32px;  --s7: 48px;  --s8: 64px;  --s9: 96px;  --s10: 128px;

  --r-sm: 8px;
  --r-md: 14px;
  --r-lg: 22px;
  --r-pill: 999px;

  --measure: 68ch;          /* max line length for body copy */
  --container: 1200px;
  --gutter: 24px;

  /* Motion — ONE curve, THREE durations, site-wide. Section 5 explains why. */
  --ease:   cubic-bezier(.16, 1, .3, 1);
  --d-fast: 120ms;
  --d-mid:  200ms;
  --d-slow: 500ms;

  color-scheme: light dark;
}

/* ── Dark ──────────────────────────────────────────────────────────────────
   THREE states, not two:

     no data-theme          follow the operating system          (the default)
     data-theme="light"     light, whatever the system says
     data-theme="dark"      dark,  whatever the system says

   The attribute is set before first paint by the inline script in
   website/partials/head.html, from localStorage. Someone running a dark OS
   who wants this site light is as legitimate as the reverse, so an explicit
   choice wins in BOTH directions — which is why the media query below is
   scoped :not([data-theme="light"]) rather than left bare.

   Plain CSS cannot share one declaration block between a media query and an
   attribute selector, so the two blocks below are deliberate duplicates of
   each other. Do not let them drift: tests/unit/website-theme.test.js parses
   this file and fails the build if they ever stop matching.
   ───────────────────────────────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:        #081320;
    --bg-alt:    #0B1A2B;
    --surface:   #0F2136;
    --surface-2: #14293F;
    --text:      #E8EEF4;
    --muted:     #8FA3B6;
    --accent:    #4FA3E3;
    --accent-ink: #06121F;
    --line:      rgba(232, 238, 244, 0.14);
    --line-soft: rgba(232, 238, 244, 0.08);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .40), 0 12px 32px rgba(0, 0, 0, .45);
    --shadow-lg: 0 4px 12px rgba(0, 0, 0, .45), 0 32px 64px rgba(0, 0, 0, .55);

    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
    --bg:        #081320;
    --bg-alt:    #0B1A2B;
    --surface:   #0F2136;
    --surface-2: #14293F;
    --text:      #E8EEF4;
    --muted:     #8FA3B6;
    --accent:    #4FA3E3;
    --accent-ink: #06121F;
    --line:      rgba(232, 238, 244, 0.14);
    --line-soft: rgba(232, 238, 244, 0.08);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, .40), 0 12px 32px rgba(0, 0, 0, .45);
    --shadow-lg: 0 4px 12px rgba(0, 0, 0, .45), 0 32px 64px rgba(0, 0, 0, .55);

    color-scheme: dark;
}
/* An explicit light choice must also pin color-scheme, or the browser keeps
   painting form controls, scrollbars and autofill from the dark OS setting
   on a page that is now light. */
:root[data-theme="light"] { color-scheme: light; }


/* 2. Reset and base ====================================================== */

/* Archivo, self-hosted. One variable file covers 400-700, latin only (the
   site is English-only by decision), 34 KB. Not loaded from Google Fonts:
   the app's Content-Security-Policy allows font-src 'self' data: and nothing
   else, and self-hosting is faster anyway. Licence: app/site/fonts/OFL.txt */
@font-face {
  font-family: 'Archivo';
  src: url('fonts/archivo-latin-var.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 17px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;          /* belt and braces against a stray wide child */
}

img, svg { max-width: 100%; height: auto; display: block; }
figure { margin: 0; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

::selection { background: var(--sky); color: var(--navy); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-ink);
  padding: var(--s3) var(--s5); border-radius: 0 0 var(--r-sm) 0;
  font-weight: 600;
}
.skip:focus { left: 0; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* 3. Typography ========================================================== */

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.1;
  text-wrap: balance;
}

h1 { font-size: clamp(2.4rem, 1.5rem + 3.6vw, 4.25rem); letter-spacing: -0.035em; line-height: 1.04; }
h2 { font-size: clamp(1.9rem, 1.3rem + 2.2vw, 2.9rem); }
h3 { font-size: 1.2rem; letter-spacing: -0.015em; line-height: 1.3; }

p { margin: 0 0 var(--s4); max-width: var(--measure); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s2);
  margin: 0 0 var(--s4);
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent);
}
.eyebrow::before {
  content: ''; width: 18px; height: 2px; border-radius: 2px;
  background: currentColor; opacity: .6;
}

.lead {
  font-size: clamp(1.05rem, 1rem + 0.4vw, 1.3rem);
  color: var(--muted);
  line-height: 1.6;
}

.section__intro { max-width: 60ch; margin-bottom: var(--s8); }
/* :not([class]) on purpose. A bare `.section__intro p` is (0,1,1) and would
   silently outrank .eyebrow and .lead (both 0,1,0) sitting inside it, so a
   classed paragraph would lose its own size and colour. Every generic
   "paragraphs inside this block" rule on this site is scoped the same way. */
.section__intro p:not([class]) { color: var(--muted); font-size: 1.1rem; }
/* Headings have margin: 0, so the supporting paragraph needs its own gap or
   it sits directly on the baseline of the heading above it. */
.section__intro h2 + p { margin-top: var(--s5); }

strong { font-weight: 600; color: var(--text); }


/* 4. Layout primitives =================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: clamp(var(--s8), 8vw, var(--s10)); }
.section--alt { background: var(--bg-alt); }
.section--tight { padding-block: clamp(var(--s7), 6vw, var(--s9)); }

/* Sticky header must not cover the heading an anchor jumps to. */
[id] { scroll-margin-top: 96px; }

/* A hairline that draws itself in when its section is revealed. */
.rule {
  height: 1px; border: 0; margin: 0;
  background: linear-gradient(90deg, var(--line), transparent);
  transform-origin: 0 50%;
}

.grid { display: grid; gap: var(--s5); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.grid--5 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }


/* 5. Motion ==============================================================
   Rules, because this is where sites go wrong:
     - transform and opacity ONLY. Never animate layout properties.
     - content is visible without JavaScript. The hidden starting state is
       applied only under html.js-anim, which site.js adds before first
       paint — so if the script never runs, nothing is ever invisible.
     - every reveal runs ONCE. Re-animating on scroll-back is what makes a
       page feel twitchy rather than considered.
     - prefers-reduced-motion turns all of it OFF, not down.
   ====================================================================== */

.js-anim .reveal {
  opacity: 0;
  transform: translateY(18px);
}
.js-anim .reveal.is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--d-slow) var(--ease),
    transform var(--d-slow) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}

/* Children stagger by their index, set in the markup via --i. */
.js-anim .stagger > * {
  opacity: 0;
  transform: translateY(18px);
}
.js-anim .stagger.is-in > * {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--d-slow) var(--ease),
    transform var(--d-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms);
}

/* The hero sequence: the strongest "this is a real product" signal there is,
   spent in the first two seconds and never repeated. */
.js-anim .hero__seq > * {
  opacity: 0;
  transform: translateY(14px);
  animation: rise var(--d-slow) var(--ease) forwards;
  animation-delay: calc(80ms + var(--i, 0) * 90ms);
}
@keyframes rise { to { opacity: 1; transform: none; } }

/* Reading-progress hairline. Pure CSS where scroll-driven animations exist;
   simply absent everywhere else, which costs nothing. */
.progress {
  position: fixed; inset: 0 auto auto 0; z-index: 60;
  height: 2px; width: 100%;
  background: linear-gradient(90deg, var(--accent), var(--sky));
  transform: scaleX(0); transform-origin: 0 50%;
  pointer-events: none;
}
@supports (animation-timeline: scroll()) {
  .js-anim .progress {
    animation: progress linear both;
    animation-timeline: scroll(root block);
  }
}
@keyframes progress { to { transform: scaleX(1); } }

/* Cross-document transitions: a multi-page static site that feels like an
   app in supporting browsers, an ordinary navigation everywhere else. */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .js-anim .reveal,
  .js-anim .stagger > *,
  .js-anim .hero__seq > * { opacity: 1 !important; transform: none !important; }
  .progress { display: none; }
}


/* 6. Header ============================================================== */

.header {
  position: sticky; top: 0; z-index: 50;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition:
    background var(--d-mid) var(--ease),
    border-color var(--d-mid) var(--ease),
    box-shadow var(--d-mid) var(--ease);
}
/* Set by site.js once the page has scrolled past the top sentinel. */
.header.is-stuck {
  background: color-mix(in srgb, var(--bg) 85%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom-color: var(--line);
  box-shadow: var(--shadow-sm);
}
@supports not (backdrop-filter: blur(1px)) {
  .header.is-stuck { background: var(--bg); }
}

.header__inner {
  display: flex; align-items: center; gap: var(--s5);
  min-height: 96px;
  transition: min-height var(--d-mid) var(--ease);
}
.header.is-stuck .header__inner { min-height: 72px; }

/* flex-shrink: 0 is not optional. .brand is a flex item, so without it the
   browser resolves a too-narrow header by COMPRESSING the lockup — an <img>
   with height set and width auto is happy to be squeezed, and the mark comes
   out horizontally distorted rather than merely small. That shipped on phones
   at 390px once: 92px of width stretched over a 164px drawing.
   With this, a header that does not fit overflows instead, and the phone
   overflow test in tests/e2e/website.spec.js fails loudly. A distorted logo
   is the one outcome nobody notices and everybody judges. */
.brand { display: flex; align-items: center; margin-right: auto; flex-shrink: 0; }
/* THE BOX IS NOT THE MARK. pct-logo-*.svg has a 262x64 viewBox whose ink
   measures 257.3 x 47.9 — a quarter of the box height is empty margin baked
   into the file by the brand-mark generator. So `height` here buys only 75%
   of itself on screen: 40px of box drew 30px of logo, which is why raising
   30 -> 40 looked like nothing changed.
   Read these as: 64 box = ~48px of actual mark. Multiply by 0.75 before
   judging any number in this block, and do not hand-edit the SVG to crop
   the margin — it is generated, and the app uses the same two files. */
.brand img { height: 64px; width: auto; transition: height var(--d-mid) var(--ease); }
.header.is-stuck .brand img { height: 52px; }
.brand:focus-visible { outline-offset: 6px; }

/* Two lockups, one per theme — both already ship with the app. One rule set
   covers every place a lockup appears (header, footer, anywhere later),
   rather than repeating the swap per component. Kept in step with the token
   blocks above: system preference unless overridden, explicit choice wins. */
.for-dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .for-light { display: none; }
  :root:not([data-theme="light"]) .for-dark  { display: block; }
}
:root[data-theme="dark"] .for-light { display: none; }
:root[data-theme="dark"] .for-dark  { display: block; }
/* An explicit light choice on a dark OS: undo the media query above. */
:root[data-theme="light"] .for-light { display: block; }
:root[data-theme="light"] .for-dark  { display: none; }

/* Appearance toggle. Sits next to Sign in; hidden until site.js unhides it,
   because it cannot work without JavaScript. */
.theme-toggle {
  display: inline-grid; place-items: center;
  width: 38px; height: 38px;
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: transparent; color: var(--muted);
  cursor: pointer; padding: 0;
  transition: color var(--d-fast) var(--ease),
              border-color var(--d-fast) var(--ease),
              background-color var(--d-fast) var(--ease);
}
.theme-toggle[hidden] { display: none; }
.theme-toggle:hover { color: var(--text); border-color: var(--muted); background: var(--surface-2); }
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Both icons occupy the same cell; the one that is not current is not
   rendered at all, so neither can be read out twice by a screen reader. */
.theme-toggle svg { grid-area: 1 / 1; display: none; }
/* Show the icon for the appearance the button would switch TO — the
   conventional reading of this control, and what the label says. */
.theme-toggle__moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle__moon { display: none; }
  :root:not([data-theme="light"]) .theme-toggle__sun  { display: block; }
}
:root[data-theme="dark"] .theme-toggle__moon { display: none; }
:root[data-theme="dark"] .theme-toggle__sun  { display: block; }
:root[data-theme="light"] .theme-toggle__moon { display: block; }
:root[data-theme="light"] .theme-toggle__sun  { display: none; }

.nav { display: flex; align-items: center; gap: var(--s5); }
.nav__link {
  color: var(--muted); text-decoration: none; font-size: 0.95rem; font-weight: 500;
  padding: var(--s2) 0; position: relative;
  transition: color var(--d-mid) var(--ease);
}
.nav__link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--accent); border-radius: 2px;
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform var(--d-mid) var(--ease);
}
.nav__link:hover, .nav__link.is-current { color: var(--text); }
.nav__link:hover::after, .nav__link.is-current::after { transform: scaleX(1); }

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

.nav-toggle {
  display: none;
  background: none; border: 1px solid var(--line); border-radius: var(--r-sm);
  color: var(--text); padding: var(--s2) var(--s3); font: inherit; font-size: .9rem;
  cursor: pointer;
}

@media (max-width: 860px) {
  .nav { display: none; }
  .nav.is-open {
    display: flex; flex-direction: column; align-items: flex-start; gap: var(--s4);
    position: absolute; left: 0; right: 0; top: 100%;
    background: var(--bg); border-bottom: 1px solid var(--line);
    padding: var(--s5) var(--gutter);
    box-shadow: var(--shadow-md);
  }
  .nav-toggle { display: block; order: 2; }
  .header__inner { position: relative; min-height: 80px; }
  .header.is-stuck .header__inner { min-height: 66px; }

  /* The lockup is 4.09 wide for every 1 tall, so 64px of box is 262px of
     width — most of a phone. It has to step down once the nav collapses to
     a Menu button, or it crowds that button and the Sign in beside it.
     Still comfortably larger than the 30px this shipped with. */
  .brand img,
  .header.is-stuck .brand img { height: 50px; }
}

@media (max-width: 620px) {
  /* A 360px phone has to seat the lockup, Sign in and Menu on one row, and
     measured at 360 that row wanted 340px inside 312px of content width.
     Where the 28px comes from, in order of what costs least:

       gutter 24 -> 16   16px, and page-wide so the header still lines up
                         with the body text beneath it
       gap    12 -> 8     8px
       lockup 40 -> 36    ~17px of width

     The appearance toggle is dropped here rather than squeezed: phones have
     a system-wide dark mode which this site already follows, so losing the
     control costs far less than a shrunken logo or a second row of chrome.
     It returns at 621px.

     Everything here is measured, not estimated — .brand is flex-shrink: 0,
     so if this budget is ever wrong the phone overflow test fails instead of
     the logo quietly compressing. */
  :root { --gutter: 16px; }
  .theme-toggle { display: none; }
  .header__inner { gap: var(--s2); }
  .brand img,
  .header.is-stuck .brand img { height: 36px; }
}


/* 7. Buttons ============================================================= */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: 11px 20px;
  border: 1px solid transparent; border-radius: var(--r-pill);
  font: inherit; font-size: 0.95rem; font-weight: 600;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition:
    transform var(--d-fast) var(--ease),
    box-shadow var(--d-mid) var(--ease),
    background var(--d-mid) var(--ease),
    border-color var(--d-mid) var(--ease);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
  background: var(--accent); color: var(--accent-ink);
  box-shadow: var(--shadow-sm);
}
.btn--primary:hover { box-shadow: var(--shadow-md); }

.btn--ghost {
  background: transparent; color: var(--text); border-color: var(--line);
}
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); }

.btn--lg { padding: 14px 26px; font-size: 1rem; }

.arrow { transition: transform var(--d-mid) var(--ease); }
.btn:hover .arrow { transform: translateX(3px); }


/* 8. Hero ================================================================ */

.hero {
  position: relative;
  padding-block: clamp(var(--s8), 9vw, 140px) clamp(var(--s7), 6vw, var(--s9));
  overflow: hidden;
}
/* A single soft brand glow. Decorative, so it is aria-hidden in the markup
   and pointer-events: none here. */
.hero__glow {
  position: absolute; inset: -30% -10% auto; height: 620px; z-index: -1;
  background:
    radial-gradient(52% 50% at 50% 40%, color-mix(in srgb, var(--sky) 26%, transparent), transparent 70%);
  pointer-events: none;
  filter: blur(6px);
}

.hero__copy { max-width: 780px; }
.hero h1 { margin-bottom: var(--s5); }
.hero .lead { max-width: 60ch; margin-bottom: var(--s6); }

.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s3); }

.hero__facts {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  margin-top: var(--s6); padding: 0; list-style: none;
  color: var(--muted); font-size: 0.92rem;
}
.hero__facts li { display: flex; align-items: center; gap: var(--s2); }
.hero__facts li::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent); flex: none;
}

.hero__shot { margin-top: clamp(var(--s7), 6vw, var(--s9)); }


/* 9. Screenshot frame ====================================================
   Product shots sit in a CSS-drawn window rather than a baked-in image
   frame, so re-capturing a screenshot never means re-editing an image.
   ====================================================================== */

.shot {
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.shot__bar {
  display: flex; align-items: center; gap: var(--s2);
  padding: 0 var(--s4); height: 40px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line-soft);
}
.shot__dot { width: 10px; height: 10px; border-radius: 50%; background: var(--line); flex: none; }
.shot__title {
  margin-left: var(--s3);
  font-size: 0.8rem; color: var(--muted); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.shot__body { display: block; width: 100%; }
.shot__body img { width: 100%; }

.shot--tilt {
  perspective: 1800px;
}
.js-anim .shot--tilt .shot {
  transform: rotateX(7deg) scale(.985);
  transform-origin: 50% 0;
  transition: transform 900ms var(--ease);
}
.js-anim .shot--tilt.is-in .shot { transform: none; }

.shot__caption {
  margin-top: var(--s3);
  font-size: 0.85rem; color: var(--muted); text-align: center;
}

/* 9b. Page header, split sections, prose ================================= */

/* The top of an inner page: smaller than the home hero, same family.
   overflow: hidden is load-bearing, not tidiness — .hero__glow is inset
   -10% on each side, so without clipping it pushes 36px of horizontal
   scroll onto every inner page at phone width. .hero clips it for the same
   reason; this rule is what stops that being a home-page-only accident. */
.pagehead {
  padding-block: clamp(var(--s8), 7vw, 110px) clamp(var(--s6), 4vw, var(--s8));
  position: relative;
  overflow: hidden;
}
.pagehead h1 { font-size: clamp(2.1rem, 1.4rem + 2.8vw, 3.4rem); margin-bottom: var(--s5); }
.pagehead .lead { max-width: 62ch; }
/* The page head already carries its own bottom padding, so a full section's
   worth on top of it opens a dead band between the title and the first
   thing the page has to say. */
.pagehead + .section { padding-top: clamp(var(--s6), 3vw, var(--s7)); }

/* Copy beside a screenshot, alternating sides down the page. */
.split {
  display: grid; gap: clamp(var(--s6), 5vw, var(--s8));
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
}
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .split--flip .split__copy { order: 2; }
}
.split + .split { margin-top: clamp(var(--s8), 7vw, 110px); }
.split__copy h3 { font-size: clamp(1.4rem, 1.1rem + 1vw, 1.9rem); margin-bottom: var(--s4); }
.split__copy p:not([class]) { color: var(--muted); }
.split__step {
  display: inline-block; margin-bottom: var(--s4);
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.12em;
  color: var(--accent);
}

/* A plain, readable list — used for capability lists and exports. */
.ticks { list-style: none; margin: var(--s5) 0 0; padding: 0; display: grid; gap: var(--s3); }
.ticks li {
  position: relative; padding-left: 26px; color: var(--muted); max-width: var(--measure);
}
.ticks li::before {
  content: ''; position: absolute; left: 4px; top: 0.62em;
  width: 9px; height: 9px; border-radius: 2px;
  background: var(--accent); opacity: .85;
}
.ticks strong { color: var(--text); font-weight: 600; }

/* A module block on /modules: heading, description, capabilities, shot. */
.module + .module { margin-top: clamp(var(--s9), 9vw, 150px); }
.module__head { display: flex; align-items: baseline; gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s5); }
.module__tabs { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0; padding: 0; list-style: none; }
.module__tabs li {
  font-size: 0.78rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 10px;
}

/* A quiet, factual block — used on /security and /platform where every claim
   is a statement of what the system does, not a benefit. */
.facts { display: grid; gap: var(--s5); }
.facts > div {
  border-left: 2px solid var(--line);
  padding-left: var(--s5);
  transition: border-color var(--d-mid) var(--ease);
}
.facts > div:hover { border-left-color: var(--accent); }
.facts h3 { margin-bottom: var(--s3); }
.facts p { color: var(--muted); }

/* One honest caveat, set apart so it cannot be mistaken for a claim. */
.caveat {
  margin-top: var(--s7); padding: var(--s5);
  border: 1px dashed var(--line); border-radius: var(--r-md);
  background: var(--surface);
}
.caveat h3 { font-size: 1rem; margin-bottom: var(--s3); }
.caveat p { color: var(--muted); font-size: 0.97rem; }

/* A step-through narrative ("what a Monday looks like"). */
.walk { counter-reset: walk; display: grid; gap: var(--s6); margin-top: var(--s7); }
.walk > li {
  counter-increment: walk; list-style: none;
  position: relative; padding-left: 58px; max-width: 62ch;
  color: var(--muted);
}
.walk > li::before {
  content: counter(walk, decimal-leading-zero);
  position: absolute; left: 0; top: 0;
  font-size: 0.85rem; font-weight: 700; letter-spacing: 0.08em;
  color: var(--accent);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 3px 10px;
}
.walk strong { color: var(--text); }


/* 9b-ii. The walkthrough ================================================= */
/* One requirement followed from the signal that raised it to the claim that
   closed it. Nineteen numbered steps down one page, sixteen of them carrying
   a screenshot. The five that do not are steps that happen on the SAME screen
   as the one above — a continuation, not a missing image — and .wt-step--same
   is what says so visually. */

/* The forwardable summary. A reader who has to argue for this internally
   needs something short they can paste into an email. */
.wt-brief { counter-reset: brief; list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s4); }
.wt-brief > li {
  counter-increment: brief; position: relative; padding-left: 46px;
  color: var(--muted); max-width: 70ch;
}
.wt-brief > li::before {
  content: counter(brief);
  position: absolute; left: 0; top: 1px;
  width: 30px; height: 30px; display: grid; place-items: center;
  font-size: 0.85rem; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--accent-ink); background: var(--accent);
  border-radius: var(--r-pill);
}
.wt-brief a { color: var(--text); font-weight: 600; text-decoration: none;
  border-bottom: 1px solid var(--line); }
.wt-brief a:hover { border-bottom-color: var(--accent); color: var(--accent); }

/* One step. */
.wt-step { padding-top: clamp(var(--s7), 6vw, var(--s9)); }
.wt-step:first-of-type { padding-top: 0; }
.wt-step__n {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent);
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--s3);
}
.wt-step h2 { font-size: clamp(1.35rem, 1.1rem + 1vw, 1.95rem); margin-bottom: var(--s4); }
.wt-step p:not([class]) { color: var(--muted); max-width: 68ch; }
.wt-step p + p { margin-top: var(--s4); }
.wt-step .shot { margin-top: var(--s6); }
.wt-step .shot + .shot { margin-top: var(--s5); }

/* A step that continues on the screen above it. Indented against a rule so
   the page never looks as though an image failed to load. */
.wt-step--same {
  padding-top: var(--s6);
  border-left: 2px solid var(--line);
  padding-left: var(--s5);
}
.wt-step--same h2 { font-size: clamp(1.1rem, 1rem + 0.5vw, 1.35rem); }

/* Numbers a reader can check, beside the screen they came from. */
.wt-figures {
  margin-top: var(--s5);
  border-top: 1px solid var(--line);
  display: grid; gap: var(--s4) var(--s6);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  padding-top: var(--s4);
}
.wt-figures div { min-width: 0; }
.wt-figures dt { font-size: 0.8rem; color: var(--muted); margin-bottom: var(--s1); }
.wt-figures dd {
  margin: 0; font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: 1.15rem; letter-spacing: -0.01em;
}

/* A detail shot is a crop of one card, not a whole window, so it gets no
   fake title bar — a chrome frame around a fragment reads as a mistake. */
.shot--detail .shot__body img { display: block; }


/* 9c. Resources: the article list and the article itself ================= */

.postlist { display: grid; gap: var(--s5); }
.postcard {
  display: block; text-decoration: none; color: inherit;
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s5) var(--s6);
  transition:
    transform var(--d-mid) var(--ease),
    box-shadow var(--d-mid) var(--ease),
    border-color var(--d-mid) var(--ease);
}
.postcard:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}
.postcard h2 { font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.6rem); margin-bottom: var(--s3); }
.postcard p { color: var(--muted); }
.postcard__meta {
  display: flex; gap: var(--s3); flex-wrap: wrap;
  margin-top: var(--s4);
  font-size: 0.82rem; color: var(--muted);
}

.post__meta { margin-top: var(--s5); font-size: 0.88rem; color: var(--muted); }

/* An article is one centred column, head included. Without this the title
   sits at the left edge of the 1200px container while the prose below is
   centred at its own 74ch measure — a small misalignment, but the kind that
   reads as carelessness on the one page people actually read at length. */
.post .pagehead .container,
.post__body { max-width: 74ch; }

/* Article prose. The measure is tighter than the rest of the site because
   this is the only place on it anyone reads more than a paragraph at a
   stretch. */
.post__body { max-width: 74ch; }
.post__body h2 {
  font-size: clamp(1.4rem, 1.15rem + 1vw, 1.9rem);
  margin: clamp(var(--s7), 5vw, var(--s8)) 0 var(--s4);
}
.post__body h3 { margin: var(--s6) 0 var(--s3); }
.post__body p,
.post__body li { font-size: 1.05rem; line-height: 1.75; }
.post__body p { margin-bottom: var(--s5); max-width: none; }
.post__body ul, .post__body ol { padding-left: 1.3em; margin: 0 0 var(--s5); }
.post__body li { margin-bottom: var(--s2); }
.post__body a { text-decoration: underline; }

.post__body blockquote {
  margin: var(--s6) 0; padding-left: var(--s5);
  border-left: 3px solid var(--accent);
  color: var(--muted); font-style: normal;
}

/* A formula, set apart. The whole premise of this section is that the
   arithmetic is shown rather than described, so it gets its own treatment. */
.formula {
  margin: var(--s6) 0; padding: var(--s5) var(--s6);
  background: var(--surface-2); border-radius: var(--r-md);
  border-left: 3px solid var(--accent);
  font-size: 1rem; line-height: 1.6;
  font-variant-numeric: tabular-nums;
  overflow-x: auto;
}

.callout {
  margin: var(--s6) 0; padding: var(--s5);
  border: 1px dashed var(--line); border-radius: var(--r-md);
  background: var(--surface);
}
.callout p { margin: 0; color: var(--muted); }

.posttable {
  width: 100%; border-collapse: collapse; margin: var(--s6) 0;
  font-size: 0.97rem;
}
.posttable th, .posttable td {
  padding: var(--s3) var(--s4); text-align: left;
  border-bottom: 1px solid var(--line);
}
.posttable thead th {
  font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted); border-bottom-color: var(--text);
}
@media (max-width: 620px) {
  .posttable { font-size: 0.9rem; }
  .posttable th, .posttable td { padding: var(--s2) var(--s3); }
}


/* 10. Cards and grids ==================================================== */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s5);
  transition:
    transform var(--d-mid) var(--ease),
    box-shadow var(--d-mid) var(--ease),
    border-color var(--d-mid) var(--ease);
}
.card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}
.card h3 { margin-bottom: var(--s3); }
.card p:not([class]) { color: var(--muted); font-size: 0.97rem; }

.card__num {
  display: block; margin-bottom: var(--s4);
  font-size: 0.8rem; font-weight: 700; letter-spacing: 0.1em;
  color: var(--accent);
}

.card__tabs {
  margin: var(--s4) 0 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: var(--s2);
}
.card__tabs li {
  font-size: 0.78rem; color: var(--muted);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 2px 10px;
}

/* The problem columns — no box, just a rule above each. */
.plain { border-top: 2px solid var(--accent); padding-top: var(--s4); }
.plain h3 { margin-bottom: var(--s3); }
.plain p:not([class]) { color: var(--muted); }


/* 11. Comparison table =================================================== */

.compare {
  width: 100%; border-collapse: collapse; margin-top: var(--s6);
  font-size: 0.97rem;
}
.compare th, .compare td {
  padding: var(--s4) var(--s4);
  border-bottom: 1px solid var(--line);
  text-align: left; vertical-align: top;
}
.compare thead th {
  font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
  border-bottom-color: var(--text);
}
.compare tbody th { font-weight: 500; color: var(--muted); width: 32%; }
.compare .col-pct { color: var(--text); font-weight: 600; }
.compare thead .col-pct { color: var(--accent); }

@media (max-width: 720px) {
  .compare, .compare tbody, .compare tr, .compare td, .compare th { display: block; }
  .compare thead { display: none; }
  .compare tr { border-bottom: 1px solid var(--line); padding-block: var(--s3); }
  .compare th, .compare td { border: 0; padding: var(--s1) 0; width: auto; }
  .compare tbody th { font-weight: 600; color: var(--text); }
  .compare td::before {
    content: attr(data-label) ' — ';
    color: var(--muted); font-size: .8rem;
  }
}


/* 12. Stats ============================================================== */

.stat { border-top: 2px solid var(--accent); padding-top: var(--s4); }
.stat h3 { margin-bottom: var(--s2); }
.stat p:not([class]) { color: var(--muted); font-size: 0.95rem; }
/* See the note in section 4: an unscoped `.stat p` outranked `.stat__num`
   and rendered the headline figure at body size. */
p.stat__num {
  font-size: clamp(2.2rem, 1.6rem + 2vw, 3.2rem);
  font-weight: 700; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  margin-bottom: var(--s3);
}

.closing {
  position: relative; overflow: hidden;
  background: var(--navy); color: var(--mist);
  border: 1px solid transparent;
  border-radius: var(--r-lg); padding: clamp(var(--s6), 5vw, var(--s8));
  text-align: center;
}
/* A single brand highlight so the block has depth rather than being a flat
   navy rectangle. Decorative only. */
.closing::before {
  content: ''; position: absolute; inset: -60% 20% auto; height: 300px;
  background: radial-gradient(50% 50% at 50% 50%, rgba(79, 163, 227, .30), transparent 70%);
  pointer-events: none;
}
.closing > * { position: relative; }
.closing h2 { color: #fff; margin-bottom: var(--s4); }
.closing p { color: color-mix(in srgb, var(--mist) 78%, transparent); margin-inline: auto; }
.closing .btn--primary { background: var(--sky); color: var(--navy); margin-top: var(--s6); }

/* In dark mode the navy is almost the page background, so the block needs an
   edge and a lift to still read as a distinct panel. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .closing {
    background: linear-gradient(160deg, #102944, #0A1B2C);
    border-color: var(--line);
    box-shadow: var(--shadow-lg);
  }
}
:root[data-theme="dark"] .closing {
  background: linear-gradient(160deg, #102944, #0A1B2C);
  border-color: var(--line);
  box-shadow: var(--shadow-lg);
}


/* 13. Footer ============================================================= */

.footer {
  border-top: 1px solid var(--line);
  padding-block: var(--s8) var(--s6);
  margin-top: var(--s9);
}
.footer__top {
  display: flex; flex-wrap: wrap; gap: var(--s7);
  justify-content: space-between; align-items: flex-start;
}
/* Under the header's 64px: the footer lockup is a sign-off, not a navigation
   anchor, so it reads as the same mark one step quieter. Remember the ink is
   75% of the box (see .brand img) — 56 here is ~42px of actual mark. The
   light/dark swap is handled by the shared .for-light/.for-dark rules in
   section 8 — nothing component-specific is needed here. */
.footer__brand img { height: 56px; width: auto; }
@media (max-width: 620px) {
  .footer__brand img { height: 44px; }
}
.footer__brand p { color: var(--muted); font-size: 0.9rem; margin-top: var(--s4); max-width: 34ch; }

.footer__nav { display: grid; gap: var(--s3); }
.footer__nav h2 {
  font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}
.footlink {
  color: var(--text); text-decoration: none; font-size: 0.95rem;
  transition: color var(--d-mid) var(--ease);
}
.footlink:hover { color: var(--accent); }

.footer__bottom {
  display: flex; flex-wrap: wrap; gap: var(--s4);
  justify-content: space-between; align-items: center;
  margin-top: var(--s8); padding-top: var(--s5);
  border-top: 1px solid var(--line-soft);
  color: var(--muted); font-size: 0.85rem;
}
