/* ===========================================================================
   vedika-tokens.css — THE SINGLE SOURCE OF TRUTH
   ---------------------------------------------------------------------------
   Every colour, surface, radius, shadow, type step, space step and motion
   value in the Vedika design system is defined in this file and nowhere else.

   THE RULE THAT MAKES THIS WORK
   -----------------------------
   No other file in the system may contain a literal colour, a literal
   border-radius or a literal box-shadow. Components consume semantic tokens
   only. `tools/check-tokens.mjs` enforces this and fails the build if it is
   violated. A component with a hard-coded colour is a bug in the system, not
   a detail.

   Consequence: retheming all 29,861 pages — a new accent, a warmer neutral, a
   different radius language, a full dark inversion — is an edit to THIS FILE
   ALONE. Nothing else changes and no page is touched.

   TWO TIERS
   ---------
     RAW      --vds-v-600, --vds-n-50, --vds-ink-magenta
              The palette. Physical values. Components MUST NOT use these.
     SEMANTIC --vds-surface-raised, --vds-text-muted, --vds-accent-primary
              The meaning. Components use ONLY these.

   The indirection is the whole point: the raw layer answers "what colour is
   it", the semantic layer answers "what is it for". Dark mode, a future
   high-contrast mode and a white-label customer theme are all re-pointings of
   the semantic layer at different raw values. None of them touch a component.

   WHY EVERY TOKEN IS PREFIXED --vds-
   ----------------------------------
   These custom properties land on :root of ~30,000 pages that already carry
   thousands of Tailwind utilities and a dozen legacy stylesheets. An
   unprefixed `--surface` would be a coin flip against whatever a page already
   defines. The prefix is not decoration; at this scale it is the only thing
   that makes a global :root safe.

   LAYER
   -----
   Everything here sits in `@layer vds.tokens`, declared by
   css/vedika-theme.css. Custom-property declarations are not affected by layer
   precedence the way normal declarations are, but keeping them in the layer
   means the whole system can be reasoned about — and disabled — as one unit.
   =========================================================================== */

@layer vds.tokens {

/* ===========================================================================
   TIER 1 — RAW PALETTE
   ---------------------------------------------------------------------------
   Do not consume these in a component. Ever. They exist to feed tier 2.
   =========================================================================== */

:root {
  /* --- violet ramp -------------------------------------------------------
     Anchored on the four values already shipping site-wide (#7c3aed, #6d28d9,
     #5b21b6, #a5b4fc) and the two the gradient sheet uses (#9b7cff, #c8b8ff),
     so adopting the system shifts no existing brand colour by a single byte. */
  --vds-v-50:  #f6f4ff;
  --vds-v-100: #ede9fe;
  --vds-v-200: #ddd6fe;
  --vds-v-300: #c8b8ff;   /* shared with css/vedika-sheet-css.css            */
  --vds-v-400: #a78bfa;
  --vds-v-500: #9b7cff;   /* shared with css/vedika-sheet-css.css            */
  --vds-v-600: #7c3aed;   /* in use site-wide today                          */
  --vds-v-700: #6d28d9;   /* in use today                                    */
  --vds-v-800: #5b21b6;   /* in use today                                    */
  --vds-v-900: #4c1d95;
  --vds-v-950: #2b1065;

  /* #a5b4fc is in use and is an indigo, not a violet. It keeps its own name
     rather than being forced into the ramp and quietly shifted. */
  --vds-periwinkle: #a5b4fc;

  /* --- neutrals ----------------------------------------------------------
     Very slightly violet-cast, so a white surface next to the ramp does not
     read as a different colour temperature. --vds-n-950 is already the hero
     background in index.html. */
  --vds-n-0:   #ffffff;
  --vds-n-25:  #fbfafd;
  --vds-n-50:  #f6f5f9;
  --vds-n-100: #eceaf2;
  --vds-n-200: #dedbe6;
  --vds-n-300: #c3bfd0;
  --vds-n-400: #948fa6;
  --vds-n-500: #6b6680;
  --vds-n-600: #524d64;
  --vds-n-700: #3b3750;
  --vds-n-800: #241f38;
  --vds-n-900: #16112a;
  --vds-n-950: #0b0716;

  /* --- dark-mode surface steps -------------------------------------------
     Dark surfaces step UP in lightness as they come forward. These are not
     neutrals — they carry the violet cast that keeps a dark Vedika page from
     looking like a generic slate dashboard. */
  --vds-d-surface:   #141029;
  --vds-d-surface-2: #1c1735;
  --vds-d-surface-3: #262041;
  --vds-d-text:      #f4f2fb;
  --vds-d-text-2:    #bdb7d2;
  --vds-d-text-3:    #9a94b4;
  --vds-d-text-4:    #6f6a8a;

  /* --- status hues, light and dark variants ------------------------------- */
  --vds-ok-600:  #0f9d6e;   --vds-ok-300:  #4ede9f;   --vds-ok-50:  #e6f7f0;
  --vds-warn-600:#b45309;   --vds-warn-300:#f7b455;   --vds-warn-50:#fdf3e3;
  --vds-err-600: #d02a4a;   --vds-err-300: #ff8098;   --vds-err-50: #fdecef;
  --vds-info-600:#2563eb;   --vds-info-300:#86b1ff;   --vds-info-50:#e8effd;

  /* --- bloom inks ---------------------------------------------------------
     The colours the dust bloom disperses. Given in OKLCH for modern engines
     and as an sRGB triplet for the pre-2023 fallback stack. Same register as
     the colourways in css/vedika-sheet-css.css, so a bloom on a button and a
     bloom on a poster are the same family on screen. */
  --vds-ink-brand:      oklch(0.646 0.198 296);  --vds-ink-brand-srgb:      155 124 255;
  --vds-ink-periwinkle: oklch(0.640 0.180 272);  --vds-ink-periwinkle-srgb: 108 118 250;
  --vds-ink-lilac:      oklch(0.760 0.130 315);  --vds-ink-lilac-srgb:      231 148 226;
  --vds-ink-magenta:    oklch(0.640 0.270 350);  --vds-ink-magenta-srgb:    246  33 143;
  --vds-ink-orange:     oklch(0.710 0.195  42);  --vds-ink-orange-srgb:     255 113  51;
  --vds-ink-gold:       oklch(0.905 0.170  96);  --vds-ink-gold-srgb:       255 208  62;
  --vds-ink-cyan:       oklch(0.790 0.135 213);  --vds-ink-cyan-srgb:        60 194 236;
  --vds-ink-azure:      oklch(0.660 0.170 250);  --vds-ink-azure-srgb:       74 140 246;
  --vds-ink-violet:     oklch(0.600 0.210 300);  --vds-ink-violet-srgb:     140 100 250;
  --vds-ink-lime:       oklch(0.880 0.170 128);  --vds-ink-lime-srgb:       190 226  70;
  --vds-ink-mint:       oklch(0.900 0.130 165);  --vds-ink-mint-srgb:       150 236 200;

  /* --- paper register -----------------------------------------------------
     Added 2026-07-29. These are NOT new brand colours: they are the literal
     bloom stops of the `PAPER` colourway that the WebGL sheet paints (see the
     PAPER object in login.html). Every ink above is saturated, which is right
     for a bloom that has to carry over a dark or neutral surface; on the pale
     auth/marketing sheets those same inks read as a single coloured ramp and
     lose the dusty, dispersing quality of the reference. Duplicating the
     sheet's own stops means a capsule sitting on the sheet is the same pigment
     as the sheet, not an approximation of it — which is the whole point of
     having one system. Converted from the source hex, not eyeballed. */
  --vds-ink-sky:        oklch(0.777 0.075 246);  --vds-ink-sky-srgb:        143 188 228;  /* #8FBCE4 */
  --vds-ink-lavender:   oklch(0.824 0.068 304);  --vds-ink-lavender-srgb:   207 187 234;  /* #CFBBEA */
  --vds-ink-coral:      oklch(0.752 0.121  37);  --vds-ink-coral-srgb:      240 146 119;  /* #F09277 */
  --vds-ink-cream:      oklch(0.924 0.049  80);  --vds-ink-cream-srgb:      247 227 194;  /* #F7E3C2 */
  --vds-ink-aqua:       oklch(0.851 0.054 214);  --vds-ink-aqua-srgb:       166 216 228;  /* #A6D8E4 */

  /* --- channel triplets ---------------------------------------------------
     Needed wherever a token must be used at variable alpha, e.g.
     rgb(var(--vds-rgb-ink) / 0.1). Kept beside the hex they mirror. */
  --vds-rgb-ink:    22 17 42;      /* --vds-n-900 as channels                */
  --vds-rgb-white:  255 255 255;
  --vds-rgb-black:  0 0 0;
  --vds-rgb-accent: 124 58 237;    /* --vds-v-600 as channels                */
  --vds-rgb-accent-light: 155 124 255;
  --vds-rgb-lattice-light: 124 58 237;
  --vds-rgb-lattice-dark:  200 184 255;


/* ===========================================================================
   TIER 1b — NON-COLOUR PRIMITIVES
   =========================================================================== */

  /* --- type ---------------------------------------------------------------
     THE PAIRING. Mono = Fragment Mono, sans = Plus Jakarta Sans. Both are
     already loaded by index.html and pricing.html today, so the two most
     important pages gain no new network dependency at all.

     >>> Fragment Mono ships ONE weight (400) plus an italic. NEVER set a
     >>> font-weight above 400 on --vds-font-mono. The browser synthesises a
     >>> smeared faux-bold that is obvious at 11px uppercase. Optical weight in
     >>> this system comes from SIZE, TRACKING and COLOUR — never from weight.
     >>> `font-synthesis-weight: none` in the base layer makes the mistake
     >>> visible rather than silently ugly.

     --vds-font-mono-display is the single escape hatch: a page that genuinely
     needs a weighted mono overrides this one token and adds JetBrains Mono to
     its own font link. Do not change the global default for one component. */
  --vds-font-sans: "Plus Jakarta Sans", "DM Sans", -apple-system,
                   BlinkMacSystemFont, "Segoe UI", system-ui, "Helvetica Neue",
                   Arial, sans-serif;
  --vds-font-mono: "Fragment Mono", ui-monospace, SFMono-Regular, "SF Mono",
                   Menlo, Consolas, "Liberation Mono", monospace;
  --vds-font-mono-display: var(--vds-font-mono);

  /* Fluid where it earns it, fixed where a line must not reflow. */
  --vds-text-display: clamp(2.5rem, 1.55rem + 3.6vw, 4.5rem);  /* 40 -> 72   */
  --vds-text-h1:      clamp(2rem,   1.5rem  + 2.0vw, 3rem);    /* 32 -> 48   */
  --vds-text-h2:      clamp(1.5rem, 1.25rem + 1.0vw, 2rem);    /* 24 -> 32   */
  --vds-text-h3:      1.25rem;
  --vds-text-h4:      1.0625rem;
  --vds-text-body:    1rem;
  --vds-text-sm:      0.875rem;
  --vds-text-xs:      0.8125rem;
  --vds-text-micro:   0.6875rem;
  --vds-text-stat:    clamp(1.75rem, 1.3rem + 1.6vw, 2.5rem);

  --vds-leading-tight: 1.06;
  --vds-leading-head:  1.18;
  --vds-leading-snug:  1.4;
  --vds-leading-body:  1.65;

  /* TRACKING. The small-caps treatment is the entire mono voice: as the size
     falls the tracking rises, which is what stops 11px uppercase from setting
     as a grey brick. These three values are the system's signature. */
  --vds-tracking-display: -0.032em;
  --vds-tracking-head:    -0.021em;
  --vds-tracking-body:    -0.004em;
  --vds-tracking-caps:     0.16em;   /* mono uppercase at 13px               */
  --vds-tracking-caps-sm:  0.20em;   /* mono uppercase at 11px               */
  --vds-tracking-caps-xs:  0.26em;   /* mono uppercase at 10-11px, eyebrows  */

  --vds-weight-regular: 400;
  --vds-weight-medium:  500;
  --vds-weight-semi:    600;
  --vds-weight-bold:    700;
  --vds-weight-black:   800;

  /* --- space: 4px base ---------------------------------------------------- */
  --vds-space-0:  0;
  --vds-space-1:  0.25rem;
  --vds-space-2:  0.5rem;
  --vds-space-3:  0.75rem;
  --vds-space-4:  1rem;
  --vds-space-5:  1.5rem;
  --vds-space-6:  2rem;
  --vds-space-7:  3rem;
  --vds-space-8:  4rem;
  --vds-space-9:  6rem;
  --vds-space-10: 8rem;

  /* --- radius -------------------------------------------------------------
     There is a deliberate hole between 20px and the full capsule. "A slightly
     rounder rounded rectangle" is exactly the mush the system exists to stop.
     Either it is a panel or it is a capsule. */
  --vds-radius-xs:   6px;
  --vds-radius-sm:   10px;
  --vds-radius-md:   14px;
  --vds-radius-lg:   20px;
  --vds-radius-pill: 999px;
  --vds-radius-circle: 50%;

  /* --- motion ------------------------------------------------------------- */
  --vds-duration-1: 90ms;    /* colour / opacity on a control                */
  --vds-duration-2: 180ms;   /* the default: transform + shadow on hover     */
  --vds-duration-3: 320ms;   /* entering or leaving                          */
  --vds-duration-4: 620ms;   /* a panel-scale reveal                         */
  /* The dust drift. Was 26s, commented "slow enough to be unnoticed" — which
     was the wrong goal: at 26s with the old keyframe travel the motion was
     below the threshold of perception, so the surface read as a static
     gradient and was repeatedly rejected as exactly that. It is meant to read
     as pigment moving in water. 11s with the wider travel in `vds-drift` is
     legible without becoming a distraction behind a form. */
  --vds-duration-bloom: 11s;

  --vds-ease:      cubic-bezier(0.32, 0.72, 0.24, 1);
  --vds-ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --vds-ease-in:   cubic-bezier(0.7, 0, 0.84, 0);
  --vds-ease-soft: cubic-bezier(0.4, 0, 0.2, 1);

  /* --- layout ------------------------------------------------------------- */
  --vds-measure: 68ch;
  --vds-container: 1200px;
  --vds-container-wide: 1400px;
  --vds-focus-width: 2px;
  --vds-focus-offset: 2px;
  --vds-hairline: 1px;

  /* --- the dither ---------------------------------------------------------
     feTurbulence(fractalNoise) is centred on 0.5 in every channel; the
     feColorMatrix flattens it to neutral grey with alpha 1, and `overlay`
     treats mid-grey as its identity. So this is a genuine ZERO-MEAN dither: it
     breaks the 8-bit quantiser without shifting the average pixel value.
     color-interpolation-filters MUST be sRGB, or the noise is generated in
     linear light and comes out biased dark. It lives here as a token because
     it is a theme asset, and because it keeps the component layer free of
     literals. */
  --vds-grain-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='168' height='168'%3E%3Cfilter id='g' x='0' y='0' width='100%25' height='100%25' color-interpolation-filters='sRGB'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='4' stitchTiles='stitch' seed='11'/%3E%3CfeColorMatrix type='matrix' values='0.34 0.33 0.33 0 0 0.34 0.33 0.33 0 0 0.34 0.33 0.33 0 0 0 0 0 0 1'/%3E%3C/filter%3E%3Crect width='168' height='168' filter='url(%23g)'/%3E%3C/svg%3E");
  --vds-grain-tile: 168px;

  /* --- gaussian falloff ---------------------------------------------------
     alpha(t) ~= exp(-3.4 t^2), as seven fractions plus zero at 100%. The same
     seven numbers as css/vedika-sheet-css.css. Change them and every bloom in
     the system changes shape together — which is exactly the point. */
  --vds-k0: 1;      --vds-s0: 0%;
  --vds-k1: 0.94;   --vds-s1: 14%;
  --vds-k2: 0.80;   --vds-s2: 28%;
  --vds-k3: 0.61;   --vds-s3: 43%;
  --vds-k4: 0.41;   --vds-s4: 58%;
  --vds-k5: 0.24;   --vds-s5: 72%;
  --vds-k6: 0.10;   --vds-s6: 86%;
}


/* ===========================================================================
   TIER 2 — SEMANTIC TOKENS, LIGHT
   ---------------------------------------------------------------------------
   This is the only tier a component may read. Light is the default.

   Contrast ratios in the comments are measured against --vds-surface and are
   verified from sampled pixels by tools/check-tokens.mjs --contrast.
   =========================================================================== */

:root,
.vds-light {
  color-scheme: light;

  /* surfaces, canvas outward */
  --vds-surface-canvas:  var(--vds-n-25);
  --vds-surface:         var(--vds-n-0);
  --vds-surface-raised:  var(--vds-n-0);
  --vds-surface-sunken:  var(--vds-n-50);
  --vds-surface-well:    var(--vds-n-100);
  --vds-surface-inverse: var(--vds-n-950);

  /* text */
  --vds-text-primary:   var(--vds-n-900);   /* 15.4:1 — body, headings       */
  --vds-text-secondary: var(--vds-n-600);   /*  8.4:1 — supporting prose     */
  --vds-text-tertiary:  var(--vds-n-500);   /*  5.9:1 — labels, captions     */
  --vds-text-muted:     var(--vds-n-400);   /*  3.1:1 — LARGE TEXT + ICONS
                                                ONLY. Never body copy.       */
  --vds-text-inverse:   var(--vds-n-25);

  /* borders */
  --vds-border-subtle: rgb(var(--vds-rgb-ink) / 0.10);
  --vds-border:        rgb(var(--vds-rgb-ink) / 0.16);
  --vds-border-strong: rgb(var(--vds-rgb-ink) / 0.30);

  /* accent */
  --vds-accent-primary: var(--vds-v-600);
  --vds-accent-text:    var(--vds-v-700);   /* 6.0:1 — accent AS TEXT        */
  --vds-accent-soft:    var(--vds-v-50);
  --vds-accent-border:  rgb(var(--vds-rgb-accent) / 0.28);
  --vds-on-accent:      var(--vds-n-0);

  /* status */
  --vds-status-ok:        var(--vds-ok-600);
  --vds-status-ok-bg:     var(--vds-ok-50);
  --vds-status-warn:      var(--vds-warn-600);
  --vds-status-warn-bg:   var(--vds-warn-50);
  --vds-status-error:     var(--vds-err-600);
  --vds-status-error-bg:  var(--vds-err-50);
  --vds-status-info:      var(--vds-info-600);
  --vds-status-info-bg:   var(--vds-info-50);

  /* the lattice */
  --vds-lattice-line: rgb(var(--vds-rgb-lattice-light) / 0.055);

  /* --- BLOOM COMPOSITING ---------------------------------------------------
     On white the blooms are INKS: they composite with `multiply`, so magenta
     over yellow really becomes red instead of going grey. That subtractive
     behaviour is the single biggest tell between this and a cheap imitation.

     --vds-bloom-null must ALWAYS be the identity element of --vds-bloom-mode
     (white for multiply, black for screen). The bloom's clearing layer is
     painted in that colour, which is how it erases ink and lets raw surface
     show through under label text. These two tokens move together. Setting
     one without the other silently breaks every contrast guarantee. */
  --vds-bloom-mode:     multiply;
  --vds-bloom-null:     var(--vds-rgb-white);
  --vds-bloom-strength: 1;

  /* shadows */
  --vds-shadow-color: var(--vds-rgb-ink);
  --vds-shadow-1: 0 1px 2px rgb(var(--vds-shadow-color) / 0.06),
                  0 1px 1px rgb(var(--vds-shadow-color) / 0.04);
  --vds-shadow-2: 0 1px 2px rgb(var(--vds-shadow-color) / 0.05),
                  0 8px 20px -10px rgb(var(--vds-shadow-color) / 0.16);
  --vds-shadow-3: 0 1px 2px rgb(var(--vds-shadow-color) / 0.05),
                  0 22px 48px -22px rgb(var(--vds-shadow-color) / 0.26);
  /* "floating on a soft outer shadow" — wide, low, no spread */
  --vds-shadow-float: 0 1px 1px rgb(var(--vds-shadow-color) / 0.05),
                      0 4px 10px -4px rgb(var(--vds-shadow-color) / 0.14),
                      0 18px 40px -18px rgb(var(--vds-shadow-color) / 0.28);
  --vds-shadow-lift:  0 1px 1px rgb(var(--vds-shadow-color) / 0.05),
                      0 8px 18px -6px rgb(var(--vds-shadow-color) / 0.16),
                      0 30px 60px -22px rgb(var(--vds-shadow-color) / 0.32);
  /* the inner hairline that makes a light capsule read as an object */
  --vds-shadow-edge: inset 0 1px 0 rgb(var(--vds-rgb-white) / 0.9);
  --vds-shadow-none: none;
}


/* ===========================================================================
   TIER 2 — SEMANTIC TOKENS, DARK
   ---------------------------------------------------------------------------
   Not an inversion. Dark surfaces step UP in lightness as they come forward,
   shadows do almost nothing, and what separates planes is a top inner
   highlight instead.

   Dark is EXPLICIT, never inferred from the OS, because Vedika has both light
   pages (pricing, docs) and dark pages (the index hero, the dashboard) and the
   operating system cannot know which is which. A page that wants to follow the
   OS opts in with data-vds-theme="auto".
   =========================================================================== */

[data-vds-theme="dark"],
.vds-dark {
  color-scheme: dark;

  --vds-surface-canvas:  var(--vds-n-950);
  --vds-surface:         var(--vds-d-surface);
  --vds-surface-raised:  var(--vds-d-surface-2);
  --vds-surface-sunken:  var(--vds-d-surface-2);
  --vds-surface-well:    var(--vds-d-surface-3);
  --vds-surface-inverse: var(--vds-n-25);

  --vds-text-primary:   var(--vds-d-text);     /* 16.1:1                     */
  --vds-text-secondary: var(--vds-d-text-2);   /*  8.3:1                     */
  --vds-text-tertiary:  var(--vds-d-text-3);   /*  5.6:1                     */
  --vds-text-muted:     var(--vds-d-text-4);   /*  3.0:1 — LARGE + ICONS ONLY*/
  --vds-text-inverse:   var(--vds-n-900);

  --vds-border-subtle: rgb(var(--vds-rgb-lattice-dark) / 0.12);
  --vds-border:        rgb(var(--vds-rgb-lattice-dark) / 0.20);
  --vds-border-strong: rgb(var(--vds-rgb-lattice-dark) / 0.36);

  --vds-accent-primary: var(--vds-v-500);
  --vds-accent-text:    var(--vds-v-300);      /*  8.1:1 on --vds-surface    */
  --vds-accent-soft:    rgb(var(--vds-rgb-accent-light) / 0.14);
  --vds-accent-border:  rgb(var(--vds-rgb-accent-light) / 0.38);
  --vds-on-accent:      var(--vds-n-950);

  /* status hues are re-pointed, not re-tinted by a component, so they stay
     above 4.5:1 on a dark surface. This is the semantic layer doing its job. */
  --vds-status-ok:       var(--vds-ok-300);
  --vds-status-ok-bg:    rgb(15 157 110 / 0.16);
  --vds-status-warn:     var(--vds-warn-300);
  --vds-status-warn-bg:  rgb(180 83 9 / 0.20);
  --vds-status-error:    var(--vds-err-300);
  --vds-status-error-bg: rgb(208 42 74 / 0.20);
  --vds-status-info:     var(--vds-info-300);
  --vds-status-info-bg:  rgb(37 99 235 / 0.20);

  --vds-lattice-line: rgb(var(--vds-rgb-lattice-dark) / 0.10);

  /* On a near-black surface an ink multiply is a no-op — the bloom would
     simply not exist. Light is ADDITIVE here: `screen`, whose identity element
     is black. Note --vds-bloom-null moves with it, as required above. */
  --vds-bloom-mode:     screen;
  --vds-bloom-null:     var(--vds-rgb-black);
  --vds-bloom-strength: 1.18;

  --vds-shadow-color: var(--vds-rgb-black);
  --vds-shadow-1: 0 1px 2px rgb(var(--vds-shadow-color) / 0.4);
  --vds-shadow-2: 0 2px 4px rgb(var(--vds-shadow-color) / 0.4),
                  0 10px 24px -12px rgb(var(--vds-shadow-color) / 0.6);
  --vds-shadow-3: 0 2px 4px rgb(var(--vds-shadow-color) / 0.4),
                  0 26px 54px -24px rgb(var(--vds-shadow-color) / 0.7);
  --vds-shadow-float: 0 2px 4px rgb(var(--vds-shadow-color) / 0.42),
                      0 20px 44px -20px rgb(var(--vds-shadow-color) / 0.66);
  --vds-shadow-lift:  0 2px 4px rgb(var(--vds-shadow-color) / 0.42),
                      0 32px 64px -24px rgb(var(--vds-shadow-color) / 0.74);
  --vds-shadow-edge: inset 0 1px 0 rgb(var(--vds-rgb-white) / 0.08);
}

/* Opt-in OS following, for pages that genuinely have no fixed identity. */
@media (prefers-color-scheme: dark) {
  [data-vds-theme="auto"] {
    color-scheme: dark;
    --vds-surface-canvas:  var(--vds-n-950);
    --vds-surface:         var(--vds-d-surface);
    --vds-surface-raised:  var(--vds-d-surface-2);
    --vds-surface-sunken:  var(--vds-d-surface-2);
    --vds-surface-well:    var(--vds-d-surface-3);
    --vds-surface-inverse: var(--vds-n-25);
    --vds-text-primary:   var(--vds-d-text);
    --vds-text-secondary: var(--vds-d-text-2);
    --vds-text-tertiary:  var(--vds-d-text-3);
    --vds-text-muted:     var(--vds-d-text-4);
    --vds-text-inverse:   var(--vds-n-900);
    --vds-border-subtle: rgb(var(--vds-rgb-lattice-dark) / 0.12);
    --vds-border:        rgb(var(--vds-rgb-lattice-dark) / 0.20);
    --vds-border-strong: rgb(var(--vds-rgb-lattice-dark) / 0.36);
    --vds-accent-primary: var(--vds-v-500);
    --vds-accent-text:    var(--vds-v-300);
    --vds-accent-soft:    rgb(var(--vds-rgb-accent-light) / 0.14);
    --vds-accent-border:  rgb(var(--vds-rgb-accent-light) / 0.38);
    --vds-on-accent:      var(--vds-n-950);
    --vds-status-ok:       var(--vds-ok-300);
    --vds-status-ok-bg:    rgb(15 157 110 / 0.16);
    --vds-status-warn:     var(--vds-warn-300);
    --vds-status-warn-bg:  rgb(180 83 9 / 0.20);
    --vds-status-error:    var(--vds-err-300);
    --vds-status-error-bg: rgb(208 42 74 / 0.20);
    --vds-status-info:     var(--vds-info-300);
    --vds-status-info-bg:  rgb(37 99 235 / 0.20);
    --vds-lattice-line: rgb(var(--vds-rgb-lattice-dark) / 0.10);
    --vds-bloom-mode:     screen;
    --vds-bloom-null:     var(--vds-rgb-black);
    --vds-bloom-strength: 1.18;
    --vds-shadow-color: var(--vds-rgb-black);
    --vds-shadow-edge: inset 0 1px 0 rgb(var(--vds-rgb-white) / 0.08);
  }
}


/* ===========================================================================
   TIER 2b — BLOOM COLOURWAYS
   ---------------------------------------------------------------------------
   A colourway is three inks plus their sRGB fallbacks. Nothing else. Adding a
   fourth colourway is six lines here and zero lines anywhere else.

   RESTRAINT: --violet is the default and should be the answer almost always.
   The other three exist for a specific, single, page-level moment. A page
   where every button blooms is worse than a page with one that does.
   =========================================================================== */

:root {
  /* Vedika's own register — THE DEFAULT */
  --vds-bloom-ink-1: var(--vds-ink-brand);
  --vds-bloom-ink-2: var(--vds-ink-periwinkle);
  --vds-bloom-ink-3: var(--vds-ink-lilac);
  --vds-bloom-srgb-1: var(--vds-ink-brand-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-periwinkle-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-lilac-srgb);
}

/* magenta -> orange -> gold. Reserve for the single highest-intent action on
   a page: Get API keys, Subscribe, Start building. */
.vds-bloom--ember {
  --vds-bloom-ink-1: var(--vds-ink-magenta);
  --vds-bloom-ink-2: var(--vds-ink-orange);
  --vds-bloom-ink-3: var(--vds-ink-gold);
  --vds-bloom-srgb-1: var(--vds-ink-magenta-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-orange-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-gold-srgb);
}

/* cyan -> azure -> violet. Informational, or secondary-but-important. */
.vds-bloom--tide {
  --vds-bloom-ink-1: var(--vds-ink-cyan);
  --vds-bloom-ink-2: var(--vds-ink-azure);
  --vds-bloom-ink-3: var(--vds-ink-violet);
  --vds-bloom-srgb-1: var(--vds-ink-cyan-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-azure-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-violet-srgb);
}

/* gold -> lime -> mint. Success, shipped, live. NEVER a call to action. */
.vds-bloom--solar {
  --vds-bloom-ink-1: var(--vds-ink-gold);
  --vds-bloom-ink-2: var(--vds-ink-lime);
  --vds-bloom-ink-3: var(--vds-ink-mint);
  --vds-bloom-srgb-1: var(--vds-ink-gold-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-lime-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-mint-srgb);
}

/* sky -> lavender -> coral. The WebGL sheet's own pigment, for a capsule that
   sits ON that sheet — auth, pricing, any pale full-bleed surface. Three
   genuinely separate hues (blue, violet, orange) is the point: the default
   violet register is brand/periwinkle/lilac, which are close enough in hue
   that the three fronds merge into one smooth ramp and read as a plain CSS
   gradient rather than dispersing dust. */
.vds-bloom--paper {
  --vds-bloom-ink-1: var(--vds-ink-sky);
  --vds-bloom-ink-2: var(--vds-ink-lavender);
  --vds-bloom-ink-3: var(--vds-ink-coral);
  --vds-bloom-srgb-1: var(--vds-ink-sky-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-lavender-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-coral-srgb);
  /* these inks are pale by construction, so they need more of themselves to
     register at all against a white capsule */
  --vds-bloom-strength: 1.35;
}

/* --- ANALOGOUS colourways, for small surfaces -------------------------------
   Added 2026-07-29 after `--paper` was rejected as muddy on buttons.

   The cause is hue span, not intensity. `--paper` runs sky (hue 246) ->
   lavender (304) -> coral (37): 267 degrees of the wheel, with sky and coral
   effectively complementary. The bloom composites its three fronds with
   `multiply`, and multiplying complementary hues drives the overlap toward
   NEUTRAL — so wherever two fronds meet you get tan or grey, not colour. It
   was measured: sweeping alpha 0.50 -> 1.20 and strength 1.35 -> 2.60 moved
   saturation p95 only 0.750 -> 0.786 and pure-white area not at all. There is
   no setting of an intensity dial that fixes a hue-geometry problem.

   On a large field (the page sheet, a full panel) that span is a virtue: the
   fronds are far apart, they rarely overlap, and the wide gamut is what makes
   the poster read as a poster. On a 400x50 capsule all three fronds sit on top
   of each other, so the overlap IS the button.

   Hence two ANALOGOUS registers, each staying inside roughly 60 degrees, for
   anything small. The reference the founder supplied works this way too: each
   of its capsules is white-to-one-hue-family, never a full spectrum. */

/* sky -> aqua -> azure. Hues 246 / 214 / 250. Cool, and the default for a
   capsule that must not compete with a warm page. */
.vds-bloom--sky {
  --vds-bloom-ink-1: var(--vds-ink-sky);
  --vds-bloom-ink-2: var(--vds-ink-aqua);
  --vds-bloom-ink-3: var(--vds-ink-azure);
  --vds-bloom-srgb-1: var(--vds-ink-sky-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-aqua-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-azure-srgb);
  --vds-bloom-strength: 1.5;
}

/* cream -> coral -> gold. Hues 80 / 37 / 96. The warm counterpart. */
.vds-bloom--dawn {
  --vds-bloom-ink-1: var(--vds-ink-cream);
  --vds-bloom-ink-2: var(--vds-ink-coral);
  --vds-bloom-ink-3: var(--vds-ink-gold);
  --vds-bloom-srgb-1: var(--vds-ink-cream-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-coral-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-gold-srgb);
  --vds-bloom-strength: 1.5;
}

/* explicit default, for overriding a colourway back on a nested element */
.vds-bloom--violet {
  --vds-bloom-ink-1: var(--vds-ink-brand);
  --vds-bloom-ink-2: var(--vds-ink-periwinkle);
  --vds-bloom-ink-3: var(--vds-ink-lilac);
  --vds-bloom-srgb-1: var(--vds-ink-brand-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-periwinkle-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-lilac-srgb);
}

/* ===========================================================================
   TIER 2c — RESPONSIVE CONTRACT
   ---------------------------------------------------------------------------
   THERE IS NO MOBILE STYLESHEET. If you find yourself writing one, the
   component is wrong.

   Mobile goes stale precisely BECAUSE it is a separate artefact. Two files
   that must change in lockstep never do. So the system removes the second
   artefact rather than trying to keep it in sync:

     * FLUID BY DEFAULT. --vds-text-* and the fluid space steps are clamp().
       A token that interpolates continuously has no mobile value to forget,
       because there is only one value.

     * CONTAINER QUERIES, NOT VIEWPORT QUERIES, for components. A card
       responds to the width of its container, not the screen. That is what
       makes one component correct in a sidebar, in a grid cell and full-bleed
       without three sets of overrides.

     * INTRINSIC LAYOUT. Grids are repeat(auto-fit, minmax(...)) and flex rows
       wrap. A grid built this way has no mobile variant to forget.

     * OVERFLOW IS IMPOSSIBLE BY CONSTRUCTION, not by patch. See the four
       tokens below and the base layer that applies them.

   These tokens are named for machine consumption: the cross-page responsive
   audit tool reads --vds-tap-min and --vds-measure to know what the system
   guarantees. Do not rename them without telling that tool's owner.
   =========================================================================== */

:root {
  /* --- tap targets -------------------------------------------------------
     WCAG 2.2 AA success criterion 2.5.8 (Target Size, Minimum) is 24x24 CSS
     px. --vds-tap-min is the FLOOR: no interactive component in the system may
     compute smaller, and the audit tool asserts it. --vds-tap-comfortable is
     the 44px figure from the Apple/Android guidance and is what primary
     controls actually use. */
  --vds-tap-min: 24px;
  --vds-tap-comfortable: 44px;

  /* --- the grid floor ----------------------------------------------------
     THE most important token for mobile correctness. Auto-fit grids must be
     written minmax(min(100%, var(--vds-col-min)), 1fr).

     Without the min(100%, ...) wrapper a 260px track floor plus a gap
     overflows a 320px container — and that single omission is the cause of
     most "text overflowing the grid on mobile" reports. With it, the track
     collapses to the container width and the grid simply becomes one column.
     There is no breakpoint involved and nothing to keep in sync. */
  --vds-col-min: 260px;
  --vds-col-min-sm: 180px;
  --vds-col-min-lg: 340px;

  /* --- measures ----------------------------------------------------------
     Maximum prose line length. The audit tool reads --vds-measure. */
  --vds-measure-narrow: 48ch;
  /* --vds-measure (68ch) is defined in tier 1b */

  /* --- fluid space -------------------------------------------------------
     Section rhythm that interpolates instead of stepping. A page using these
     needs no @media rule to feel right at 390px and at 2560px. */
  --vds-space-section: clamp(3rem, 1.6rem + 6vw, 8rem);
  --vds-space-gutter:  clamp(1rem, 0.4rem + 2.6vw, 3rem);
  --vds-space-gap:     clamp(1rem, 0.7rem + 1.2vw, 1.5rem);

  /* --- container-query breakpoints ---------------------------------------
     Named steps so every component asks the same questions of its container.
     CSS does not allow a custom property inside a @container condition, so
     these are documentation, not substitution — but keeping the list here is
     what stops six components inventing six different numbers.

       vds-cq-sm   24rem / 384px   stack, single column
       vds-cq-md   34rem / 544px   two-up becomes viable
       vds-cq-lg   48rem / 768px   side-by-side with a rail
       vds-cq-xl   64rem / 1024px  full composition

     Viewport @media is reserved for exactly two things: page chrome that has
     no container (the nav), and prefers-reduced-motion. Everything else uses
     @container. */
}

} /* @layer vds.tokens */

/* ===========================================================================
   TIER 2d — THE SQUARE EDGE  (appended 2026-07-29, pricing/cardgrid lane)
   ---------------------------------------------------------------------------
   The radius scale above deliberately has a hole between 20px and the capsule,
   on the grounds that "a slightly rounder rounded rectangle" is mush. It also
   had no way to say NOT ROUNDED AT ALL, so the only way to draw a crisp
   rectangular panel was an inline `border-radius: 0`, i.e. a literal outside
   the system. These two entries close that end of the scale.

     --vds-radius-none  a true rectangle. Grid panels, ruled tables, anything
                        whose job is to read as a division of a plane rather
                        than as an object sitting on one.
     --vds-radius-edge  1px. Not a rounded corner — an anti-alias on the
                        corner pixel, for a large dark panel where a perfectly
                        square corner reads as a printing error.

   Nothing above is renamed or re-valued; these are additive.
   =========================================================================== */

@layer vds.tokens {
  :root {
    --vds-radius-none: 0px;
    --vds-radius-edge: 1px;
  }
}

/* ===========================================================================
   TIER 2e — THE FOURTH ANALOGOUS REGISTER  (appended 2026-07-29, pricing lane)
   ---------------------------------------------------------------------------
   The analogous set added earlier today gave small surfaces two registers,
   --sky (cool) and --dawn (warm). A row of FOUR price tiers needs four, so
   that each tier owns a hue and the reader gets help ranking them; with only
   two, two cards have to share and the row reads as pairs.

   --violet is the third and already qualifies: brand 296 / periwinkle 272 /
   lilac 315 is a 43-degree span, comfortably inside the ~60-degree rule. So
   only ONE new colourway is needed.

   --indigo is azure 250 / periwinkle 272 / brand 296: a 46-degree span, and
   deliberately the DEEPEST of the four. It is distinct from --sky (which is
   pale, 214-250) and from --violet (which is pinker, 272-315) while sharing an
   edge with both, so the four registers form a continuous ladder rather than
   four unrelated accidents:

       --sky     214-250   palest, coolest      entry tier
       --dawn     37- 96   the one warm one     the tier being recommended
       --violet  272-315   the house register   mid tier
       --indigo  250-296   deepest, most sober  top tier

   Same construction as every other colourway: three inks, three sRGB
   fallbacks, one strength. Nothing above is renamed or re-valued.
   =========================================================================== */

.vds-bloom--indigo {
  --vds-bloom-ink-1: var(--vds-ink-azure);
  --vds-bloom-ink-2: var(--vds-ink-periwinkle);
  --vds-bloom-ink-3: var(--vds-ink-brand);
  --vds-bloom-srgb-1: var(--vds-ink-azure-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-periwinkle-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-brand-srgb);
  /* these three inks are the most saturated of the four registers, so they
     need LESS of themselves than the pale --sky/--dawn pair to read at the
     same weight on a white card */
  --vds-bloom-strength: 1.15;
}

/* ===========================================================================
   TIER 2f — THE TEN-REGISTER ANALOGOUS SCALE
   (appended 2026-07-29, reviews / use-cases / case-studies lane)
   ---------------------------------------------------------------------------
   WHY TEN, AND WHY A SCALE RATHER THAN TEN PICKS.

   A set of review cards, or a grid of 22 use cases, needs every card in the
   set to differ while the set still reads as one thing. Two registers (--sky,
   --dawn) forces cards to share; four (+ --violet, --indigo) still does at
   this count. Ten arbitrary swatches would solve the sharing and lose the
   coherence — which is the worse failure, because a swatch dump reads as a
   mistake and a shared hue only reads as a repeat.

   So this is a SCALE, not a palette: ten registers whose centre hues are 36
   degrees apart, covering the wheel exactly once. Every register is built by
   the same rule, from the same three lightness/chroma pairs, so they are
   siblings by construction rather than by taste.

   THE ANALOGOUS RULE IS LOAD-BEARING — see TIER 2b. The bloom composites its
   three fronds with `multiply`, and multiplying colours far apart on the wheel
   drives their overlap toward NEUTRAL. --paper spans 267 degrees and that is
   the entire cause of the "dull" complaint on this project; it was measured,
   and sweeping alpha 0.50->1.20 and strength 1.35->2.60 moved saturation p95
   only 0.750->0.786. Intensity cannot fix a hue-geometry problem.

   Every register here therefore spans exactly 36 degrees — its three fronds
   sit at centre-18, centre, centre+18 — which is inside the ~60-degree rule
   with room to spare. Ten registers x 36 degrees each, spaced 36 apart, is
   why adjacent registers share an edge frond: --rose-hi is hue 30 and
   --clay-lo is hue 30. The ladder is continuous, so two neighbouring cards
   never collide and never look unrelated.

   THE THREE FRONDS ARE NOT THE SAME COLOUR AT THREE HUES. They differ in
   lightness and chroma as well:

       -lo   L 0.845  C 0.098   hue -18
       (mid) L 0.884  C 0.072   hue  0
       -hi   L 0.802  C 0.118   hue +18

   Three inks at identical L and C merge into one smooth ramp and read as a
   plain CSS gradient — the exact thing the bloom is not supposed to look
   like. The lightness split is what keeps the fronds legible as separate
   pigment.

   CONVERTED, NOT EYEBALLED. Every sRGB triplet below is the machine
   conversion of the OKLCH beside it (Oklab -> LMS -> linear sRGB -> sRGB
   transfer), the same method used for the --paper register. Where the
   authored chroma fell outside sRGB the chroma was BISECTED DOWN until it
   fit, and the reduced value was written back into the OKLCH — so the two
   notations always describe the same colour and the @supports fallback in
   css/vedika-ds.css cannot drift from the modern path. Twelve of the thirty
   fronds were clamped this way; they are the pinks and the blues, whose
   gamut is narrowest at these lightnesses.

   STRENGTH COMPENSATES FOR THAT CLAMP. A register that lost chroma to the
   gamut would otherwise read weaker than its neighbours at the same alpha,
   which would break the "one scale" reading far more visibly than a hue
   difference. --vds-bloom-strength is therefore set per register, inversely
   to the chroma the register actually achieved, normalised on the median.
   The spread is 1.42 to 1.70 and it is arithmetic, not taste.

   HUE CARRIES MEANING. Each register is named for what it is FOR, and the
   pages assign it by subject rather than by rotation order: matching is
   --rose everywhere, commerce is --clay everywhere, AI is --iris everywhere.
   A reader who notices the colour at all learns something from it.

       --rose      hue  12   matching, marriage, compatibility, dating
       --clay      hue  48   commerce, gemstones, physical goods
       --sand      hue  84   panchang, calendar, muhurta, timing
       --leaf      hue 120   health, wellness, ayurveda
       --jade      hue 156   career, prosperity, growth, fintech
       --lagoon    hue 192   media, publishing, daily content
       --cerulean  hue 228   developer tooling, SDKs, app builds
       --iris      hue 264   AI, chatbots, natural language
       --orchid    hue 300   astrologer platforms, consultation, jyotish
       --plum      hue 336   naming, numerology, identity

   These are PALE by construction (L 0.80-0.88), because their job is a wash
   rising behind body copy on a white card, not a saturated capsule. Nothing
   above is renamed or re-valued; all of this is additive.
   =========================================================================== */

@layer vds.tokens {
  :root {
    --vds-ink-rose-lo:      oklch(0.845 0.097 354);  --vds-ink-rose-lo-srgb:      255 178 208;
    --vds-ink-rose:         oklch(0.884 0.062  12);  --vds-ink-rose-srgb:         255 201 206;
    --vds-ink-rose-hi:      oklch(0.802 0.114  30);  --vds-ink-rose-hi-srgb:      255 162 147;

    --vds-ink-clay-lo:      oklch(0.845 0.085  30);  --vds-ink-clay-lo-srgb:      255 184 171;
    --vds-ink-clay:         oklch(0.884 0.067  48);  --vds-ink-clay-srgb:         255 204 179;
    --vds-ink-clay-hi:      oklch(0.802 0.118  66);  --vds-ink-clay-hi-srgb:      242 175 104;

    --vds-ink-sand-lo:      oklch(0.845 0.098  66);  --vds-ink-sand-lo-srgb:      248 192 135;
    --vds-ink-sand:         oklch(0.884 0.072  84);  --vds-ink-sand-srgb:         239 214 163;
    --vds-ink-sand-hi:      oklch(0.802 0.118 102);  --vds-ink-sand-hi-srgb:      205 193  98;

    --vds-ink-leaf-lo:      oklch(0.845 0.098 102);  --vds-ink-leaf-lo-srgb:      217 207 130;
    --vds-ink-leaf:         oklch(0.884 0.072 120);  --vds-ink-leaf-srgb:         210 224 171;
    --vds-ink-leaf-hi:      oklch(0.802 0.118 138);  --vds-ink-leaf-hi-srgb:      152 209 133;

    --vds-ink-jade-lo:      oklch(0.845 0.098 138);  --vds-ink-jade-lo-srgb:      173 220 157;
    --vds-ink-jade:         oklch(0.884 0.072 156);  --vds-ink-jade-srgb:         179 232 197;
    --vds-ink-jade-hi:      oklch(0.802 0.118 174);  --vds-ink-jade-hi-srgb:       94 215 185;

    --vds-ink-lagoon-lo:    oklch(0.845 0.098 174);  --vds-ink-lagoon-lo-srgb:    131 225 199;
    --vds-ink-lagoon:       oklch(0.884 0.072 192);  --vds-ink-lagoon-srgb:       160 233 229;
    --vds-ink-lagoon-hi:    oklch(0.802 0.118 210);  --vds-ink-lagoon-hi-srgb:     76 211 232;

    --vds-ink-cerulean-lo:  oklch(0.845 0.098 210);  --vds-ink-cerulean-lo-srgb:  120 222 239;
    --vds-ink-cerulean:     oklch(0.884 0.072 228);  --vds-ink-cerulean-srgb:     167 227 255;
    --vds-ink-cerulean-hi:  oklch(0.802 0.106 246);  --vds-ink-cerulean-hi-srgb:  131 197 255;

    --vds-ink-iris-lo:      oklch(0.845 0.082 246);  --vds-ink-iris-lo-srgb:      160 210 255;
    --vds-ink-iris:         oklch(0.884 0.056 264);  --vds-ink-iris-srgb:         198 217 255;
    --vds-ink-iris-hi:      oklch(0.802 0.102 282);  --vds-ink-iris-hi-srgb:      180 182 255;

    --vds-ink-orchid-lo:    oklch(0.845 0.079 282);  --vds-ink-orchid-lo-srgb:    195 199 255;
    --vds-ink-orchid:       oklch(0.884 0.067 300);  --vds-ink-orchid-srgb:       223 208 255;
    --vds-ink-orchid-hi:    oklch(0.802 0.118 318);  --vds-ink-orchid-hi-srgb:    222 166 240;

    --vds-ink-plum-lo:      oklch(0.845 0.098 318);  --vds-ink-plum-lo-srgb:      231 184 246;
    --vds-ink-plum:         oklch(0.884 0.072 336);  --vds-ink-plum-srgb:         248 199 235;
    --vds-ink-plum-hi:      oklch(0.802 0.118 354);  --vds-ink-plum-hi-srgb:      250 158 196;
  }
}

/* Same six-line construction as every colourway above: three inks, three sRGB
   fallbacks, one strength. Nothing else. */

.vds-bloom--rose {
  --vds-bloom-ink-1: var(--vds-ink-rose-lo);
  --vds-bloom-ink-2: var(--vds-ink-rose);
  --vds-bloom-ink-3: var(--vds-ink-rose-hi);
  --vds-bloom-srgb-1: var(--vds-ink-rose-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-rose-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-rose-hi-srgb);
  --vds-bloom-strength: 1.50;
}

.vds-bloom--clay {
  --vds-bloom-ink-1: var(--vds-ink-clay-lo);
  --vds-bloom-ink-2: var(--vds-ink-clay);
  --vds-bloom-ink-3: var(--vds-ink-clay-hi);
  --vds-bloom-srgb-1: var(--vds-ink-clay-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-clay-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-clay-hi-srgb);
  --vds-bloom-strength: 1.51;
}

.vds-bloom--sand {
  --vds-bloom-ink-1: var(--vds-ink-sand-lo);
  --vds-bloom-ink-2: var(--vds-ink-sand);
  --vds-bloom-ink-3: var(--vds-ink-sand-hi);
  --vds-bloom-srgb-1: var(--vds-ink-sand-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-sand-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-sand-hi-srgb);
  --vds-bloom-strength: 1.42;
}

.vds-bloom--leaf {
  --vds-bloom-ink-1: var(--vds-ink-leaf-lo);
  --vds-bloom-ink-2: var(--vds-ink-leaf);
  --vds-bloom-ink-3: var(--vds-ink-leaf-hi);
  --vds-bloom-srgb-1: var(--vds-ink-leaf-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-leaf-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-leaf-hi-srgb);
  --vds-bloom-strength: 1.42;
}

.vds-bloom--jade {
  --vds-bloom-ink-1: var(--vds-ink-jade-lo);
  --vds-bloom-ink-2: var(--vds-ink-jade);
  --vds-bloom-ink-3: var(--vds-ink-jade-hi);
  --vds-bloom-srgb-1: var(--vds-ink-jade-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-jade-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-jade-hi-srgb);
  --vds-bloom-strength: 1.42;
}

.vds-bloom--lagoon {
  --vds-bloom-ink-1: var(--vds-ink-lagoon-lo);
  --vds-bloom-ink-2: var(--vds-ink-lagoon);
  --vds-bloom-ink-3: var(--vds-ink-lagoon-hi);
  --vds-bloom-srgb-1: var(--vds-ink-lagoon-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-lagoon-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-lagoon-hi-srgb);
  --vds-bloom-strength: 1.42;
}

.vds-bloom--cerulean {
  --vds-bloom-ink-1: var(--vds-ink-cerulean-lo);
  --vds-bloom-ink-2: var(--vds-ink-cerulean);
  --vds-bloom-ink-3: var(--vds-ink-cerulean-hi);
  --vds-bloom-srgb-1: var(--vds-ink-cerulean-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-cerulean-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-cerulean-hi-srgb);
  --vds-bloom-strength: 1.48;
}

.vds-bloom--iris {
  --vds-bloom-ink-1: var(--vds-ink-iris-lo);
  --vds-bloom-ink-2: var(--vds-ink-iris);
  --vds-bloom-ink-3: var(--vds-ink-iris-hi);
  --vds-bloom-srgb-1: var(--vds-ink-iris-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-iris-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-iris-hi-srgb);
  /* the narrowest gamut of the ten at these lightnesses, so the most
     compensation: mean achieved chroma 0.080 against a median of 0.094 */
  --vds-bloom-strength: 1.70;
}

.vds-bloom--orchid {
  --vds-bloom-ink-1: var(--vds-ink-orchid-lo);
  --vds-bloom-ink-2: var(--vds-ink-orchid);
  --vds-bloom-ink-3: var(--vds-ink-orchid-hi);
  --vds-bloom-srgb-1: var(--vds-ink-orchid-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-orchid-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-orchid-hi-srgb);
  --vds-bloom-strength: 1.55;
}

.vds-bloom--plum {
  --vds-bloom-ink-1: var(--vds-ink-plum-lo);
  --vds-bloom-ink-2: var(--vds-ink-plum);
  --vds-bloom-ink-3: var(--vds-ink-plum-hi);
  --vds-bloom-srgb-1: var(--vds-ink-plum-lo-srgb);
  --vds-bloom-srgb-2: var(--vds-ink-plum-srgb);
  --vds-bloom-srgb-3: var(--vds-ink-plum-hi-srgb);
  --vds-bloom-strength: 1.42;
}

/* ===========================================================================
   TIER 1c — THE DISPLAY FACE  (appended 2026-07-29, reviews lane)
   ---------------------------------------------------------------------------
   The type tier above names two roles, mono and sans, and the system has been
   running a THIRD one on pages for weeks without a token for it: Newsreader,
   the serif that carries display type on the book page, on pricing and on
   home. Every page that wanted it declared its own `h1 { font-family:
   'Newsreader', ... }` in a page <style>, which is precisely the drift the
   token layer exists to stop — six pages, six fallback stacks.

     --vds-font-display   Newsreader. Headings, pull quotes, plan names.
     --vds-font-sans      Plus Jakarta Sans / DM Sans. Reading.
     --vds-font-mono      Fragment Mono, weight 400 ONLY. Labels.

   Newsreader ships 300/400/500 plus an italic, so unlike the mono it may take
   a weight — but authority at display size comes from SIZE, not weight, and
   400 is the default for a reason. The page still has to LOAD the face; this
   token only fixes what it is called and what it degrades to.
   =========================================================================== */

@layer vds.tokens {
  :root {
    --vds-font-display: "Newsreader", Georgia, "Iowan Old Style",
                        "Times New Roman", Times, serif;
  }
}


/* ===========================================================================
   TIER 1d — THE POSTER REGISTER  (appended 2026-07-29, chatbot-API lane)
   ---------------------------------------------------------------------------
   Three inks and one paper, added for `.vds-comb` (see css/vedika-ds.css) and
   for any surface that wants the exhibition-poster reading: cream stock, one
   hue per sheet, nothing else.

   WHY THREE NEW INKS AND NOT THE EXISTING ONES. The bloom inks above are tuned
   to carry over white or near-black at high chroma. Printed on cream at poster
   scale they fail in two different ways at once: --vds-ink-gold sits at
   L 0.905, which is 2.0:1 against the paper and simply is not there, while
   --vds-ink-coral and --vds-ink-mint sit 0.16 apart in lightness, so a row of
   three sheets reads as one loud, one quiet and one missing rather than as
   three equals.

   These three are therefore chosen on ONE constraint: matched L. All three sit
   at L 0.590-0.620 and clear 3:1 against --vds-paper-cream, so the three
   sheets in a row carry the same optical weight and each is legible as a
   graphic on its own. Chroma is then set per hue to what that hue can actually
   hold at that lightness.

     terracotta  oklch(0.620 0.135  42)  #C8673F   3.35:1 on cream
     saffron     oklch(0.620 0.128  78)  #B07B05   3.22:1 on cream
     teal        oklch(0.590 0.085 202)  #308C93   3.46:1 on cream

   The sRGB triplets are the OKLCH values run through the standard
   OKLab -> linear sRGB matrix and the sRGB transfer function, not eyeballed —
   same rule as the paper register above.

   ONE HUE PER SURFACE. These are deliberately NOT offered as a bloom colourway
   triple. The bloom composites its fronds with `multiply`, and the whole
   lesson recorded against `--paper` above is that a wide hue span drives its
   own overlaps to grey. A comb has no overlaps, because it is one ink; that is
   why it can be this saturated and still read as colour.
   =========================================================================== */

@layer vds.tokens {
  :root {
    /* --- poster inks ---------------------------------------------------- */
    --vds-ink-terracotta: oklch(0.620 0.135  42);  --vds-ink-terracotta-srgb: 200 103  63;  /* #C8673F */
    --vds-ink-saffron:    oklch(0.620 0.128  78);  --vds-ink-saffron-srgb:    176 123   5;  /* #B07B05 */
    --vds-ink-teal:       oklch(0.590 0.085 202);  --vds-ink-teal-srgb:        48 140 147;  /* #308C93 */

    /* --- the paper -------------------------------------------------------
       Warm, slightly yellow-cast stock. Held apart from --vds-surface (pure
       white) on purpose: a poster on white is a card, a poster on cream is a
       printed sheet, and that difference is the entire reference. Measured
       against it: --vds-text-primary 15.96:1, --vds-text-secondary 7.03:1,
       --vds-text-tertiary 4.77:1 — every text token stays AA on this ground. */
    --vds-paper-cream:      #f4efe4;
    --vds-paper-cream-deep: #e9e3d4;   /* the wall the sheets are pinned to */

    --vds-surface-paper:      var(--vds-paper-cream);
    --vds-surface-paper-deep: var(--vds-paper-cream-deep);
  }
}

/* Dark: the sheet stops being paper and becomes a lit panel, and the inks step
   up in lightness to hold the same 3:1 against it. Declared here so a surface
   that adopts --vds-surface-paper is not silently broken in dark mode. */
@layer vds.tokens {
  :where([data-vds-theme="dark"], .vds-dark) {
    --vds-surface-paper:      var(--vds-d-surface-2);
    --vds-surface-paper-deep: var(--vds-d-surface);
    --vds-ink-terracotta: oklch(0.760 0.125  42);
    --vds-ink-saffron:    oklch(0.800 0.130  85);
    --vds-ink-teal:       oklch(0.760 0.090 202);
  }
}

@media (prefers-color-scheme: dark) {
  @layer vds.tokens {
    [data-vds-theme="auto"] {
      --vds-surface-paper:      var(--vds-d-surface-2);
      --vds-surface-paper-deep: var(--vds-d-surface);
      --vds-ink-terracotta: oklch(0.760 0.125  42);
      --vds-ink-saffron:    oklch(0.800 0.130  85);
      --vds-ink-teal:       oklch(0.760 0.090 202);
    }
  }
}


/* ===========================================================================
   TIER 2g — THE COMB INK LADDER
   (appended 2026-07-29, chatbot-API lane — surface correction pass)
   ---------------------------------------------------------------------------
   WHY THIS EXISTS. `.vds-comb` shipped earlier today with three inks —
   terracotta / saffron / teal — chosen by matching LIGHTNESS against the cream
   paper stock that page was then using. That stock was wrong: this site is
   white, and the founder's reaction to the cream page named the saffron comb
   specifically ("golden"). Two separate problems fell out of it:

     1. The three inks were tuned against the wrong ground. Measured: 3.35:1,
        3.22:1 and 3.46:1 against #f4efe4. Re-measured on #ffffff the same
        three land at 3.84 / 3.70 / 3.96 — heavier than intended, and no
        longer as tightly matched to each other.
     2. Three is not enough. The brief is a comb in EVERY grid, and a page
        with ten card rows would have to repeat each hue three times.

   WHAT THIS IS. Ten comb inks, one per hue of the TIER 2f bloom ladder, at
   the SAME hues (12 / 48 / 84 / ... / 336), so a card whose bloom register is
   --iris can carry a comb in the iris hue and the two read as one decision
   rather than two palettes. The comb is the structured, discrete counterpart
   to the diffuse bloom; sharing the hue is what makes that legible, and it
   inherits TIER 2f's subject mapping for free (--iris is AI/chatbots,
   --cerulean is developer tooling, --sand is timing, and so on).

   NOT the same VALUES, though, and that is deliberate. The TIER 2f inks are
   pale by construction (L 0.80-0.88) because their job is a wash rising
   behind body copy on a white card. A comb is a FOREGROUND graphic — bars
   against the page — so it needs a mid-tone. These sit at L 0.622-0.654.

   SOLVED, NOT PICKED. Each ink is a bisection on L, at fixed chroma 0.125,
   for a target of 3.40:1 against #ffffff — the non-text contrast floor a
   graphical object is held to, and therefore the same visual weight for all
   ten by construction. Achieved range 3.39-3.43:1. Where chroma 0.125 fell
   outside sRGB at the solved L it was bisected down until it fit and the
   reduced value written back, so the OKLCH and the sRGB triplet always
   describe the same colour (lagoon -> 0.107, cerulean -> 0.123; the other
   eight kept 0.125). Same method as TIER 2b and TIER 2f.

   NO PER-INK STRENGTH KNOB, unlike the bloom registers. The comb lays its ink
   down at full opacity at the baseline and dissolves upward, so matching the
   ink IS matching the weight; there is nothing left for a compensation dial
   to do.

   Purely additive. The three original inks above are untouched, so any page
   still on the paper stock keeps exactly what it had.
   =========================================================================== */

@layer vds.tokens {
  :root {
    --vds-ink-comb-rose:     oklch(0.654 0.125  12);  --vds-ink-comb-rose-srgb:     208 110 124;  /* 3.39:1 on #fff */
    --vds-ink-comb-clay:     oklch(0.649 0.125  48);  --vds-ink-comb-clay-srgb:     204 117  70;  /* 3.39:1 */
    --vds-ink-comb-sand:     oklch(0.640 0.125  84);  --vds-ink-comb-sand-srgb:     176 132  24;  /* 3.41:1 */
    --vds-ink-comb-leaf:     oklch(0.629 0.125 120);  --vds-ink-comb-leaf-srgb:     128 147  49;  /* 3.42:1 */
    --vds-ink-comb-jade:     oklch(0.622 0.125 156);  --vds-ink-comb-jade-srgb:      56 156 102;  /* 3.43:1 */
    --vds-ink-comb-lagoon:   oklch(0.624 0.107 192);  --vds-ink-comb-lagoon-srgb:     0 155 152;  /* 3.42:1, chroma clamped to gamut */
    --vds-ink-comb-cerulean: oklch(0.630 0.123 228);  --vds-ink-comb-cerulean-srgb:   0 150 195;  /* 3.41:1, chroma clamped to gamut */
    --vds-ink-comb-iris:     oklch(0.639 0.125 264);  --vds-ink-comb-iris-srgb:     101 138 216;  /* 3.40:1 */
    --vds-ink-comb-orchid:   oklch(0.648 0.125 300);  --vds-ink-comb-orchid-srgb:   154 124 207;  /* 3.41:1 */
    --vds-ink-comb-plum:     oklch(0.652 0.125 336);  --vds-ink-comb-plum-srgb:     191 113 173;  /* 3.40:1 */
  }
}

/* Dark: the ladder steps up to L 0.760 at chroma 0.115, which measures
   8.15-9.04:1 against --vds-d-surface (#141029) — the same band the three
   original dark comb inks already occupy (8.26 / 9.85 / 8.89), so a page that
   mixes old and new inks in dark mode stays even. Ten values, one L, one C:
   the ladder is still a ladder. */
@layer vds.tokens {
  :where([data-vds-theme="dark"], .vds-dark) {
    --vds-ink-comb-rose:     oklch(0.760 0.115  12);
    --vds-ink-comb-clay:     oklch(0.760 0.115  48);
    --vds-ink-comb-sand:     oklch(0.760 0.115  84);
    --vds-ink-comb-leaf:     oklch(0.760 0.115 120);
    --vds-ink-comb-jade:     oklch(0.760 0.115 156);
    --vds-ink-comb-lagoon:   oklch(0.760 0.115 192);
    --vds-ink-comb-cerulean: oklch(0.760 0.115 228);
    --vds-ink-comb-iris:     oklch(0.760 0.115 264);
    --vds-ink-comb-orchid:   oklch(0.760 0.115 300);
    --vds-ink-comb-plum:     oklch(0.760 0.115 336);
  }
}

@media (prefers-color-scheme: dark) {
  @layer vds.tokens {
    [data-vds-theme="auto"] {
      --vds-ink-comb-rose:     oklch(0.760 0.115  12);
      --vds-ink-comb-clay:     oklch(0.760 0.115  48);
      --vds-ink-comb-sand:     oklch(0.760 0.115  84);
      --vds-ink-comb-leaf:     oklch(0.760 0.115 120);
      --vds-ink-comb-jade:     oklch(0.760 0.115 156);
      --vds-ink-comb-lagoon:   oklch(0.760 0.115 192);
      --vds-ink-comb-cerulean: oklch(0.760 0.115 228);
      --vds-ink-comb-iris:     oklch(0.760 0.115 264);
      --vds-ink-comb-orchid:   oklch(0.760 0.115 300);
      --vds-ink-comb-plum:     oklch(0.760 0.115 336);
    }
  }
}
