# Foundations

> Design tokens (light and dark), shared keyframes and utilities. Every component needs this file.

- Import it once from your base layout.
- Follows the OS theme. Force one with `<html data-theme="light">` or `"dark"`.
- Components read tokens, never hard-coded colors, radii, shadows or easings.

## Tokens (55)

`--vs-page`, `--vs-canvas`, `--vs-surface`, `--vs-inset`, `--vs-field`, `--vs-hover`, `--vs-hover-2`, `--vs-ink`, `--vs-ink-2`, `--vs-ink-3`, `--vs-ink-inverse`, `--vs-line`, `--vs-line-strong`, `--vs-line-soft`, `--vs-accent`, `--vs-accent-ink`, `--vs-accent-tint`, `--vs-on-accent`, `--vs-green`, `--vs-green-tint`, `--vs-orange`, `--vs-orange-tint`, `--vs-red`, `--vs-red-tint`, `--vs-tooltip-bg`, `--vs-tooltip-fg`, `--vs-tooltip-muted`, `--vs-tooltip-border`, `--vs-elev-ring`, `--vs-elev-ring-strong`, `--vs-shadow-xs`, `--vs-shadow-sm`, `--vs-shadow-md`, `--vs-shadow-lg`, `--vs-shadow-inset-field`, `--vs-font-sans`, `--vs-font-stretch`, `--vs-font-mono`, `--vs-radius-chip`, `--vs-radius-control`, `--vs-radius-card`, `--vs-radius-window`, `--vs-radius-pill`, `--vs-ease-out`, `--vs-ease-in-out`, `--vs-ease-link`, `--vs-duration-fast`, `--vs-duration`, `--vs-grid-line`, `--vs-shadow-hairline`, `--vs-shadow-btn`, `--vs-shadow-card`, `--vs-shadow-raised`, `--vs-shadow-overlay`, `--vs-focus-ring`

## foundation.css

```css
/* ──────────────────────────────────────────────────────────
 * Viewership UI: foundation
 *
 * The one stylesheet every component needs. Import it once, globally:
 *
 *   import "../styles/foundation.css";
 *
 * Contents: design tokens (Viewership paper + ink, and a neutral
 * alternate), shared keyframes, and the reduced-motion baseline.
 * Components never hard-code colors, radii, shadows or easings; they read
 * these tokens, so a theme is just a different set of values.
 *
 * Token system adapted from Beautiful UI (MIT, © 2026 Shane Levine,
 * https://www.beautifului.dev); see THIRD_PARTY_NOTICES.md.
 *
 * Mode (paper or ink), on any element:
 *   - default follows the OS (prefers-color-scheme)
 *   - <html data-theme="light|dark"> forces one for the page
 *   - data-vs-mode="light|dark" forces one for an element and its subtree
 * Theme:
 *   - Viewership (paper + ink, signal accent) is the default
 *   - data-vs-theme="neutral" restores the original Beautiful UI look; put it
 *     on the same element as data-vs-mode
 * ────────────────────────────────────────────────────────── */

/* ── Viewership, paper (light) ── */
:root,
[data-vs-mode="light"] {
  color-scheme: light;

  /* Surfaces, back to front */
  --vs-page: #f2ece4;
  --vs-canvas: #e8e0d5;
  --vs-surface: #fbf8f3;
  --vs-inset: #f6f1ea;
  --vs-field: #ede6dc;
  --vs-hover: #f0e9e0;
  --vs-hover-2: #e5dccf;

  /* Ink ramp: primary, secondary, tertiary */
  --vs-ink: #0d0809;
  --vs-ink-2: #5a4f50;
  --vs-ink-3: #7a6f6e;
  /* Ink on top of a filled --vs-ink surface (primary buttons) */
  --vs-ink-inverse: #f2ece4;

  /* Hairlines */
  --vs-line: #e3dbd0;
  --vs-line-strong: #d2c7ba;
  --vs-line-soft: #ece5db;

  /* Accent + semantic condiments (use sparingly) */
  --vs-accent: #e1232a;
  --vs-accent-ink: #b81a20;
  --vs-accent-tint: #f8dde5;
  --vs-on-accent: #ffffff;
  --vs-green: #357045;
  --vs-green-tint: #dfe8da;
  --vs-orange: #9e5812;
  --vs-orange-tint: #f3e2c8;
  --vs-red: #b0341b;
  --vs-red-tint: #f4d9cc;

  /* Tooltips / dark popovers (stay dark in both modes) */
  --vs-tooltip-bg: #0d0809;
  --vs-tooltip-fg: #f2ece4;
  --vs-tooltip-muted: #a99e97;
  --vs-tooltip-border: #2b2023;

  /* Elevation: warm, ink-tinted, low. Rings are the hairline of a raised surface. */
  --vs-elev-ring: #e3dbd0;
  --vs-elev-ring-strong: #d2c7ba;
  --vs-shadow-xs: 0 1px 1px rgb(13 8 9 / 0.04), 0 1px 2px rgb(13 8 9 / 0.04);
  --vs-shadow-sm: 0 1px 2px rgb(13 8 9 / 0.04), 0 2px 4px rgb(13 8 9 / 0.04);
  --vs-shadow-md: 0 2px 4px rgb(13 8 9 / 0.04), 0 6px 14px rgb(13 8 9 / 0.05);
  --vs-shadow-lg: 0 4px 8px rgb(13 8 9 / 0.05), 0 16px 36px rgb(13 8 9 / 0.09);
  --vs-shadow-inset-field: inset 0 1px 2px rgb(13 8 9 / 0.1);

  /* Type: Archivo at its normal width for UI; mono is for code only */
  --vs-font-sans: "Archivo Variable", "Archivo", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --vs-font-stretch: 100%;
  --vs-font-mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;

  /* Radii */
  --vs-radius-chip: 4px;
  --vs-radius-control: 6px;
  --vs-radius-card: 8px;
  --vs-radius-window: 12px;
  --vs-radius-pill: 999px;

  /* Motion */
  --vs-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --vs-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --vs-ease-link: cubic-bezier(0.16, 1, 0.3, 1);
  --vs-duration-fast: 150ms;
  --vs-duration: 300ms;
}

/* ── Viewership, ink (dark) ── */
[data-vs-mode="dark"],
:root[data-theme="dark"] {
  color-scheme: dark;
  --vs-page: #0d0809;
  --vs-canvas: #120b0d;
  --vs-surface: #1a1214;
  --vs-inset: #150e10;
  --vs-field: #231a1c;
  --vs-hover: #221a1c;
  --vs-hover-2: #2c2225;
  --vs-ink: #f2ece4;
  --vs-ink-2: #b5aba3;
  --vs-ink-3: #8a7f7a;
  --vs-ink-inverse: #0d0809;
  --vs-line: #2a2023;
  --vs-line-strong: #3a2e31;
  --vs-line-soft: #211819;
  --vs-accent: #e8272a;
  --vs-accent-ink: #f7a9b8;
  --vs-accent-tint: rgb(232 39 42 / 0.18);
  --vs-green: #7fbf8e;
  --vs-green-tint: rgb(127 191 142 / 0.14);
  --vs-orange: #e2a35c;
  --vs-orange-tint: rgb(226 163 92 / 0.14);
  --vs-red: #f08a6b;
  --vs-red-tint: rgb(240 138 107 / 0.14);
  --vs-tooltip-bg: #2a1f22;
  --vs-tooltip-border: #3a2e31;
  --vs-elev-ring: rgb(242 236 228 / 0.09);
  --vs-elev-ring-strong: rgb(242 236 228 / 0.13);
  --vs-shadow-xs: 0 1px 2px rgb(0 0 0 / 0.35);
  --vs-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.25), 0 2px 6px rgb(0 0 0 / 0.25);
  --vs-shadow-md: 0 2px 10px rgb(0 0 0 / 0.3);
  --vs-shadow-lg: 0 8px 28px rgb(0 0 0 / 0.45);
  --vs-shadow-inset-field: inset 0 1px 2px rgb(0 0 0 / 0.45);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --vs-page: #0d0809;
    --vs-canvas: #120b0d;
    --vs-surface: #1a1214;
    --vs-inset: #150e10;
    --vs-field: #231a1c;
    --vs-hover: #221a1c;
    --vs-hover-2: #2c2225;
    --vs-ink: #f2ece4;
    --vs-ink-2: #b5aba3;
    --vs-ink-3: #8a7f7a;
    --vs-ink-inverse: #0d0809;
    --vs-line: #2a2023;
    --vs-line-strong: #3a2e31;
    --vs-line-soft: #211819;
    --vs-accent: #e8272a;
    --vs-accent-ink: #f7a9b8;
    --vs-accent-tint: rgb(232 39 42 / 0.18);
    --vs-green: #7fbf8e;
    --vs-green-tint: rgb(127 191 142 / 0.14);
    --vs-orange: #e2a35c;
    --vs-orange-tint: rgb(226 163 92 / 0.14);
    --vs-red: #f08a6b;
    --vs-red-tint: rgb(240 138 107 / 0.14);
    --vs-tooltip-bg: #2a1f22;
    --vs-tooltip-border: #3a2e31;
    --vs-elev-ring: rgb(242 236 228 / 0.09);
    --vs-elev-ring-strong: rgb(242 236 228 / 0.13);
    --vs-shadow-xs: 0 1px 2px rgb(0 0 0 / 0.35);
    --vs-shadow-sm: 0 1px 2px rgb(0 0 0 / 0.25), 0 2px 6px rgb(0 0 0 / 0.25);
    --vs-shadow-md: 0 2px 10px rgb(0 0 0 / 0.3);
    --vs-shadow-lg: 0 8px 28px rgb(0 0 0 / 0.45);
    --vs-shadow-inset-field: inset 0 1px 2px rgb(0 0 0 / 0.45);
  }
}

/* ── Neutral (the original Beautiful UI look), light ── */
[data-vs-theme="neutral"] {
  color-scheme: light;
  --vs-page: oklch(0.985 0.001 286.376);
  --vs-canvas: oklch(0.961 0.002 247.84);
  --vs-surface: oklch(1 0 0);
  --vs-inset: oklch(0.979 0.002 247.839);
  --vs-field: oklch(0.961 0.001 286.375);
  --vs-hover: oklch(0.97 0.002 247.839);
  --vs-hover-2: oklch(0.933 0.003 247.86);
  --vs-ink: oklch(0.247 0.006 258.361);
  --vs-ink-2: oklch(0.506 0.01 264.477);
  --vs-ink-3: oklch(0.695 0.009 264.505);
  --vs-ink-inverse: oklch(0.985 0.001 286.376);
  --vs-line: oklch(0.946 0.003 264.542);
  --vs-line-strong: oklch(0.912 0.005 258.326);
  --vs-line-soft: oklch(0.966 0.002 264.542);
  --vs-accent: oklch(0.626 0.205 254.947);
  --vs-accent-ink: oklch(0.556 0.187 255.617);
  --vs-accent-tint: oklch(0.96 0.019 252.878);
  --vs-on-accent: #ffffff;
  --vs-green: oklch(0.603 0.155 150.883);
  --vs-green-tint: oklch(0.958 0.017 159.118);
  --vs-orange: oklch(0.689 0.179 49.902);
  --vs-orange-tint: oklch(0.964 0.021 67.581);
  --vs-red: oklch(0.621 0.192 23.042);
  --vs-red-tint: oklch(0.956 0.017 17.462);
  --vs-tooltip-bg: oklch(0.272 0.008 264.435);
  --vs-tooltip-fg: oklch(0.976 0.002 247.839);
  --vs-tooltip-muted: oklch(0.731 0.008 260.731);
  --vs-tooltip-border: oklch(0.356 0.007 264.474);
  --vs-elev-ring: oklch(0.946 0.003 264.542);
  --vs-elev-ring-strong: oklch(0.912 0.005 258.326);
  --vs-shadow-xs: 0 1px 1px oklch(0 0 0 / 0.04), 0 1px 2px oklch(0 0 0 / 0.04);
  --vs-shadow-sm: 0 1px 2px oklch(0 0 0 / 0.04), 0 2px 4px oklch(0 0 0 / 0.04);
  --vs-shadow-md: 0 2px 4px oklch(0 0 0 / 0.04), 0 6px 14px oklch(0 0 0 / 0.06);
  --vs-shadow-lg: 0 4px 8px oklch(0 0 0 / 0.05), 0 16px 36px oklch(0 0 0 / 0.1);
  --vs-shadow-inset-field: inset 0 1px 2px oklch(0 0 0 / 0.12);
  --vs-font-sans: "Inter Variable", "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --vs-font-stretch: 100%;
  --vs-font-mono: "JetBrains Mono Variable", "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --vs-radius-chip: 6px;
  --vs-radius-control: 8px;
  --vs-radius-card: 10px;
  --vs-radius-window: 14px;
  --vs-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
}

/* ── Neutral, dark ── */
[data-vs-theme="neutral"][data-vs-mode="dark"],
:root[data-theme="dark"][data-vs-theme="neutral"] {
  color-scheme: dark;
  --vs-page: oklch(0.209 0.004 264.477);
  --vs-canvas: oklch(0.231 0.004 264.487);
  --vs-surface: oklch(0.26 0.006 271.191);
  --vs-inset: oklch(0.243 0.004 264.492);
  --vs-field: oklch(0.293 0.006 271.223);
  --vs-hover: oklch(0.289 0.006 271.22);
  --vs-hover-2: oklch(0.318 0.007 274.747);
  --vs-ink: oklch(0.964 0.002 247.839);
  --vs-ink-2: oklch(0.731 0.008 260.731);
  --vs-ink-3: oklch(0.541 0.01 264.484);
  --vs-ink-inverse: oklch(0.231 0.004 264.487);
  --vs-line: oklch(0.308 0.006 258.354);
  --vs-line-strong: oklch(0.356 0.007 264.474);
  --vs-line-soft: oklch(0.278 0.006 258.354);
  --vs-accent: oklch(0.68 0.173 253.301);
  --vs-accent-ink: oklch(0.788 0.113 248.33);
  --vs-accent-tint: oklch(0.68 0.173 253.301 / 0.16);
  --vs-green: oklch(0.705 0.154 153.814);
  --vs-green-tint: oklch(0.705 0.154 153.814 / 0.14);
  --vs-orange: oklch(0.746 0.156 55.642);
  --vs-orange-tint: oklch(0.746 0.156 55.642 / 0.14);
  --vs-red: oklch(0.666 0.18 21.433);
  --vs-red-tint: oklch(0.666 0.18 21.433 / 0.14);
  --vs-tooltip-bg: oklch(0.182 0.004 264.459);
  --vs-tooltip-border: oklch(0.308 0.006 258.354);
  --vs-elev-ring: oklch(1 0 0 / 0.11);
  --vs-elev-ring-strong: oklch(1 0 0 / 0.15);
  --vs-shadow-xs: 0 1px 2px oklch(0 0 0 / 0.3);
  --vs-shadow-sm: 0 1px 2px oklch(0 0 0 / 0.2), 0 2px 6px oklch(0 0 0 / 0.2);
  --vs-shadow-md: 0 2px 10px oklch(0 0 0 / 0.22);
  --vs-shadow-lg: 0 8px 28px oklch(0 0 0 / 0.34);
  --vs-shadow-inset-field: inset 0 1px 2px oklch(0 0 0 / 0.4);
}

/* ── Derived tokens ──
 * Re-declared on every element that sets a mode or theme, so their var()
 * references resolve against that element's palette, not the page's. */
:root,
[data-vs-mode],
[data-vs-theme] {
  --vs-grid-line: color-mix(in srgb, var(--vs-line) 78%, transparent);
  --vs-shadow-hairline: 0 0 0 1px var(--vs-line);
  --vs-shadow-btn: 0 0 0 1px var(--vs-elev-ring-strong), var(--vs-shadow-xs);
  --vs-shadow-card: 0 0 0 1px var(--vs-elev-ring), var(--vs-shadow-sm);
  --vs-shadow-raised: 0 0 0 1px var(--vs-elev-ring), var(--vs-shadow-md);
  --vs-shadow-overlay: 0 0 0 1px var(--vs-elev-ring-strong), var(--vs-shadow-lg);
  --vs-focus-ring: 0 0 0 2px var(--vs-surface), 0 0 0 3.5px var(--vs-accent);
}

/* A scoped mode or theme also sets the text basics, so components never
 * inherit the host page's colour or type. */
[data-vs-mode],
[data-vs-theme] {
  color: var(--vs-ink);
  font-family: var(--vs-font-sans);
  font-stretch: var(--vs-font-stretch);
}

/* ── Shared keyframes (prefixed so they never collide with app CSS) ── */

@keyframes vs-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes vs-fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes vs-pop-in {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes vs-shimmer-text {
  from { background-position: 150% center; }
  to { background-position: -50% center; }
}
@keyframes vs-pixel-on {
  0%, 100% { opacity: 0.15; }
  18%, 42% { opacity: 1; }
  62% { opacity: 0.15; }
}
@keyframes vs-spin {
  to { transform: rotate(360deg); }
}
@keyframes vs-caret-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}
@keyframes vs-pulse {
  0%, 100% { opacity: 0.35; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1); }
}

/* ── Shared utilities ── */

/* `hidden` always wins, even over a component's own `display` rule. */
:where([hidden]) {
  display: none !important;
}

/* Visually hidden but announced by screen readers. */
.vs-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Reduced motion: components still render their end state ── */
@media (prefers-reduced-motion: reduce) {
  :root {
    --vs-duration-fast: 0ms;
    --vs-duration: 0ms;
  }
}
```
