/* ==========================================================================
   AERIAL SENSING — DESIGN TOKENS
   Standalone. No build step, no Tailwind, no dependency on the marketing site,
   no @import, no external asset. The chevron and film-grain graphics are
   inline data URIs. Drop into a plain HTML page with
   <link rel="stylesheet" href="aerial-sensing-tokens.css">.

   The ONLY external thing is the webfont pair below, and both font stacks
   fall back to system fonts, so the file is fully functional without it.

   VENDOR THIS FILE. Copy it into your repo — do not hot-link it from
   aerialsensing.ai. See design/README.md for why.

   --------------------------------------------------------------------------
   QUICK START

     <html class="as-auto">            <!-- optional: follow OS light/dark -->
       <body class="as-root">
         <span class="as-eyebrow">Step 1</span>
         <h1>Ground control plan</h1>
         <p>Body copy.</p>
         <button class="as-btn-primary">Run check</button>
       </body>
     </html>

   `.as-root` on <body> is what switches on the typography and the reset.

   CONTENTS
     1. Tokens          :root (dark) / .light / .as-auto
     2. Base + reset    html, body, .as-root, headings, ::selection, focus
     3. Recipes         .as-btn-primary .as-btn-secondary .as-link .as-eyebrow
                        .as-pill-alpha .as-pill-soon .as-card .as-input
                        .as-ambient
     4. Reduced motion
     5. Contrast budget

   --------------------------------------------------------------------------
   FONTS — add to your <head>, this file cannot load them for you:

     <link rel="preconnect" href="https://fonts.googleapis.com">
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">

   Both stacks fall back to real system fonts, so the page is legible and
   correctly proportioned before (or without) the webfont.

   --------------------------------------------------------------------------
   DARK IS THE DEFAULT. Add class="light" to <html> for the light theme.

   Every colour is declared TWICE — hex first, then oklch(). Browsers that
   understand oklch() use it and get the wider gamut; browsers that do not
   discard the second declaration and keep the hex. Hex values were computed
   from the oklch values, not sampled by eye, so the two agree.
   ========================================================================== */

:root {
  /* -- surfaces -------------------------------------------------------- */
  --as-bg: #070b13;
  --as-bg: oklch(0.15 0.018 260);
  --as-surface-raised: #121720;
  --as-surface-raised: oklch(0.205 0.02 260);
  --as-surface-overlay: #1d232e;
  --as-surface-overlay: oklch(0.255 0.022 260);

  /* -- foreground ------------------------------------------------------ */
  --as-fg: #f6f9fc;
  --as-fg: oklch(0.98 0.005 260);
  --as-fg-muted: #b3b8bf;
  --as-fg-muted: oklch(0.78 0.012 260);
  --as-fg-dim: #848b96;
  --as-fg-dim: oklch(0.634 0.018 260);

  /* -- lines ----------------------------------------------------------- */
  --as-border: #2e333c99;
  --as-border: oklch(0.32 0.018 260 / 0.6);
  --as-border-strong: #4f5661;
  --as-border-strong: oklch(0.45 0.02 260);

  /* -- accent ---------------------------------------------------------- */
  --as-accent: #ff9639;
  --as-accent: oklch(0.78 0.17 55);
  --as-accent-dim: #b86724;          /* hover/pressed fill under --as-accent-fg */
  --as-accent-dim: oklch(0.595 0.13 55);
  --as-accent-fg: #140b05;           /* text ON an accent fill */
  --as-accent-fg: oklch(0.16 0.02 60);
  --as-accent-soft: #3f2717;         /* opaque tint for pills/notices */
  --as-accent-soft: oklch(0.3 0.045 55);
  --as-glow: #ff963929;
  --as-glow: oklch(0.78 0.17 55 / 0.16);

  /* -- type ------------------------------------------------------------ */
  --as-font-display: "Space Grotesk", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --as-font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono",
    Menlo, Consolas, "Liberation Mono", monospace;

  /* Fluid scale. Min is the phone size, max the desktop size; both are hard
     floors/ceilings so nothing collapses or runs away on extreme viewports. */
  --as-text-xs:   clamp(0.6875rem, 0.66rem + 0.14vw, 0.75rem);    /* 11 -> 12 */
  --as-text-sm:   clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);   /* 13 -> 14 */
  --as-text-base: clamp(0.9375rem, 0.91rem + 0.14vw, 1rem);       /* 15 -> 16 */
  --as-text-lg:   clamp(1.0625rem, 1.01rem + 0.28vw, 1.1875rem);  /* 17 -> 19 */
  --as-text-xl:   clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);        /* 20 -> 24 */
  --as-text-2xl:  clamp(1.5rem, 1.29rem + 1.07vw, 2rem);          /* 24 -> 32 */
  --as-text-3xl:  clamp(2rem, 1.57rem + 2.14vw, 3rem);            /* 32 -> 48 */
  --as-text-4xl:  clamp(2.5rem, 1.64rem + 4.29vw, 4rem);          /* 40 -> 64 */

  --as-leading-tight: 1.15;
  --as-leading-snug: 1.35;
  --as-leading-normal: 1.6;
  --as-tracking-tight: -0.025em;
  --as-tracking-normal: -0.005em;
  --as-tracking-wide: 0.14em;   /* uppercase mono labels */
  --as-tracking-wider: 0.18em;  /* uppercase mono eyebrows */

  /* -- radius ---------------------------------------------------------- */
  --as-radius-sm: 0.375rem;   /* 6px  */
  --as-radius-md: 0.5rem;     /* 8px  */
  --as-radius-control: 0.75rem; /* 12px — buttons, inputs */
  --as-radius-card: 0.9rem;   /* ~14px — cards, panels */
  --as-radius-pill: 999px;

  /* -- spacing (4px base) ---------------------------------------------- */
  --as-space-1: 0.25rem;
  --as-space-2: 0.5rem;
  --as-space-3: 0.75rem;
  --as-space-4: 1rem;
  --as-space-5: 1.25rem;
  --as-space-6: 1.5rem;
  --as-space-8: 2rem;
  --as-space-10: 2.5rem;
  --as-space-12: 3rem;
  --as-space-16: 4rem;

  /* -- focus ring ------------------------------------------------------
     One property set so every focusable element looks identical. Do not
     restyle per component; change it here. */
  --as-focus-width: 2px;
  --as-focus-style: solid;
  --as-focus-color: var(--as-accent);
  --as-focus-offset: 3px;
  --as-focus-radius: 4px;

  /* -- motion ---------------------------------------------------------- */
  --as-transition-fast: 120ms;
  --as-transition-base: 150ms;
  --as-transition-slow: 220ms;
  --as-ease: cubic-bezier(0.4, 0, 0.2, 1);

  color-scheme: dark;
}

.light {
  --as-bg: #f5f7f9;
  --as-bg: oklch(0.975 0.004 260);
  --as-surface-raised: #ffffff;
  --as-surface-raised: oklch(1 0 0);
  --as-surface-overlay: #eaedf1;
  --as-surface-overlay: oklch(0.945 0.006 260);

  --as-fg: #131922;
  --as-fg: oklch(0.21 0.02 260);
  --as-fg-muted: #474d57;
  --as-fg-muted: oklch(0.42 0.018 260);
  --as-fg-dim: #666b74;
  --as-fg-dim: oklch(0.527 0.015 260);

  --as-border: #6c727b47;
  --as-border: oklch(0.55 0.015 260 / 0.28);
  --as-border-strong: #5d646f;
  --as-border-strong: oklch(0.5 0.02 260);

  --as-accent: #b84400;
  --as-accent: oklch(0.539 0.17 48);
  --as-accent-dim: #ad5e2d;
  --as-accent-dim: oklch(0.565 0.12 50);
  --as-accent-fg: #fffaf5;
  --as-accent-fg: oklch(0.99 0.01 60);
  --as-accent-soft: #fce8df;
  --as-accent-soft: oklch(0.945 0.025 48);
  --as-glow: #ef852e24;
  --as-glow: oklch(0.72 0.16 55 / 0.14);

  color-scheme: light;
}

/* ==========================================================================
   OPTIONAL — FOLLOW THE OS LIGHT/DARK PREFERENCE

   Off by default: dark is the brand default and the marketing site ships an
   explicit toggle. If your funnel has no toggle and you would rather respect
   the OS setting, add class="as-auto" to <html>.

   Overrides, if you also ship a toggle:
     class="as-auto light"    -> always light
     class="as-auto as-dark"  -> always dark (the :not() below steps aside and
                                 the :root defaults apply, so no duplication)
   ========================================================================== */

@media (prefers-color-scheme: light) {
  .as-auto:not(.as-dark) {
    --as-bg: #f5f7f9;
    --as-bg: oklch(0.975 0.004 260);
    --as-surface-raised: #ffffff;
    --as-surface-raised: oklch(1 0 0);
    --as-surface-overlay: #eaedf1;
    --as-surface-overlay: oklch(0.945 0.006 260);

    --as-fg: #131922;
    --as-fg: oklch(0.21 0.02 260);
    --as-fg-muted: #474d57;
    --as-fg-muted: oklch(0.42 0.018 260);
    --as-fg-dim: #666b74;
    --as-fg-dim: oklch(0.527 0.015 260);

    --as-border: #6c727b47;
    --as-border: oklch(0.55 0.015 260 / 0.28);
    --as-border-strong: #5d646f;
    --as-border-strong: oklch(0.5 0.02 260);

    --as-accent: #b84400;
    --as-accent: oklch(0.539 0.17 48);
    --as-accent-dim: #ad5e2d;
    --as-accent-dim: oklch(0.565 0.12 50);
    --as-accent-fg: #fffaf5;
    --as-accent-fg: oklch(0.99 0.01 60);
    --as-accent-soft: #fce8df;
    --as-accent-soft: oklch(0.945 0.025 48);
    --as-glow: #ef852e24;
    --as-glow: oklch(0.72 0.16 55 / 0.14);

    color-scheme: light;
  }
}

/* ==========================================================================
   BASE / MINIMAL RESET

   Enough to make a bare HTML page correct. This is not a full reset like
   normalize.css — it fixes the three things that otherwise look broken:
   the default body margin, the unpainted html canvas (which shows a white
   gutter in dark mode), and inherited box-sizing.

   Put class="as-root" on <body>.
   ========================================================================== */

html {
  /* Paints the canvas so short pages and overscroll do not flash white. */
  background-color: var(--as-bg);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
}

.as-root {
  box-sizing: border-box;
  background-color: var(--as-bg);
  color: var(--as-fg);
  font-family: var(--as-font-display);
  font-size: var(--as-text-base);
  line-height: var(--as-leading-normal);
  letter-spacing: var(--as-tracking-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

.as-root *,
.as-root *::before,
.as-root *::after {
  box-sizing: inherit;
}

/* Headings actually use the scale. Without these the tokens above are inert
   and headings fall back to the browser's defaults. */
.as-root h1,
.as-root h2,
.as-root h3,
.as-root h4 {
  letter-spacing: var(--as-tracking-tight);
  line-height: var(--as-leading-tight);
  margin: 0 0 var(--as-space-4);
  text-wrap: balance; /* ignored where unsupported */
}
.as-root h1 { font-size: var(--as-text-4xl); font-weight: 600; }
.as-root h2 { font-size: var(--as-text-2xl); font-weight: 600; }
.as-root h3 { font-size: var(--as-text-lg);  font-weight: 500; }
.as-root h4 { font-size: var(--as-text-base); font-weight: 500; }

.as-root p {
  margin: 0 0 var(--as-space-4);
  max-width: 68ch; /* readable measure; override per layout if needed */
}

.as-root small {
  font-size: var(--as-text-sm);
  color: var(--as-fg-dim);
}

.as-root code,
.as-root pre,
.as-root kbd,
.as-root samp {
  font-family: var(--as-font-mono);
  font-size: 0.9em;
}

.as-root img,
.as-root svg,
.as-root video {
  max-width: 100%;
  height: auto;
}

.as-root hr {
  border: 0;
  border-top: 1px solid var(--as-border);
  margin: var(--as-space-8) 0;
}

::selection {
  background-color: var(--as-accent);
  color: var(--as-accent-fg);
}

.as-root :focus-visible,
.as-btn-primary:focus-visible,
.as-btn-secondary:focus-visible,
.as-input:focus-visible,
.as-card:focus-visible,
.as-link:focus-visible {
  outline: var(--as-focus-width) var(--as-focus-style) var(--as-focus-color);
  outline-offset: var(--as-focus-offset);
  border-radius: var(--as-focus-radius);
}

/* ==========================================================================
   COMPONENT RECIPES

   Each is a complete, self-contained class. Copy the whole rule including
   its state selectors — the states are where the contrast guarantees live.
   ========================================================================== */

/* --- .as-btn-primary ------------------------------------------------------
   Solid accent fill. The highest-emphasis action; one per view.
   States: hover/focus darken the fill, active adds a 1px press, disabled
   drops to a flat surface with dim text and no pointer.
   Contrast: --as-accent-fg on --as-accent 8.96:1 dark / 5.24:1 light.
              on --as-accent-dim (hover) 4.63:1 dark / 4.58:1 light. */
.as-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: var(--as-space-2);
  padding: var(--as-space-3) var(--as-space-5);
  border: 0;
  border-radius: var(--as-radius-control);
  background-color: var(--as-accent);
  color: var(--as-accent-fg);
  font-family: inherit;
  font-size: var(--as-text-sm);
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--as-transition-fast) var(--as-ease),
    transform var(--as-transition-fast) var(--as-ease);
}
.as-btn-primary:hover,
.as-btn-primary:focus-visible {
  background-color: var(--as-accent-dim);
}
.as-btn-primary:active {
  background-color: var(--as-accent-dim);
  transform: translateY(1px);
}
.as-btn-primary:disabled,
.as-btn-primary[aria-disabled="true"] {
  background-color: var(--as-surface-overlay);
  color: var(--as-fg-dim);
  cursor: not-allowed;
  transform: none;
  pointer-events: none;
}

/* --- .as-btn-secondary ----------------------------------------------------
   Outlined. Pairs with primary without competing.
   Contrast: --as-fg on --as-bg 18.64:1 dark / 16.43:1 light. */
.as-btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: var(--as-space-2);
  padding: var(--as-space-3) var(--as-space-5);
  border: 1px solid var(--as-border-strong);
  border-radius: var(--as-radius-control);
  background-color: transparent;
  color: var(--as-fg);
  font-family: inherit;
  font-size: var(--as-text-sm);
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--as-transition-fast) var(--as-ease),
    border-color var(--as-transition-fast) var(--as-ease),
    transform var(--as-transition-fast) var(--as-ease);
}
.as-btn-secondary:hover,
.as-btn-secondary:focus-visible {
  background-color: var(--as-surface-overlay);
  border-color: var(--as-accent);
}
.as-btn-secondary:active {
  transform: translateY(1px);
}
.as-btn-secondary:disabled,
.as-btn-secondary[aria-disabled="true"] {
  color: var(--as-fg-dim);
  border-color: var(--as-border);
  background-color: transparent;
  cursor: not-allowed;
  transform: none;
  pointer-events: none;
}

/* --- .as-pill-alpha -------------------------------------------------------
   "This is live." Opaque tint, never an alpha overlay — the contrast of the
   label must not depend on whatever sits behind the pill.
   Contrast: --as-accent on --as-accent-soft 6.39:1 dark / 4.59:1 light. */
.as-pill-alpha {
  display: inline-flex;
  align-items: center;
  padding: var(--as-space-1) var(--as-space-3);
  border-radius: var(--as-radius-pill);
  background-color: var(--as-accent-soft);
  color: var(--as-accent);
  font-family: var(--as-font-mono);
  font-size: var(--as-text-xs);
  text-transform: uppercase;
  letter-spacing: var(--as-tracking-wide);
  white-space: nowrap;
}

/* --- .as-pill-soon --------------------------------------------------------
   "Not yet." Deliberately quieter than .as-pill-alpha but still AA.
   Contrast: --as-fg-dim on --as-surface-overlay 4.59:1 dark / 4.56:1 light. */
.as-pill-soon {
  display: inline-flex;
  align-items: center;
  padding: var(--as-space-1) var(--as-space-3);
  border-radius: var(--as-radius-pill);
  background-color: var(--as-surface-overlay);
  color: var(--as-fg-dim);
  font-family: var(--as-font-mono);
  font-size: var(--as-text-xs);
  text-transform: uppercase;
  letter-spacing: var(--as-tracking-wide);
  white-space: nowrap;
}

/* --- .as-card -------------------------------------------------------------
   Raised panel. Add .as-card--interactive when the whole card is a link. */
.as-card {
  display: block;
  padding: var(--as-space-5);
  border-radius: var(--as-radius-card);
  background-color: var(--as-surface-raised);
  color: inherit;
  text-decoration: none;
}
.as-card--interactive {
  cursor: pointer;
  transition:
    background-color var(--as-transition-base) var(--as-ease),
    box-shadow var(--as-transition-slow) var(--as-ease),
    transform var(--as-transition-base) var(--as-ease);
  box-shadow: 0 0 0 1px var(--as-glow);
}
.as-card--interactive:hover,
.as-card--interactive:focus-visible {
  background-color: var(--as-surface-overlay);
  box-shadow:
    0 0 0 1px var(--as-glow),
    0 14px 36px -14px var(--as-glow);
  transform: translateY(-2px);
}
.as-card--interactive:active {
  transform: translateY(0);
}
.as-card--interactive[aria-disabled="true"] {
  cursor: not-allowed;
  pointer-events: none;
  box-shadow: none;
}

/* --- .as-link -------------------------------------------------------------
   Inline text link. Underline offset keeps descenders legible.
   Contrast: --as-accent on --as-bg 9.07:1 dark / 5.06:1 light. */
.as-link {
  color: var(--as-accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  border-radius: var(--as-focus-radius);
  transition: color var(--as-transition-fast) var(--as-ease);
}
.as-link:hover {
  text-decoration-thickness: 2px;
}
.as-link:active {
  color: var(--as-accent-dim);
}
.as-link[aria-disabled="true"] {
  color: var(--as-fg-dim);
  text-decoration: none;
  cursor: not-allowed;
  pointer-events: none;
}

/* --- .as-eyebrow ----------------------------------------------------------
   Uppercase mono label above a heading. This is the pattern that uses
   --as-tracking-wider. Never shrink it below --as-text-xs: uppercase mono at
   small sizes is already the hardest thing on the page to read.
   Contrast: --as-accent on --as-bg 9.07:1 dark / 5.06:1 light.
   Add .as-eyebrow--muted for the quieter variant (--as-fg-muted). */
.as-eyebrow {
  display: block;
  margin: 0 0 var(--as-space-3);
  font-family: var(--as-font-mono);
  font-size: var(--as-text-xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--as-tracking-wider);
  color: var(--as-accent);
}
.as-eyebrow--muted {
  color: var(--as-fg-muted);
}

/* --- .as-input ------------------------------------------------------------
   Contrast: --as-fg on --as-surface-raised 17.00:1 dark / 17.65:1 light.
   Placeholder uses --as-fg-dim: 5.23:1 dark / 5.36:1 light — AA on this
   surface. Do not swap it for a lighter value to "soften" it. */
.as-input {
  display: block;
  width: 100%;
  padding: var(--as-space-3) var(--as-space-4);
  border: 1px solid var(--as-border-strong);
  border-radius: var(--as-radius-control);
  background-color: var(--as-surface-raised);
  color: var(--as-fg);
  font-family: inherit;
  font-size: var(--as-text-base);
  line-height: var(--as-leading-snug);
  transition:
    border-color var(--as-transition-fast) var(--as-ease),
    background-color var(--as-transition-fast) var(--as-ease);
}
.as-input::placeholder {
  color: var(--as-fg-dim);
  opacity: 1; /* Firefox dims placeholders by default; that would break AA */
}
.as-input:hover {
  border-color: var(--as-fg-dim);
}
.as-input:focus,
.as-input:focus-visible {
  border-color: var(--as-accent);
  outline: var(--as-focus-width) var(--as-focus-style) var(--as-focus-color);
  outline-offset: var(--as-focus-offset);
}
.as-input:disabled {
  background-color: var(--as-surface-overlay);
  color: var(--as-fg-dim);
  border-color: var(--as-border);
  cursor: not-allowed;
}
.as-input[aria-invalid="true"] {
  border-color: var(--as-accent);
}
/* Works on <textarea> as-is. <select> additionally needs the UA arrow
   suppressed, or it renders a light-on-light control in dark mode. */
textarea.as-input {
  resize: vertical;
  min-height: 6rem;
}
select.as-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: var(--as-space-8);
  cursor: pointer;
  /* Chevron drawn as an inline data URI — no external asset, no icon font.
     currentColor is not available inside url(), so this is --as-fg-dim. */
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23848b96' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M4 6l4 4 4-4'/></svg>");
  background-repeat: no-repeat;
  background-position: right var(--as-space-3) center;
  background-size: 1rem 1rem;
}
.light select.as-input,
.as-auto select.as-input {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23666b74' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M4 6l4 4 4-4'/></svg>");
}

/* --- .as-ambient ----------------------------------------------------------
   Optional. The marketing site's signature backdrop: a soft accent glow
   behind the hero plus a fine film grain. Purely decorative and
   pointer-events:none, so it never intercepts clicks.

   Usage: <div class="as-ambient" aria-hidden="true"></div> as the first child
   of a position:relative container.

   The grain is an inline SVG data URI — no external asset. */
.as-ambient {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60rem 34rem at 50% -6rem, var(--as-glow), transparent 70%);
}
.as-ambient::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.028;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 1 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 160px 160px;
}
.light .as-ambient::after,
.as-auto .as-ambient::after {
  opacity: 0.02;
  mix-blend-mode: multiply;
}

/* ==========================================================================
   REDUCED MOTION
   Transitions are near-zeroed rather than removed, so transitionend still
   fires and any JS waiting on it does not hang.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
  .as-btn-primary:active,
  .as-btn-secondary:active,
  .as-card--interactive:hover,
  .as-card--interactive:focus-visible {
    transform: none !important;
  }
}

/* ==========================================================================
   CONTRAST BUDGET — MEASURED, NOT ESTIMATED

   Every pair below was computed by converting the oklch token to sRGB,
   quantising to 8-bit as the browser rasterises, compositing any alpha over
   its real backdrop, and applying the WCAG 2.1 relative-luminance formula.
   Source: DEPLOY.md section 7 in the marketing-site repo.

     PAIR                                          LIGHT     DARK
     ------------------------------------------------------------
     fg on bg  (body copy, headings)               16.43     18.64
     fg-muted on bg  (secondary copy)               7.93      9.87
     fg-muted on surface-raised  (card body)        8.51      9.00
     fg-dim on bg  (disclaimer, fine print)         4.99      5.73
     fg-dim on surface-raised  (de-emphasised)      5.36      5.23
     fg-dim on surface-overlay  (.as-pill-soon)     4.56      4.59
     accent on bg  (eyebrows, links)                5.06      9.07
     accent on surface-raised                       5.43      8.27
     accent on accent-soft  (.as-pill-alpha)        4.59      6.39
     accent-fg on accent  (.as-btn-primary)         5.24      8.96
     accent-fg on accent-dim  (primary hover)       4.58      4.63

   Worst case: 4.56:1 light, 4.59:1 dark. Both clear WCAG AA (4.5:1).
   No pair here qualifies as large text, so 4.5:1 is the bar for all of them.

   --------------------------------------------------------------------------
   THE RULE

   Any new foreground/background pairing must measure at least 4.5:1 in BOTH
   light and dark before it ships. Both. A pair that passes in dark and fails
   in light is a failing pair.

   Two specific traps, both of which produced real failures in this palette:

     1. Do not reach for an opacity modifier to de-emphasise text. Alpha makes
        the effective contrast depend on whatever happens to be behind the
        element, so it is not a fixed value you can verify once. Use the
        --as-fg-muted / --as-fg-dim step instead. Every /70 and /85 modifier
        in the source site failed AA and was removed.

     2. Do not put text on an alpha tint of the accent. Use --as-accent-soft,
        which is opaque and measured. The pill built as accent-at-15%-alpha
        measured 3.73:1.
   ========================================================================== */
