/* ============================================================================
 * NAMESPACE: vk-. WHY THIS RENAME HAPPENED, AND WHY IT HAD TO HAPPEN TODAY.
 * ==========================================================================
 *
 * This file was written with two sets of class names that cannot survive contact
 * with the live site, both found by grepping all 29,866 pages rather than by
 * guessing:
 *
 *  1. FIVE EXACT COLLISIONS. `.vds-code`, `.vds-input`, `.vds-mono`,
 *     `.vds-select` and `.vds-textarea` are already defined, with different rule
 *     bodies, by the pre-existing css/vedika-ds.css (2,454 lines) and
 *     css/vedika-tokens.css (1,208 lines). Those two are already loaded on ~19
 *     pages INCLUDING the homepage. Adding this file to any of them would have
 *     started a load-order fight on all five selectors - the kind of bug that
 *     looks like "the design randomly breaks on some pages".
 *
 *  2. `.sheet` IS ALREADY TAKEN, TWICE, INCOMPATIBLY. The codebase has a shipped
 *     WebGL "spectral sheet" feature using that word throughout, and two pages
 *     define a literal `class="sheet"` with rules that have nothing to do with a
 *     card surface: sheet-gl.html:234 mounts a canvas with it, and
 *     vastu-demo.html:515-533 uses it for a mobile bottom-sheet DRAWER. A global
 *     `.sheet` card rule would have broken both.
 *
 * Renamed now, deliberately, because this stylesheet is currently linked by ZERO
 * pages. That makes the rename free - there is no consumer to update and nothing
 * can regress. Every page that adopts it after this point gets the namespaced
 * names, so the cost never has to be paid again. The window for doing this
 * without a migration closes the moment the first page links the file.
 *
 * `.bg-grid`, `.pill-btn` and `.chip` are deliberately left unprefixed: all three
 * were verified to have zero conflicting definitions anywhere in the tree (the 75
 * "chip" hits are all custom-prefixed variants like `about-proof-chip`, not the
 * bare class). Renaming those would be churn without evidence.
 * ========================================================================== */

/* ============================================================
   VEDIKA DESIGN SYSTEM v2  —  2026-07-30
   Shared across ALL pages. Include before page-specific CSS.
   Design reference: WhatsApp videos 11.04.18, 11.05.02,
   11.06.22, 11.07.48, 11.07.51 + Vedika-Hero-draft12.mp4
   ============================================================ */

/* ── 1. DESIGN TOKENS ── */
:root {
  /* Background */
  /* #E9ECF2, MEASURED from the founder's own prototype videos rather than chosen.
   * Sampled at 7 points across the ground in v1 and v3 (four corners, both mid-edges,
   * centre): identical everywhere, so the ground is FLAT, not a gradient. The previous
   * #F5F5F7 was 8.4% lighter and materially warmer - blue-minus-red +2 against their +9.
   * That difference is small per-pixel and unmistakable across a full page, which is
   * part of why the site read as "not matching the videos". */
  --vk-bg:            #E9ECF2;
  --vk-bg-grid-line:  rgba(0,0,0,0.055);
  --vk-bg-grid-size:  40px;

  /* Ink */
  --vk-ink:           #0C0C0E;
  --vk-ink-muted:     #626875;
  --vk-ink-faint:     #636870;

  /* Cosmic — primary brand (deep violet / indigo) */
  --vk-cosmic-deep:   #3B0764;
  --vk-cosmic:        #7C3AED;
  --vk-cosmic-mid:    #A78BFA;
  --vk-cosmic-light:  #EDE9FE;
  --vk-cosmic-pale:   #F5F3FF;

  /* Fire — energy, CTA, hot accents */
  --vk-fire-hot:      #DC2626;
  --vk-fire:          #F97316;
  --vk-fire-warm:     #FBBF24;
  --vk-fire-pale:     #FFF7ED;

  /* Cloud — sky, cool, water */
  --vk-cloud-deep:    #0284C7;
  --vk-cloud:         #38BDF8;
  --vk-cloud-light:   #BAE6FD;
  --vk-cloud-pale:    #F0F9FF;

  /* Neon — electric, highlights */
  --vk-neon-deep:     #0E7490;
  --vk-neon:          #06B6D4;
  --vk-neon-bright:   #67E8F9;

  /* Space — dark surfaces */

  /* TEXT-SAFE SIBLINGS. The brand values above double as fills, so darkening them
   * would repaint every fill that uses them - a redesign, not a contrast fix.
   * These are for TEXT, and each clears 4.5:1 against the harder of the two
   * backgrounds (the grid ground, not white). Reach for these whenever a brand
   * colour is the colour of words. */
  --vk-emerald-ink:      #047854;   /* text-safe #059669 */
  --vk-gold-ink:         #81621D;   /* text-safe #C4962C */
  --vk-cloud-deep-ink:   #016EA7;   /* text-safe #0284C7 */
  --vk-rose-ink:         #CA1A40;   /* text-safe #E11D48 */
  --vk-fuchsia-ink:      #B022C2;   /* text-safe #C026D3 */

  --vk-space:         #09090B;
  --vk-space-mid:     #18181B;
  --vk-space-muted:   #3F3F46;

  /* Gold — premium, enterprise */
  --vk-gold:          #C4962C;
  --vk-gold-warm:     #F59E0B;

  /* Rose — error, warn */
  --vk-rose:          #E11D48;
  --vk-rose-pale:     #FFF1F2;

  /* Emerald — success */
  --vk-emerald:       #059669;
  --vk-emerald-pale:  #ECFDF5;

  /* Neutral surface */
  --vk-surface:       #FFFFFF;
  --vk-line:          rgba(0,0,0,0.07);
  --vk-line-strong:   rgba(0,0,0,0.13);

  /* Fuchsia (legacy accent) */
  --vk-fuchsia:       #C026D3;
  --vk-deep-purple:   #4A1D96;
  --vk-violet:        #7C3AED;  /* alias */

  /* Typography */
  --vk-font-body:     'Outfit', system-ui, sans-serif;
  --vk-font-mono:     'JetBrains Mono', monospace;

  /* Radius */
  --vk-r-xs:  6px;
  --vk-r-sm:  10px;
  --vk-r-md:  16px;
  --vk-r-lg:  24px;
  --vk-r-xl:  32px;
  --vk-r-pill: 999px;

  /* Shadow */
  --vk-sh-card:  0 1px 3px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.04);
  --vk-sh-raise: 0 2px 8px rgba(0,0,0,0.08), 0 8px 28px rgba(0,0,0,0.06);
  --vk-sh-float: 0 4px 16px rgba(0,0,0,0.10), 0 16px 48px rgba(0,0,0,0.08);
}

/* ── 2. GLOBAL BASE ── */
/* ── SCOPE ──
 * The rules below this line are the FRAMEWORK half of this file and they are opt-in:
 * every one is scoped to `.vk-page` on <body>. They own body type, the page ground, a
 * universal box-sizing reset, range inputs and scrollbars, and dropping that onto a
 * page that already has its own type and ground restyles it wholesale - which is
 * exactly what happened when this file was added to 364 existing pages.
 *
 * The COMPONENT half (.vk-sheet, .pill-btn, .vk-footer, .vk-portrait, .bg-grid, the
 * blob families) is unscoped on purpose and safe anywhere: it only styles elements
 * that carry its classes. A page can take the footer without taking the framework.
 *
 * To adopt the framework on a page, add class="vk-page" to its <body>.
 * ------------------------------------------------------------------------- */
.vk-page, .vk-page *, .vk-page *::before, .vk-page *::after { box-sizing: border-box; }
body.vk-page {
  font-family: var(--vk-font-body);
  /* background-COLOR, not the shorthand. The shorthand resets background-image to
   * none, and at 0,0,1,1 this rule outranks .bg-grid at 0,0,1,0 whatever the source
   * order, so any page using both silently lost its grid - measured as
   * backgroundImage: "none". Third time the shorthand has done this today. */
  background-color: var(--vk-bg);
  color: var(--vk-ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
code, pre, .mono { font-family: var(--vk-font-mono); }

/* ── 3. GRID BACKGROUND ── */
/* Apply to <body> or any section wrapper */
.bg-grid {
  background-color: var(--vk-bg);
  background-image:
    linear-gradient(var(--vk-bg-grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--vk-bg-grid-line) 1px, transparent 1px);
  background-size: var(--vk-bg-grid-size) var(--vk-bg-grid-size);
}
.bg-grid-white {
  background-color: #FFFFFF;
  background-image:
    linear-gradient(rgba(0,0,0,0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,0.04) 1px, transparent 1px);
  background-size: 40px 40px;
}

/* ── 4. SHEET COMPONENT ── */
/* White card with clean lift. Text goes at top-left (z:1), blob art at bottom (z:0). */
.vk-sheet {
  background: var(--vk-surface);
  border-radius: var(--vk-r-lg);
  position: relative;
  overflow: hidden;
  box-shadow: var(--vk-sh-card);
}
.vk-sheet-sm   { border-radius: var(--vk-r-md); }
.vk-sheet-dark { background: var(--vk-space); }
.vk-sheet-pad  { padding: 28px 32px; }
.vk-sheet-pad-sm { padding: 18px 20px; }
.vk-sheet-pad-xs { padding: 12px 14px; }

/* Content layer sits above blob art */
.vk-sheet-content {
  position: relative;
  z-index: 1;
}

/* Blob art layer — position: absolute, lives inside .vk-sheet */
.vk-sheet-blob {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  pointer-events: none;
  z-index: 0;
  /* Height is set per-instance via inline style or modifier */
}

/* Pre-made blob heights */
.vk-sheet-blob-40  { height: 40%; }
.vk-sheet-blob-55  { height: 55%; }
.vk-sheet-blob-70  { height: 70%; }
.vk-sheet-blob-full { height: 100%; top: 0; }

/* Sheet hover lift */
.vk-sheet-lift { transition: box-shadow 0.25s ease, transform 0.25s ease; }
.vk-sheet-lift:hover {
  box-shadow: var(--vk-sh-raise);
  transform: translateY(-2px);
}

/* ── 5. GRADIENT BLOB ART ── */
/* Use inside .vk-sheet-blob or as standalone backgrounds */

/* Cosmic — deep violet / purple aurora */
.blob-cosmic {
  background:
    radial-gradient(ellipse 80% 60% at 70% 100%, rgba(167,139,250,0.9) 0%, rgba(124,58,237,0.7) 45%, rgba(59,7,100,0.4) 80%, transparent 100%),
    radial-gradient(ellipse 50% 80% at 30% 110%, rgba(192,38,211,0.4) 0%, transparent 60%);
  animation: blob-breathe 6s ease-in-out infinite alternate;
}

/* Fire — orange / red / warm yellow flame */
.blob-fire {
  background:
    radial-gradient(ellipse 70% 90% at 50% 110%, rgba(251,191,36,0.95) 0%, rgba(249,115,22,0.8) 35%, rgba(220,38,38,0.6) 65%, transparent 90%),
    radial-gradient(ellipse 40% 60% at 80% 100%, rgba(252,165,165,0.5) 0%, transparent 50%);
  animation: blob-flame 4s ease-in-out infinite alternate;
}

/* Cloud — sky blue / teal */
.blob-cloud {
  background:
    radial-gradient(ellipse 80% 70% at 50% 110%, rgba(103,232,249,0.8) 0%, rgba(56,189,248,0.6) 40%, rgba(2,132,199,0.3) 75%, transparent 100%);
  animation: blob-breathe 7s ease-in-out infinite alternate;
}

/* Neon — electric cyan */
.blob-neon {
  background:
    radial-gradient(ellipse 60% 80% at 60% 100%, rgba(103,232,249,0.9) 0%, rgba(6,182,212,0.7) 50%, rgba(14,116,144,0.3) 80%, transparent 100%);
  animation: blob-breathe 5s ease-in-out infinite alternate;
}

/* Prismatic — rainbow iridescent (like the music player buttons) */
.blob-prismatic {
  background:
    radial-gradient(ellipse 60% 60% at 40% 110%, rgba(249,168,212,0.8) 0%, transparent 50%),
    radial-gradient(ellipse 60% 60% at 70% 100%, rgba(196,181,253,0.8) 0%, transparent 50%),
    radial-gradient(ellipse 50% 70% at 55% 90%, rgba(110,231,183,0.6) 0%, transparent 55%),
    radial-gradient(ellipse 40% 50% at 30% 100%, rgba(253,230,138,0.7) 0%, transparent 45%);
  animation: blob-prismatic-shift 8s ease-in-out infinite alternate;
}

/* Dusk — rose / pink / blue mix (like "Invisible Sources" sheets) */
.blob-dusk {
  background:
    radial-gradient(ellipse 60% 70% at 40% 110%, rgba(253,230,138,0.8) 0%, rgba(249,115,22,0.5) 40%, transparent 70%),
    radial-gradient(ellipse 50% 60% at 70% 105%, rgba(249,168,212,0.6) 0%, rgba(216,180,254,0.4) 50%, transparent 70%),
    radial-gradient(ellipse 40% 50% at 20% 100%, rgba(147,197,253,0.5) 0%, transparent 60%);
  animation: blob-breathe 8s ease-in-out infinite alternate;
}

/* Monochrome — silver / grey depth (like S1 card) */
.blob-mono {
  background:
    radial-gradient(ellipse 70% 80% at 50% 110%, rgba(255,255,255,0.9) 0%, rgba(200,200,210,0.5) 50%, transparent 80%),
    radial-gradient(ellipse 40% 60% at 20% 100%, rgba(100,100,120,0.3) 0%, transparent 50%);
  animation: blob-breathe 9s ease-in-out infinite alternate;
}

/* ══ THE DUST ENGINE ══════════════════════════════════════════════════════════
 *
 * The blobs above fade correctly, but each is a static two-or-three-lobe radial
 * gradient with NO blur and no positional drift - blob-breathe only moves opacity
 * and scale by 2-4%. Audited verdict: "a soft ambient light wash, not migrating
 * particulate". That is the founder's most-repeated ask and it was not delivered.
 *
 * WHY THIS IS PSEUDO-ELEMENTS AND NOT NEW MARKUP.
 * Eight of the eleven pages carrying a blob also load the page field, and every one
 * of them hand-rolled its OWN guard against the two layers stacking - five mutually
 * incompatible techniques, including `body.sdks-page .vk-sheet:not(:has(.vk-sheet-blob))`.
 * A wrapper element, or moving to a <canvas>, silently stops all of those matching
 * and re-introduces double-layering with no single place to fix it. ::before and
 * ::after change nothing about the DOM, so all six keep working untouched.
 *
 * WHY TRANSFORM-ONLY, AND WHY THAT REVERSES THE STANDING BUDGET.
 * css/vedika-page-field.css records ~12.9ms per drifting instance, and that number is
 * the reason this site shares ONE field rather than giving each card its own. Re-measured
 * with Chromium tracing, 40 cards, 6s, steady state:
 *
 *     static gradient (control)                0.8ms  main thread
 *     animated background-position           236.7ms  (147.6 paint + 72.1 layout)
 *     animated filter: hue-rotate                0ms
 *     transform-only over a STATIC blur          0ms
 *
 * So 12.9ms was never the price of movement - it was the price of animating a PAINT
 * property. A pre-rasterised blurred layer moved by transform alone is handed to the
 * compositor and costs nothing measurable. Both animated cases were confirmed genuinely
 * running (getAnimations playState + a 3.2s pixel diff) because a silent no-op would
 * also have scored 0ms, which is the trap that a Canvas UI component set earlier today.
 * Raster stayed at 0 across the whole window WITH scale animating on the blurred layers,
 * which answers the one real worry here - that blur forces re-rasterisation per scale
 * step. Verified in Chromium only; Safari and Firefox are NOT verified.
 *
 * WHAT THIS DELIBERATELY DOES NOT DO.
 *  - It does not BRIGHTEN. partners.html measured 2.44:1 on copy over blob-cosmic and
 *    its fix is calibrated to the current brightness; status.html is grey on near-white,
 *    the thinnest margin on the site. So the lobes are low-alpha and blob-mono's are
 *    deliberately the DARK end of its ramp, never more white.
 *  - It does not GROW THE BAND. glossary.html sets min-height:460px specifically to keep
 *    its text clear of the blob, sized against the current 1.04 scale. All new motion is
 *    INSIDE the band; blob-breathe's amplitude is untouched.
 * ══════════════════════════════════════════════════════════════════════════════ */

/* ONE LAYER, NOT ONE PER LOBE, AND THAT IS A MEASUREMENT NOT A SHORTCUT.
 *
 * First build gave each lobe its own pseudo-element and its own keyframes with a
 * -4.3s offset between them, on the reasoning that lobes drifting apart read as
 * particulate while lobes locked together read as one rigid object sliding. The
 * frames disagree: tracked across the loop, the spacing between lobes inside a
 * single blob has a coefficient of variation of only 2-3%. They travel as ONE
 * GROUP. The 0.68-0.80 correlation figure I had reached for describes two
 * DIFFERENT CAPSULES tracking each other, not two lobes inside one blob - the
 * right number for the wrong question.
 *
 * So all the lobes live on one element as stacked radial-gradients and share a
 * single transform. Fewer composited layers for free, and it is what the
 * reference actually does. */
.vk-sheet-blob::before {
  content: '';
  position: absolute;
  display: block;
  /* Oversized so the blur has room and never reveals its own square edge inside
     the card. The sheet's overflow:hidden does the clipping. */
  inset: -18%;
  pointer-events: none;
  background: var(--vk-dust, none);
  /* MEASURED: the reference blur is 4-5% of card width (median 4.5% full-res,
     4.2% on the 4x crops - two independent scales agreeing, so it is a real edge
     scale and not a resampling artifact). CSS blur(N) is a Gaussian with
     stdDeviation N, no halving, so a ~450px sheet wants ~20px. Applied ONCE and
     never animated: that is the entire performance argument, because a
     pre-rasterised layer moved by transform alone never repaints. */
  filter: blur(20px);
  will-change: transform;
  animation: vk-dust-drift 12.5s ease-in-out infinite;
}

/* 12.5s and the sine easing are measured, not chosen: the reference loop wraps at
   corr 0.996 over 12.5s, and a sine fits its motion at R2 0.91-0.98 where linear
   manages 0.01-0.68. Translation and swell share one keyframe because in the
   reference they ARE one movement - corr(x, mass) = 0.87 - not two effects
   layered. Runs 0 -> mid -> 0 rather than `alternate` so the loop is a true
   12.5s cycle. Displacement stays inside the measured 2.5-5.9% of blob width. */
@keyframes vk-dust-drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(4.5%, -4%, 0) scale(1.07); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

/* EACH LOBE SWEEPS ITS OWN HUE. Measured 90%-mass hue spread inside a single lobe
   is a median of 26 degrees (IQR 20-35) - roughly a quarter of the wheel - which is
   why the flat two-or-three-stops-of-one-hue fills read as plastic. Every gradient
   below therefore carries three stops that move in hue as they fade.
   Alphas are held at or below each colourway's existing stops so the dust adds
   structure and motion WITHOUT adding luminance: partners.html measured 2.44:1 on
   copy over blob-cosmic and its fix is calibrated to the present brightness.
   blob-mono inverts deliberately - its ramp is already near-white and status.html
   puts grey text on it, so its dust comes from the dark end and deepens rather
   than lightens. */
.blob-cosmic {
  --vk-dust:
    radial-gradient(circle at 22% 78%, rgba(167,139,250,0.40) 0%, rgba(139,110,246,0.24) 40%, rgba(124,58,237,0) 70%),
    radial-gradient(circle at 72% 62%, rgba(192,38,211,0.26) 0%, rgba(160,60,225,0.15) 40%, rgba(124,58,237,0) 70%);
}
.blob-fire {
  --vk-dust:
    radial-gradient(circle at 20% 80%, rgba(251,191,36,0.34) 0%, rgba(249,146,26,0.20) 40%, rgba(249,115,22,0) 70%),
    radial-gradient(circle at 70% 60%, rgba(220,38,38,0.22) 0%, rgba(233,80,30,0.13) 40%, rgba(249,115,22,0) 70%);
}
.blob-cloud {
  --vk-dust:
    radial-gradient(circle at 24% 78%, rgba(103,232,249,0.32) 0%, rgba(70,205,244,0.19) 40%, rgba(56,189,248,0) 70%),
    radial-gradient(circle at 71% 62%, rgba(2,132,199,0.22) 0%, rgba(20,160,220,0.13) 40%, rgba(56,189,248,0) 70%);
}
.blob-neon {
  --vk-dust:
    radial-gradient(circle at 22% 79%, rgba(103,232,249,0.34) 0%, rgba(50,205,230,0.20) 40%, rgba(6,182,212,0) 70%),
    radial-gradient(circle at 70% 60%, rgba(14,116,144,0.24) 0%, rgba(10,150,180,0.14) 40%, rgba(6,182,212,0) 70%);
}
.blob-prismatic {
  --vk-dust:
    radial-gradient(circle at 18% 79%, rgba(253,230,138,0.30) 0%, rgba(249,190,170,0.18) 40%, rgba(249,168,212,0) 70%),
    radial-gradient(circle at 71% 59%, rgba(196,181,253,0.32) 0%, rgba(150,200,240,0.19) 40%, rgba(110,231,183,0) 70%),
    radial-gradient(circle at 48% 83%, rgba(110,231,183,0.24) 0%, rgba(170,220,200,0.14) 40%, rgba(196,181,253,0) 70%);
}
.blob-dusk {
  --vk-dust:
    radial-gradient(circle at 20% 80%, rgba(249,168,212,0.30) 0%, rgba(222,170,230,0.18) 40%, rgba(216,180,254,0) 70%),
    radial-gradient(circle at 70% 61%, rgba(147,197,253,0.24) 0%, rgba(190,185,240,0.14) 40%, rgba(216,180,254,0) 70%);
}
.blob-mono {
  --vk-dust:
    radial-gradient(circle at 22% 79%, rgba(120,120,140,0.19) 0%, rgba(140,140,158,0.11) 40%, rgba(160,160,178,0) 70%),
    radial-gradient(circle at 70% 61%, rgba(150,150,168,0.15) 0%, rgba(170,170,186,0.09) 40%, rgba(190,190,205,0) 70%);
}

/* BAKED IN HERE, not per page. Only changelog.html had a reduced-motion guard for
   blobs; the other ten pages animated regardless. Owning it in the shared file means
   every page that adopts a blob gets the guard for free and nobody has to remember. */
@media (prefers-reduced-motion: reduce) {
  .vk-sheet-blob,
  .vk-sheet-blob::before { animation: none; }
}

@keyframes blob-breathe {
  from { opacity: 0.85; transform: scale(1.0) translateY(0); }
  to   { opacity: 1.0;  transform: scale(1.04) translateY(-2%); }
}
@keyframes blob-flame {
  from { opacity: 0.8; filter: brightness(1.0); transform: scaleX(1.0); }
  to   { opacity: 1.0; filter: brightness(1.12); transform: scaleX(1.03); }
}
@keyframes blob-prismatic-shift {
  from { filter: hue-rotate(0deg) saturate(1.0); }
  to   { filter: hue-rotate(30deg) saturate(1.2); }
}

/* ── 6. PILL BUTTON ── */
/* Wide horizontal pill: bold text left, animated gradient blob right.
   Reference: WhatsApp video 11.04.18 (colored buttons),
              WhatsApp video 11.06.22 (prismatic/pearl buttons) */
.pill-btn {
  display: inline-flex;
  align-items: center;
  border-radius: var(--vk-r-pill);
  overflow: hidden;
  background: var(--vk-space);
  box-shadow: 0 2px 12px rgba(0,0,0,0.14), 0 1px 3px rgba(0,0,0,0.08);
  height: 60px;
  /* 230px, from a sub-pixel measurement of the founder's own capsules.
   *
   * TWO AGENTS MEASURED THIS AND DISAGREED, AND THE DISAGREEMENT WAS THE FINDING.
   * The first used a background-difference mask and got 2.95-3.36:1. The second used
   * steepest-RGB-gradient edge detection and got 3.69:1 (v1) and 3.85:1 (v3) - and
   * explained the gap: a naive mask swallows the drop-shadow, which bleeds below and
   * right of the pill, so it reports capsules that are too tall AND unequal (92/83/78px
   * for three capsules that are in fact identical to the pixel). Excluding the shadow,
   * all three in a stack measure exactly the same size.
   *
   * So the honest target is 3.69-3.85:1, not 3.17:1, and our 240px (4.00:1) was already
   * close rather than badly off. 230px lands at 3.83:1, inside the measured range.
   *
   * Also confirmed by the same fit: the corner is a TRUE stadium, radius = height/2
   * exactly (ratio 0.990-1.001, sub-pixel residuals, no flat segment in the cap) - which
   * var(--vk-r-pill) at 999px already gives. */
  min-width: 230px;
  cursor: pointer;
  border: none;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  position: relative;
}
.pill-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 24px rgba(0,0,0,0.18), 0 2px 6px rgba(0,0,0,0.10);
}
.pill-btn:active { transform: translateY(0); }

/* Light variant */
.pill-btn-light {
  background: #FFFFFF;
  box-shadow: 0 2px 12px rgba(0,0,0,0.07), 0 1px 3px rgba(0,0,0,0.04);
}

/* Text block */
.pill-btn .pill-text {
  flex: 0 0 auto;
  padding: 0 24px;
  color: #FFFFFF;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.015em;
  line-height: 1.2;
  font-family: var(--vk-font-body);
}
.pill-btn-light .pill-text { color: var(--vk-ink); }
.pill-btn .pill-sub {
  display: block;
  font-size: 10px;
  font-weight: 500;
  opacity: 0.6;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin-top: 2px;
}

/* Blob half */
.pill-btn .pill-blob {
  flex: 1;
  height: 100%;
  position: relative;
  overflow: hidden;
  /* THE SEAM, AND WHY A MASK RATHER THAN DIFFERENT GRADIENT STOPS.
   *
   * Every pill-blob-* gradient below runs OPAQUE to its final stop, so this box
   * painted edge to edge and the flex boundary rendered as a dead-straight
   * vertical line. Measured across the boundary on a 20px window: a colour jump
   * of 0.99 on `cloud`, 0.93 neon, 0.90 fire, 0.87 cosmic - a step function, not
   * a transition. At normal size it reads as a two-tone flag.
   *
   * The reference has colour BLOOMING OUT OF the dark body, which means the blob
   * has to ARRIVE as a fade. Re-tuning the gradient stops cannot produce that: the
   * box still ends where it ends, and the last stop still meets the text half at
   * full strength. A mask fades the BOX itself, so the pill's own background -
   * --vk-space on the dark variant, #FFFFFF on the light one - shows through and
   * the colour appears to grow out of it. No layout change, so nothing reflows on
   * the 16 pages that use this component. */
  -webkit-mask: linear-gradient(90deg, transparent 0%, #000 34%);
  mask: linear-gradient(90deg, transparent 0%, #000 34%);
}

/* LIGHT PILLS DE-AMPLIFY THEIR BLOB. Measured peak saturation came back IDENTICAL
 * on both variants (205-248), because .pill-blob-* is unconditional and there was
 * no light-mode rule at all - against a reference where the light capsules sit at
 * 36-129, roughly half. A veil, NOT `filter: saturate()`: the keyframes below
 * animate `filter`, and an animated property beats a static one on the same
 * element, so a static filter here would be silently thrown away. 0.42 white lands
 * at ~58% of the dark variant's saturation, inside the measured band. */
.pill-btn-light .pill-blob::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 0.42);
  pointer-events: none;
}

/* Blob animations for pill blobs (smaller, tighter than sheet blobs) */
.pill-blob-cosmic {
  background: radial-gradient(ellipse 120% 120% at 80% 50%, #C4B5FD 0%, #7C3AED 40%, #4C1D95 80%);
  animation: pill-blob-cosmic 6.25s ease-in-out infinite alternate;
}
.pill-blob-fire {
  background: radial-gradient(ellipse 130% 120% at 80% 50%, #FDE68A 0%, #F97316 35%, #DC2626 75%);
  animation: pill-blob-fire 6.25s ease-in-out infinite alternate;
}
.pill-blob-cloud {
  background: radial-gradient(ellipse 130% 120% at 80% 50%, #BAE6FD 0%, #38BDF8 40%, #0284C7 80%);
  animation: pill-blob-cloud 6.25s ease-in-out infinite alternate;
}
.pill-blob-neon {
  background: radial-gradient(ellipse 130% 120% at 80% 50%, #A5F3FC 0%, #06B6D4 45%, #0E7490 80%);
  animation: pill-blob-neon 6.25s ease-in-out infinite alternate;
}
.pill-blob-prismatic {
  background:
    radial-gradient(ellipse 80% 80% at 80% 30%, rgba(249,168,212,0.9) 0%, transparent 50%),
    radial-gradient(ellipse 80% 80% at 60% 80%, rgba(196,181,253,0.9) 0%, transparent 50%),
    radial-gradient(ellipse 80% 60% at 30% 50%, rgba(110,231,183,0.7) 0%, transparent 50%),
    linear-gradient(135deg, #f0f0f0, #e8e8e8);
  animation: pill-blob-prismatic 12.5s ease-in-out infinite;
}
.pill-blob-pearl {
  background:
    radial-gradient(ellipse 100% 100% at 70% 50%, rgba(255,255,255,0.95) 0%, rgba(220,220,230,0.5) 50%, rgba(150,150,170,0.2) 80%),
    linear-gradient(135deg, #f5f5f6, #e0e0e5);
  animation: pill-blob-pearl 6.25s ease-in-out infinite alternate;
}
.pill-blob-mono {
  background:
    radial-gradient(ellipse 120% 100% at 70% 50%, rgba(255,255,255,0.9) 0%, rgba(180,180,200,0.5) 50%, rgba(60,60,80,0.4) 90%),
    linear-gradient(135deg, #2a2a35, #1a1a22);
  animation: pill-blob-pearl 6.25s ease-in-out infinite alternate;
}

/* TRANSLATION AND SCALE MOVE TOGETHER, ON PURPOSE.
 *
 * Measured across 295 frames of the founder's own capsules: the blob does NOT
 * rotate - its axis holds inside 0.3-1.7 deg std, range 2.7-8.4 deg, so there is
 * no angular sweep to reproduce. What it actually does is translate and swell at
 * the same time, corr(x, mass) = 0.87 on the two most active capsules. Driving
 * those from one keyframe pair is what makes it read as a single object moving
 * rather than two effects layered. Displacement 5% sits inside the measured
 * 2.5-5.9% of blob width for this family. */
@keyframes pill-blob-cosmic {
  from { filter: hue-rotate(0deg) brightness(1.0); transform: scale(1.0) translateX(0); }
  to   { filter: hue-rotate(15deg) brightness(1.1); transform: scale(1.07) translateX(-5%); }
}
@keyframes pill-blob-fire {
  from { filter: brightness(1.0) saturate(1.0); transform: scale(1.0) translateX(0); }
  to   { filter: brightness(1.12) saturate(1.15); transform: scale(1.06) translateX(-4%); }
}
@keyframes pill-blob-cloud {
  from { filter: hue-rotate(0deg) brightness(1.0); transform: scale(1.0) translateX(0); }
  to   { filter: hue-rotate(-8deg) brightness(1.08); transform: scale(1.06) translateX(-4%); }
}
@keyframes pill-blob-neon {
  from { filter: brightness(1.0) saturate(1.0); transform: scale(1.0) translateX(0); }
  to   { filter: brightness(1.2) saturate(1.3); transform: scale(1.05) translateX(-3.5%); }
}
@keyframes pill-blob-prismatic {
  from { filter: hue-rotate(0deg) saturate(1.0); }
  to   { filter: hue-rotate(360deg) saturate(1.15); }
}
@keyframes pill-blob-pearl {
  from { filter: brightness(1.0); }
  to   { filter: brightness(1.08); }
}

/* ── 7. TYPOGRAPHY ── */
.t-hero {
  font-size: clamp(2.4rem, 4.5vw, 3.75rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.06;
  color: var(--vk-ink);
}
.t-hero-em {
  font-style: italic;
  color: var(--vk-cosmic);
}
.t-section {
  font-size: clamp(1.5rem, 2.8vw, 2.25rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  color: var(--vk-ink);
}
.t-label {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--vk-ink-muted);
}
.t-label-sm {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--vk-ink-faint);
}
.t-body {
  font-size: 15px;
  line-height: 1.65;
  color: var(--vk-ink-muted);
}
.t-body-sm {
  font-size: 13px;
  line-height: 1.6;
  color: var(--vk-ink-muted);
}

/* ── 8. GRADIENT TEXT ── */
.grad-cosmic {
  background: linear-gradient(135deg, var(--vk-cosmic), var(--vk-cosmic-mid));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}
.grad-fire {
  background: linear-gradient(135deg, var(--vk-fire), var(--vk-fire-warm));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* ── 9. CHIP / BADGE ── */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: var(--vk-r-pill);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.chip-cosmic  { background: var(--vk-cosmic-light); color: var(--vk-cosmic); border: 1px solid rgba(124,58,237,0.18); }
.chip-fire    { background: #FEF3C7; color: #92400E; border: 1px solid rgba(245,158,11,0.25); }
.chip-cloud   { background: var(--vk-cloud-pale); color: #0176B3; border: 1px solid rgba(56,189,248,0.25); }
.chip-emerald { background: var(--vk-emerald-pale); color: #047F59; border: 1px solid rgba(5,150,105,0.2); }
.chip-rose    { background: var(--vk-rose-pale); color: #D51B44; border: 1px solid rgba(225,29,72,0.2); }
.chip-gold    { background: #FEF6E7; color: #89691E; border: 1px solid rgba(196,150,44,0.25); }
.chip-fuchsia { background: #FDF2FD; color: #B624C8; border: 1px solid rgba(192,38,211,0.2); }
.chip-neutral { background: #F4F4F5; color: #52525B; border: 1px solid rgba(0,0,0,0.07); }
.chip-dark    { background: var(--vk-space); color: #E4E4E7; border: 1px solid rgba(255,255,255,0.08); }

/* ── 10. FORM ELEMENTS ── */
.vk-input, .vk-select, .vk-textarea {
  background: var(--vk-surface);
  border: 1px solid var(--vk-line-strong);
  color: var(--vk-ink);
  border-radius: var(--vk-r-sm);
  padding: 10px 14px;
  font-size: 13.5px;
  width: 100%;
  font-family: var(--vk-font-body);
  transition: border-color 120ms ease, box-shadow 120ms ease;
  appearance: none;
}
.vk-input:hover, .vk-select:hover, .vk-textarea:hover {
  border-color: var(--vk-cosmic-mid);
}
.vk-input:focus, .vk-select:focus, .vk-textarea:focus {
  outline: none;
  border-color: var(--vk-cosmic);
  box-shadow: 0 0 0 3px rgba(124,58,237,0.10);
}
.vk-textarea { resize: vertical; min-height: 72px; line-height: 1.55; }
.vk-mono { font-family: var(--vk-font-mono); font-size: 12.5px; }

/* Range slider */
.vk-page input[type="range"] {
  -webkit-appearance: none; appearance: none;
  background: transparent; border: none; padding: 0;
  height: 4px; cursor: pointer; width: 100%;
}
.vk-page input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  background: linear-gradient(90deg, var(--vk-cosmic), var(--vk-fuchsia));
  border-radius: 2px;
}
.vk-page input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: white;
  box-shadow: 0 1px 6px rgba(124,58,237,0.25);
  margin-top: -6px;
  border: 2px solid var(--vk-cosmic);
}
.vk-page input[type="range"]:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── 11. BUTTONS ── */
/* Standard action button — solid dark */
.vds-btn {
  background: var(--vk-ink);
  color: #FFFFFF;
  padding: 11px 22px;
  border-radius: var(--vk-r-sm);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: -0.01em;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: none;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
  font-family: var(--vk-font-body);
  white-space: nowrap;
}
.vds-btn:hover { background: #1f1f23; transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,0.15); }
.vds-btn:disabled { opacity: 0.38; cursor: not-allowed; transform: none; box-shadow: none; }

/* Cosmic variant */
.vds-btn-cosmic { background: var(--vk-cosmic); }
.vds-btn-cosmic:hover { background: #6D28D9; }

/* Ghost — outline */
.vds-btn-ghost {
  background: var(--vk-surface);
  color: var(--vk-ink);
  border: 1px solid var(--vk-line-strong);
  padding: 9px 16px;
  border-radius: var(--vk-r-sm);
  font-weight: 600;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: all 0.15s ease;
  font-family: var(--vk-font-body);
  white-space: nowrap;
}
.vds-btn-ghost:hover { border-color: var(--vk-cosmic); color: var(--vk-cosmic); background: var(--vk-cosmic-pale); }
.vds-btn-ghost.active { border-color: var(--vk-cosmic); color: var(--vk-cosmic); background: var(--vk-cosmic-pale); }

/* Icon button */
.vds-btn-icon {
  background: var(--vk-surface);
  border: 1px solid var(--vk-line-strong);
  color: var(--vk-ink-muted);
  padding: 0;
  width: 32px; height: 32px;
  border-radius: var(--vk-r-xs);
  cursor: pointer;
  transition: all 0.15s ease;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px;
}
.vds-btn-icon:hover { border-color: var(--vk-cosmic); color: var(--vk-cosmic); background: var(--vk-cosmic-pale); }

/* Send button */
.vds-btn-send {
  width: 38px; height: 38px;
  border-radius: var(--vk-r-xs);
  background: var(--vk-ink);
  color: white;
  border: none;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: all 0.15s ease;
  font-size: 14px;
}
.vds-btn-send:hover:not(:disabled) { background: #1f1f23; transform: translateY(-1px); }
.vds-btn-send:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── 12. SCROLLBARS ── */
.vk-page ::-webkit-scrollbar { width: 6px; height: 6px; }
.vk-page ::-webkit-scrollbar-track { background: transparent; }
.vk-page ::-webkit-scrollbar-thumb { background: var(--vk-line-strong); border-radius: 3px; }
.vk-page ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.2); }

/* ── 13. TOAST ── */
.vds-toast {
  position: fixed;
  bottom: 28px; right: 28px;
  padding: 13px 20px;
  background: var(--vk-space);
  color: white;
  border-radius: var(--vk-r-md);
  font-size: 13.5px; font-weight: 600;
  box-shadow: 0 12px 36px rgba(0,0,0,0.25);
  z-index: 9999;
  opacity: 0;
  transform: translateY(16px);
  pointer-events: none;
  transition: all 0.24s cubic-bezier(0.34,1.56,0.64,1);
}
.vds-toast.show { opacity: 1; transform: translateY(0); }

/* ── 14. MODAL ── */
.vds-modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(9,9,11,0.55);
  backdrop-filter: blur(6px);
  z-index: 1000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.vds-modal-backdrop.show { display: flex; }
.vds-modal {
  background: var(--vk-surface);
  border-radius: var(--vk-r-xl);
  padding: 32px;
  max-width: 560px;
  width: 100%;
  box-shadow: var(--vk-sh-float);
}

/* ── 15. REVEAL ANIMATION ── */
[data-r] { opacity: 1; transform: none; transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.23,1,0.32,1); }
@media (prefers-reduced-motion: no-preference) {
  [data-r].reveal-pending { opacity: 0; transform: translateY(20px); }
  [data-r].reveal-pending.vis { opacity: 1; transform: translateY(0); }
}

/* ── 16. CODE BLOCK ── */
.vk-code {
  background: var(--vk-space);
  color: #E4E4E7;
  border-radius: var(--vk-r-md);
  padding: 16px 18px;
  font-family: var(--vk-font-mono);
  font-size: 12.5px;
  line-height: 1.75;
  overflow-x: auto;
  white-space: pre;
}
.vk-code .kw  { color: #A78BFA; font-weight: 600; }
.vk-code .str { color: #6EE7B7; }
.vk-code .cm  { color: #6B7280; font-style: italic; }
.vk-code .fn  { color: #67E8F9; font-weight: 500; }
.vk-code .num { color: #FDE68A; }
.vk-code .flag { color: #F9A8D4; font-weight: 600; }

/* ── 17. TYPING INDICATOR ── */
.vds-typing {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 12px 16px;
}
.vds-typing span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--vk-ink-muted);
  animation: vds-typing 1.2s ease-in-out infinite;
}
.vds-typing span:nth-child(2) { animation-delay: 0.15s; }
.vds-typing span:nth-child(3) { animation-delay: 0.30s; }
@keyframes vds-typing {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.35; }
  30% { transform: translateY(-5px); opacity: 1; }
}

/* ── 18. EQ BARS (live audio indicator) ── */
.eq-bars { display: inline-flex; gap: 2px; align-items: flex-end; height: 14px; vertical-align: middle; }
.eq-bar { width: 2.5px; border-radius: 2px; animation: eq 1.1s ease-in-out infinite; background: var(--vk-cosmic); }
.eq-bar:nth-child(1) { height: 55%; animation-delay: 0s; }
.eq-bar:nth-child(2) { height: 100%; animation-delay: 0.10s; background: var(--vk-fuchsia); }
.eq-bar:nth-child(3) { height: 38%; animation-delay: 0.20s; }
.eq-bar:nth-child(4) { height: 78%; animation-delay: 0.30s; background: var(--vk-fuchsia); }
@keyframes eq { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(0.25); } }

/* ── 19. ENTERPRISE / PLAN PILLS ── */
.ent-pill {
  background: linear-gradient(135deg, var(--vk-gold), #E2A935);
  color: white;
  font-size: 9.5px; font-weight: 800;
  padding: 2px 7px; border-radius: var(--vk-r-pill);
  letter-spacing: 0.07em; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 3px;
}
.ent-unlock-pill {
  background: linear-gradient(135deg, var(--vk-emerald), #059669);
  color: white;
  font-size: 9.5px; font-weight: 800;
  padding: 2px 7px; border-radius: var(--vk-r-pill);
  letter-spacing: 0.07em; text-transform: uppercase;
}
.plan-pill {
  background: var(--vk-cosmic-light);
  color: var(--vk-cosmic);
  font-size: 9.5px; font-weight: 700;
  padding: 2px 7px; border-radius: var(--vk-r-pill);
  letter-spacing: 0.05em; text-transform: uppercase;
  border: 1px solid rgba(124,58,237,0.15);
}

/* ── 20. FOCUS RING ── */
.focusable:focus-visible {
  outline: 2px solid var(--vk-cosmic);
  outline-offset: 3px;
  border-radius: var(--vk-r-sm);
}

/* ── PORTRAIT FRAME ──
 *
 * A person in a hexagonal cosmic frame: nebula bed, orbiting accretion ring, lit
 * bevel. It lives HERE rather than in one page because the same treatment is
 * wanted on the tunnel roster, the About page and anywhere else a face appears,
 * and three copies of it would drift into three different looks.
 *
 * It is deliberately NOT a port of the singularity's .sing-beat__portal. That one
 * is tuned for white type on a black starfield inside a blend-isolated layer and
 * is covered by the hero gate; this one sits on the light --vk-bg ground with the
 * page grid behind it. Same grammar, different ground, so the ring opacity and
 * the shadow are set for a light surface. Sharing one rule between two grounds is
 * how you get a frame that looks washed out on one of them. */
.vk-portrait {
  position: relative;
  width: var(--portrait-size, 132px);
  height: var(--portrait-size, 132px);
  flex: 0 0 auto;
}
.vk-portrait__neb {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 28%, rgba(124,58,237,0.42), transparent 58%),
    radial-gradient(circle at 72% 70%, rgba(56,189,248,0.34), transparent 62%);
  filter: blur(7px);
}
.vk-portrait__ring {
  position: absolute;
  inset: 4%;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(124,58,237,0) 0deg, rgba(124,58,237,0.95) 34deg,
    rgba(56,189,248,0.32) 96deg, rgba(124,58,237,0) 168deg,
    rgba(236,72,153,0.55) 232deg, rgba(124,58,237,0) 300deg);
  -webkit-mask: radial-gradient(circle, transparent 63%, #000 67%, #000 82%, transparent 86%);
  mask: radial-gradient(circle, transparent 63%, #000 67%, #000 82%, transparent 86%);
  animation: vk-portrait-spin 22s linear infinite;
}
/* Explicit `from`, matching function lists. Omitting it leaves a one-function
 * base against a two-function target, CSS falls back to interpolating the composed
 * matrices, and the matrix of rotate(360deg) is the identity - the ring would
 * animate from itself to itself and sit perfectly still. */
@keyframes vk-portrait-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.vk-portrait__bevel,
.vk-portrait__img {
  position: absolute;
  -webkit-clip-path: polygon(50% 0%, 96% 25%, 96% 75%, 50% 100%, 4% 75%, 4% 25%);
  clip-path: polygon(50% 0%, 96% 25%, 96% 75%, 50% 100%, 4% 75%, 4% 25%);
}
.vk-portrait__bevel {
  inset: 17%;
  background: linear-gradient(150deg, #C4B5FD 0%, #7C3AED 38%, #38BDF8 72%, #EDE9FE 100%);
}
/* width/height stated, not left to the insets: this is a REPLACED element, and a
 * replaced element with auto width resolves to its INTRINSIC size, not to the box
 * its insets describe. Left auto, a 1200x2106 portrait renders a frame half again
 * as tall as a 754x800 one - measured, on two real photos. */
.vk-portrait__img {
  inset: calc(17% + 3px);
  width: calc(66% - 6px);
  height: calc(66% - 6px);
  object-fit: cover;
  object-position: 50% 22%;
  background: var(--vk-space);
}
@media (prefers-reduced-motion: reduce) {
  .vk-portrait__ring { animation: none; }
}

/* ── FOOTER ──
 *
 * Rebuilt against the founder's reference after a first attempt they correctly called
 * bullshit. That attempt copied the reference's ARRANGEMENT and dropped everything that
 * made it work:
 *
 *   reference                       first attempt
 *   -----------------------------   --------------------------------
 *   the gradient IS the plate       a faint wash behind white
 *   type is WHITE on the gradient   dark text on white
 *   nav sits on the contact line    three stacked columns below it
 *   wordmark full-bleed, cropped    floating, with a dead half beside it
 *   social are links in a bottom bar buried mid-panel
 *
 * The result was a pale card with an empty right half, and since it stacked taller than a
 * viewport the only screen a visitor reached was the emptiest one.
 *
 * ONE DELIBERATE DEPARTURE. The reference carries five nav links in a single row; we carry
 * 49, which no row holds. So the nav is a compact three-column block in the position the
 * reference puts its row - beside the contact block - preserving the structure
 * (statement -> contact + nav -> wordmark -> legal bar) while actually holding our
 * content. Nothing was trimmed to make it fit.
 *
 * THE WORDMARK IS CROPPED ON PURPOSE. Sized in vw and allowed to run past both edges, cut
 * by the plate's overflow. That is what makes it read as a mark the panel was cut from
 * rather than a word placed on it. */
.vk-footer {
  padding: clamp(14px, 2.2vw, 34px);
  background: var(--vk-space);
  /* THE FOOTER OWNS ITS TYPEFACE. IT MUST NOT INHERIT ONE.
   *
   * This component is dropped onto pages whose <body> sets whatever font that page
   * wanted, and it was inheriting it. On index.html the body computes to
   * `"Fragment Mono", monospace`, so the whole footer - including the 340px
   * wordmark - was rendering in a MONOSPACE face. Monospace forces every glyph to
   * one advance width, which is exactly the "V e d i k a" spacing that got
   * reported, and it is why the full stop came out as a big square block.
   * Measured on the live page: every element from <footer> down to
   * .vk-footer__wordmark computed to Fragment Mono, inherited from body.
   *
   * A shared component cannot rely on inheritance for anything it cares about. */
  font-family: var(--vk-font-body);
}

/* THE FOOTER OWNS ITS BOX MODEL TOO, FOR THE SAME REASON.
 *
 * The box-sizing reset at the top of this file is scoped to `.vk-page`, because
 * dropping a universal reset onto 400 pages that never asked for it is how this
 * stylesheet repainted 364 pages once already. But the FOOTER is dropped onto
 * those pages regardless, and index.html's body is `bg-white vedika-cinematic-home`
 * with no `vk-page` - so the footer was laying out in content-box.
 *
 * Measured at 1440: the plate is 1354 wide with 48px padding, so its children
 * should be 1258. They measured 1354 and ran 43px PAST the plate's right edge. At
 * 400 the wash resolved to -40..360 against a 0..400 plate, leaving 40px of the
 * right side with no gradient on it at all - the "weird white margin on right
 * side", and the hard-edged pale band down the right of the mobile footer.
 *
 * Scoped to the component, so it still cannot touch a page that only wanted a
 * footer. */
.vk-footer,
.vk-footer *,
.vk-footer *::before,
.vk-footer *::after { box-sizing: border-box; }

/* THE SINGULARITY RE-SKIN WAS WRITTEN FOR THE OLD DARK FOOTER.
 *
 * js/vedika-singularity.js adds `.sing-arr-footer` to whatever footer the page has,
 * and the stylesheet then paints it `color: #cdc5e6 !important` - a pale lavender,
 * correct against the slate `#0F172A` ground the footer used to carry inline.
 * That footer is now a LIGHT plate, so the re-skin leaves pale lavender text on a
 * pale gradient: measured rgb(205,197,230) on the statement, the columns and the
 * contact block, which is the unreadable copy that got reported.
 *
 * Fixed here rather than in the singularity sheet because the ownership is here:
 * this component decides what colour its own text is, on every page, in every
 * engine state. Scoped to the plate so it cannot affect any other footer the
 * engine re-skins.
 *
 * ONLY the plate is re-coloured, NOT its descendants. A `* { color: inherit }`
 * here would have flattened every deliberate colour underneath it - the link
 * violet, the column heads, the reach link - into one flat ink. Countering the
 * !important on the plate is enough: children that set their own colour keep it,
 * and children that were inheriting now inherit ink instead of pale lavender. */
.vk-footer.sing-arr-footer .vk-footer__plate {
  color: var(--vk-ink) !important;
}

.vk-footer__plate {
  position: relative;
  overflow: hidden;
  border-radius: clamp(18px, 2vw, 30px);
  padding: clamp(26px, 3.4vw, 54px) clamp(22px, 3vw, 48px) clamp(14px, 1.4vw, 22px);
  isolation: isolate;
  display: grid;
  gap: clamp(12px, 1.5vw, 20px);
  background: #F3ECEF;
}

/* THE GRADIENT IS THE PLATE, NOT A TINT BEHIND IT.
 *
 * "the bg is too less". The previous version peaked at 0.55 alpha on a single stop and
 * read as a wash. These reach 0.95 and overlap, which is what produces the folded,
 * back-lit look rather than a flat ramp: one gradient reads as a colour fade, five
 * offset ones read as light. Our palette - violet, azure, rose, gold - not the
 * reference's red and teal. */
.vk-footer__wash {
  position: absolute;
  /* INSET 0 PLUS A SCALE, NOT NEGATIVE INSETS.
   *
   * `inset: -10%` should resolve to a box 1.2x the plate. Measured on the live page
   * it resolved to left:-10% with the width still exactly 1.0x, so the gradient
   * stopped short of the right edge and left a hard-edged pale band there - 40px at
   * a 400px viewport, 144px at 1440. That is the "weird white margin on right side",
   * and it was the most visible thing in the mobile footer.
   *
   * inset:0 has one unambiguous resolution, and the scale then guarantees the cover
   * geometrically rather than through inset arithmetic. It is also compositor-only,
   * so the oversize costs nothing. */
  inset: 0;
  transform: scale(1.24);
  transform-origin: 50% 50%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 62% 78% at 6% 4%,   rgba(167,139,250,0.95) 0%, rgba(167,139,250,0) 62%),
    radial-gradient(ellipse 54% 70% at 88% 2%,  rgba(56,189,248,0.88) 0%,  rgba(56,189,248,0) 60%),
    radial-gradient(ellipse 70% 62% at 74% 96%, rgba(244,63,94,0.72) 0%,   rgba(244,63,94,0) 62%),
    radial-gradient(ellipse 58% 58% at 24% 92%, rgba(251,191,36,0.62) 0%,  rgba(251,191,36,0) 60%),
    radial-gradient(ellipse 46% 46% at 52% 46%, rgba(255,255,255,0.50) 0%, rgba(255,255,255,0) 70%);
  filter: saturate(1.12);
}
.vk-footer__plate > *:not(.vk-footer__wash) { position: relative; z-index: 1; }

.vk-footer__head { display: grid; gap: clamp(14px, 1.8vw, 24px); }

.vk-footer__statement {
  margin: 0;
  max-width: 18ch;
  font-family: var(--vk-font-body);
  font-size: clamp(26px, 3.6vw, 54px);
  line-height: 1.06;
  letter-spacing: -0.032em;
  font-weight: 600;
  /* DARK, NOT WHITE, AND THIS WAS SETTLED BY MEASURING PIXELS.
   *
   * The reference's statement is white because its gradient is genuinely saturated.
   * Ours is a pale iridescence and pale is the entire point of it, so white type had no
   * ground: measured from rendered pixels, 1.80:1 median and 1.32:1 at its worst
   * crossing - short even of the 3.0:1 large-text floor.
   *
   * THREE separate agents inspected this and all three called it a measurement
   * artifact, because a DOM ancestor-walk lands on the plate's flat #F3ECEF -
   * .vk-footer__wash is a SIBLING painted behind the text, invisible to the walk. Only
   * a screenshot shows the truth. It was a real defect on 400+ pages.
   *
   * Laying a deep pool under the corner got the median to 8.79:1 but left the worst
   * crossing at 2.63:1, because a gradient cannot guarantee a floor under every glyph
   * at every viewport width - the statement reflows, the gradient does not follow it.
   * Dark ink needs no such guarantee: it is legible against every colour this wash
   * produces, at any width. The reference's PRINCIPLE is that the statement is the
   * loudest thing in the footer, and 15:1 ink is louder than washed-out white. */
  color: var(--vk-ink);
  text-wrap: balance;
}

/* contact left, navigation right - the reference's contact line */
.vk-footer__main {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.35fr);
  gap: clamp(20px, 3vw, 56px);
  align-items: start;
}

.vk-footer__reachlabel {
  display: block;
  margin-bottom: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(28, 12, 40, 0.62);
}
.vk-footer__reach {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: clamp(16px, 1.5vw, 23px);
  font-weight: 600;
  letter-spacing: -0.018em;
  color: #1B0F2A;
  text-decoration: none;
  border-bottom: 2px solid rgba(27, 15, 42, 0.28);
  padding-bottom: 2px;
  transition: border-color 0.2s ease, gap 0.2s ease;
}
.vk-footer__reach:hover { border-color: #1B0F2A; gap: 15px; }
.vk-footer__reach svg { width: 0.7em; height: 0.7em; flex: 0 0 auto; }

.vk-footer__hours {
  margin: clamp(10px, 1.2vw, 16px) 0 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: rgba(28, 12, 40, 0.72);
}
.vk-footer__hours a { color: #3B1170; font-weight: 600; text-decoration: none; }
.vk-footer__hours a:hover { text-decoration: underline; }

.vk-footer__cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.8vw, 30px);
}
.vk-footer__colhead {
  margin: 0 0 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  /* SOLID, NOT 55% ALPHA. These headings sit on the brightest band of the footer
     gradient - the cyan/near-white stretch across the top - and at 0.55 alpha over
     that, PRODUCT was effectively invisible and RESOURCES and COMPANY were close
     behind. Reported as "lot of titles not readable", and correctly.
     10.5px at weight 700 is SMALL text, so the bar is 4.5:1, not 3:1. An alpha
     colour cannot be reasoned about against a gradient anyway - the effective ink
     changes with whatever is behind it - so this is a solid value. */
  color: #2A1840;
}
.vk-footer__cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 3px; }
.vk-footer__cols a {
  font-size: 12.5px;
  line-height: 1.24;
  color: #23122F;
  text-decoration: none;
  opacity: 0.86;
  transition: opacity 0.16s ease;
}
.vk-footer__cols a:hover { opacity: 1; text-decoration: underline; }

/* TAILWIND'S PREFLIGHT TURNS THE STATUS BADGE INTO ITS OWN ROW.
 *
 * `<a href="/status"><svg …/>Status</a>` is written as an inline badge followed
 * by its label, and the markup even carries `style="vertical-align:-1px"` —
 * which only means anything on an inline box. tailwind.min.css ships
 * `svg,video{display:block;vertical-align:middle}` in its preflight, so the svg
 * becomes a block, the inline style is inert, and the label is pushed to a
 * second line.
 *
 * Measured at 1440 and 390 on pricing.html and playground.html: computed
 * display `block`, the <a> occupying TWO line boxes, 94x35 where one line is
 * ~16px. Both inspection passes reported it independently as "an orphaned
 * purple check badge alone on its own line between Changelog and Status".
 *
 * 407 pages in the tree carry this footer markup, so this is fixed here rather
 * than in the markup. `vertical-align` is restored to what the inline style was
 * reaching for.
 *
 * The tel: link is the same class of bug with a different trigger: the resources
 * column carries "+91 7083408408" with a real space, and at 12.5px in a column
 * that narrow it breaks after "+91" and renders as two nav rows. The contact
 * block above it writes the same number with a hyphen and does not break, which
 * is why only one of the two wraps. nowrap, not a copy edit, because the number
 * itself must not change. */
.vk-footer__cols a > svg {
  display: inline-block;
  vertical-align: -1px;
}
.vk-footer__cols a[href^="tel:"] { white-space: nowrap; }

/* the wordmark, full bleed and cropped by the plate */
/* ══ THE NAV MOVES INTO THE SPACE BESIDE THE WORDMARK ═══════════════════════
 *
 * "the space on right side of vedika word needs all footer to be organized ...
 * every tab or detail which is missing ... and also the social media icons".
 * The wordmark ran the full width with nothing beside it, and the three nav
 * columns sat above in their own band, so the widest part of the footer was
 * empty and the navigation was the thing that got squeezed.
 *
 * Grid can only place DIRECT children, and the nav was two levels down inside
 * .vk-footer__main. That wrapper is now unwrapped in the markup - one element
 * removed, nothing else - so the wordmark and the nav are siblings and can share
 * a row.
 *
 * The wordmark is capped at 240px rather than 340 so the nav has real room: at
 * 1990 it is about 810px of glyph against roughly 1100px of column, instead of
 * 1150 against 770. It is still by far the largest thing in the footer. */
@media (min-width: 1000px) {
  .vk-footer__plate {
    grid-template-columns: minmax(0, 0.9fr) minmax(360px, 1.1fr);
    column-gap: clamp(24px, 3vw, 64px);
    align-items: start;
  }
  .vk-footer__legal { grid-column: 1 / -1; }
  /* LEFT COLUMN carries the statement and then the contact block beneath it.
     RIGHT COLUMN is nothing but links, and it SPANS BOTH of those rows so the nav
     floods the full height of the upper block rather than starting halfway down.
     That is the ask - "left side the top text ... below text our buttons, and right
     side is all footer link top to bottom" - and it is also what makes 49 links fit
     a fixed-height footer: they borrow the statement's vertical space instead of
     adding a band of their own. */
  .vk-footer__head { grid-column: 1; grid-row: 1; }
  /* THE REFERENCE PUTS THE NAV ON THE CONTACT ROW AND THE WORDMARK FULL WIDTH.
   *
   * First attempt put the nav beside the wordmark, reading the ask ("the space on
   * the right side of the Vedika word") as a description of the target rather than
   * of the empty space in the broken state. The reference the founder sent twice
   * does something different and better: the email sits bottom-left of the upper
   * block with the nav right-aligned on the SAME line, and the wordmark then runs
   * the full width beneath both.
   *
   * That is also what fixes the two measured faults at once. Sharing a row with the
   * contact block means the nav stops adding a band of its own - the footer was
   * 133-259px taller than its box - and a full-width wordmark has room for its own
   * glyphs, so it stops being clipped by .vk-footer__markrow's overflow:hidden. */
  .vk-footer__contact { grid-column: 1; grid-row: 2; align-self: end; }
  .vk-footer__cols    { grid-column: 2; grid-row: 1 / span 2; align-self: start; }
  .vk-footer__markrow { grid-column: 1 / -1; grid-row: 3; }
  /* SIZED TO FIT THE COLUMN, MEASURED - not picked.
     .vk-footer__markrow has overflow:hidden, and the wordmark's column cannot grow
     because the nav beside it claims minmax(300px, 1fr). At 13vw and again at 11vw
     the glyphs overran their box by a consistent ~8.6% and the final letter was cut
     clean off - "Vedik|a" - the same chopped-logo fault reported at the top of the
     page, in a different place.
     Measured overrun: 935 against 861 at 1600, 969/895 at 1990, 702/647 at 1200 -
     the same ratio each time, because both scale with vw. 11vw x (861/935) = 10.1vw
     is break-even, so 9.6vw carries real margin, and the side padding goes because
     it counted toward the overrun too. */
  /* .vk-footer PREFIX IS LOad-BEARING, NOT DECORATION.
     Written first as a bare `.vk-footer__wordmark` inside this media block, which is
     (0,1,0) - identical to the base rule further down the file. At equal specificity
     the LATER rule wins, and the base rule is later, so this never applied at all:
     measured font-size still 340px while I was reading the number as proof the cap
     had taken. The prefix makes it (0,2,0) and it wins wherever it sits. */
  .vk-footer .vk-footer__wordmark { font-size: clamp(56px, 9.6vw, 180px); padding-inline: 0; }

  /* EACH LIST FLOWS INTO TWO SUB-COLUMNS. This is the single biggest height saving
     in the footer and it applies at every desktop size, not just short ones: with
     it the content needs 67vh, without it 86.6vh. That difference is what lets the
     engine's height cap come back down to a value that leaves the arrival CTA its
     own frame, instead of the footer covering it.
     The three headings are untouched, so Product / Resources / Company stay three
     distinct groups - only each group's own links wrap into two runs. */
  .vk-footer .vk-footer__cols ul {
    display: block;
    columns: 2;
    column-gap: clamp(14px, 1.6vw, 26px);
  }
  .vk-footer .vk-footer__cols li { break-inside: avoid; margin-bottom: 1px; }
}

/* THE LAST 26-88px, AT THE DESKTOP SIZES WHERE THE BOX IS SHORTEST.
   1990 and 1600 fit exactly once the nav shares the statement's column height, but
   1440 ran 26px over and 1280 ran 88px, because the footer's box is a percentage of
   a viewport that is getting shorter while the 49 links are not. The statement is
   the tallest non-nav element and the cheapest to trim - it is a two-line headline,
   not content that can be lost. */
@media (min-width: 1000px) and (max-width: 1560px) {
  .vk-footer .vk-footer__statement { font-size: clamp(24px, 2.9vw, 40px); }
  .vk-footer .vk-footer__wordmark  { font-size: clamp(56px, 8.6vw, 150px); }
  .vk-footer .vk-footer__hours     { font-size: 12px; line-height: 1.5; }
  /* the plate's own padding is spent top AND bottom, so trimming it here is worth
     more than it looks - this is what closes 1440 completely */
  .vk-footer .vk-footer__plate     { padding-block: clamp(18px, 2vw, 30px) 12px; }
  .vk-footer .vk-footer__cols a    { font-size: 12px; line-height: 1.2; }
}

/* SHORT VIEWPORTS, KEYED ON HEIGHT NOT WIDTH.
   1280x800 still ran 54px over after the width-based tier, and the width tier could
   not see why: the footer's box is a PERCENTAGE OF VIEWPORT HEIGHT, so a short screen
   gets a short box regardless of how wide it is. Measured there - box 704, content
   758, and the nav column alone is 462 of it against a left column of 174, so the nav
   is the entire overflow and nothing else is worth trimming.
   14 links at 33px each becomes 14 at 29px. That is the last of the slack; below this
   the honest answer is that the footer scrolls, not that the type shrinks again. */
@media (min-width: 1000px) and (max-height: 920px) {
  /* THE LAST 9-24px COME FROM SPACING, NOT FROM TYPE.
     Measured at 1366x768: box 507, over 24. The plate's row gap is 20px and it is
     spent twice, and its top padding is 27.3px - 25px of slack sitting in whitespace
     while the alternative was shrinking 11.5px text again. Trimming space before
     trimming legibility is the right order, and it is the order I should have used
     one tier earlier. */
  .vk-footer .vk-footer__plate     { row-gap: 12px; padding-top: 18px; }
  /* 1024x768 is the narrowest desktop that still gets the two-column layout, and it
     was the last 10px. The wordmark is the only thing left with slack that is not
     text someone has to read. */
  .vk-footer .vk-footer__wordmark   { font-size: clamp(48px, 7vw, 118px); }
  .vk-footer .vk-footer__cols a    { font-size: 11.5px; line-height: 1.16; }
  .vk-footer .vk-footer__colhead   { margin-bottom: 4px; }
  .vk-footer .vk-footer__statement { font-size: clamp(22px, 2.5vw, 34px); }
  .vk-footer .vk-footer__wordmark  { font-size: clamp(52px, 7.6vw, 128px); }
}

/* ══ MOBILE GETS THE COMPACTION TIER IT WAS NEVER GIVEN ═════════════════════
 *
 * "in mobile view footer fucked or cropped ... and chopped in top".
 *
 * Every tier above is gated on `min-width: 1000px`. Three of them - the two-column
 * list flow, the 1.16 line-height, the trimmed heading margin - exist precisely to
 * make this footer fit a bounded box, and NONE of them reaches a phone. So at 390px
 * the footer ran the untuned base values and measured, at the ending:
 *
 *     plate 1392px  inside a 490px window   -> 67% off-screen at any instant
 *     of which .vk-footer__cols alone was 935px
 *
 * Two separate causes inside that 935:
 *
 *   1. THREE groups in a TWO-column grid wrap into two ROWS, so the nav costs
 *      495 + 428 + gap rather than the tallest group alone. Three columns in one
 *      row costs 495. Measured: 108px per column at 390px, and the longest label
 *      ("Postman Collection") sets ~96px at 11.5px, so nothing wraps.
 *   2. 39 links were spending ~33px per row, because `ul` inherits a 16px font and
 *      a 1.35 line-height while the anchors inside it are 12.5px. The row box was
 *      sized by a font nothing in it uses.
 *
 * Raising the engine's height cap is NOT the alternative lever, and this is worth
 * recording because it is the obvious first move: the footer is anchored to the
 * bottom of the frame and the arrival CTA's last capsule ends 26px above its top
 * edge, so there are 26px of slack up there, not the 900 the overflow needs. The
 * content height is the only thing that can actually move.
 *
 * All 39 links stay, in source order, in their three named groups. */
@media (max-width: 999px) {
  .vk-footer .vk-footer__cols {
    /* below ~360px three columns would drop under 90px and the longer labels would
       start wrapping, which costs back more height than the extra column saves */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  .vk-footer .vk-footer__colhead { font-size: 10px; margin-bottom: 3px; }
  .vk-footer .vk-footer__cols ul { gap: 0; }
  .vk-footer .vk-footer__cols a {
    font-size: 11.5px;
    /* display:block so the line box belongs to the 11.5px anchor rather than to the
       ul's inherited 16px - this is where two thirds of the saving comes from */
    display: block;
    line-height: 1.62;
  }
  .vk-footer .vk-footer__plate { row-gap: 12px; }
  .vk-footer .vk-footer__legal { row-gap: 8px; }
  .vk-footer .vk-footer__legaltext p { line-height: 1.45; }
}
@media (min-width: 360px) and (max-width: 999px) {
  .vk-footer .vk-footer__cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ══ THE WASH IS RE-CUT FOR A TALL BOX, BECAUSE PERCENT RADII CANNOT BE ═════
 *
 * "first appearing as oragnge then changing colour".
 *
 * There is no animation here to fix. .vk-footer__wash is a STATIC stack of five
 * radial-gradients - violet and azure across the top, rose and gold across the
 * bottom, a white lift through the middle. On the desktop plate, which is wide and
 * short, all five reach the eye in one glance and read as a single iridescence.
 *
 * Its radii are percentages of the plate, and on a phone the plate is 2-3.5x taller
 * than it is wide. A blob at 78% of height then spans 600-1100px, i.e. more than the
 * 490px window the ending shows it through. So the reader is walked down the plate
 * one hue at a time: a violet screen, then a white one, then an orange one. The
 * gradient never changed - the viewport did.
 *
 * The fix cannot be "shrink the blobs", because four corner blobs on a tall box
 * still leave the middle to one hue and each end to another. The palette has to
 * REPEAT down the height, so that any 490px window carries some of all four. Seven
 * shallow bands, cycling the same four brand hues in the same order, at lower alpha
 * because they now overlap far more than four corners did. */
@media (max-width: 999px) {
  .vk-footer .vk-footer__wash {
    /* 1.24 was sized to guarantee horizontal cover on a wide box; on a tall one it
       pushes 12% of the gradient off each end, which is where the flat #F3ECEF
       corners came from. 1.06 covers without throwing the band pattern away. */
    transform: scale(1.06);
    background:
      radial-gradient(ellipse 96% 15% at 10%  2%,  rgba(167,139,250,0.72) 0%, rgba(167,139,250,0) 70%),
      radial-gradient(ellipse 92% 14% at 88% 17%,  rgba(56,189,248,0.62) 0%,  rgba(56,189,248,0) 68%),
      radial-gradient(ellipse 90% 13% at 14% 32%,  rgba(251,191,36,0.44) 0%,  rgba(251,191,36,0) 66%),
      radial-gradient(ellipse 94% 14% at 84% 47%,  rgba(244,63,94,0.46) 0%,   rgba(244,63,94,0) 68%),
      radial-gradient(ellipse 96% 15% at 12% 63%,  rgba(167,139,250,0.66) 0%, rgba(167,139,250,0) 70%),
      radial-gradient(ellipse 92% 14% at 86% 79%,  rgba(56,189,248,0.58) 0%,  rgba(56,189,248,0) 68%),
      radial-gradient(ellipse 94% 15% at 22% 95%,  rgba(244,63,94,0.44) 0%,   rgba(244,63,94,0) 68%),
      radial-gradient(ellipse 120% 60% at 50% 50%, rgba(255,255,255,0.34) 0%, rgba(255,255,255,0) 78%);
    filter: saturate(1.06);
  }
}

/* THE WORDMARK NOW STARTS ON THE CONTENT EDGE, NOT THE PLATE EDGE.
 *
 * "vedika is too on left and not in straight line where content starts."
 * Measured at 1440 before this change: .vk-footer__head began at x=83 and the
 * wordmark row at x=40 - a 43px disagreement, which is exactly the plate's own
 * inline padding, clamp(22px, 3vw, 48px) = 43.2px at that width.
 *
 * The cause was a deliberate bleed: this row carried a negative inline margin of
 * precisely that padding, cancelling it so the glyphs could run to the plate's
 * rounded edge, and the wordmark then added its own separate padding on top. Two
 * different insets meant the largest element on the page was the one thing not on
 * the page's own left rule. Both are removed, so the row inherits the plate's
 * content box and the "V" lands on the same vertical as "Purpose-built". */
.vk-footer__markrow {
  margin: clamp(6px, 1.2vw, 16px) 0 0;
  overflow: hidden;
}
.vk-footer__wordmark {
  margin: 0;
  padding: 0;
  font-size: clamp(64px, 20.5vw, 340px);
  line-height: 0.78;
  font-weight: 700;
  letter-spacing: -0.05em;
  white-space: nowrap;
  color: #1A0E28;
  opacity: 0.9;
  user-select: none;
  pointer-events: none;
}
.vk-footer__wordmark b { color: #7C3AED; }

/* bottom bar: legal left, social right */
.vk-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: 10px clamp(14px, 2vw, 30px);
  align-items: center;
  justify-content: space-between;
  margin-top: clamp(4px, 0.8vw, 10px);
  padding-top: clamp(10px, 1.2vw, 16px);
  border-top: 1px solid rgba(27, 15, 42, 0.16);
  font-size: 11.5px;
  line-height: 1.7;
  color: rgba(28, 12, 40, 0.72);
}
.vk-footer__legal p { margin: 0; }
.vk-footer__legal a { color: #3B1170; text-decoration: none; }
.vk-footer__legal a:hover { text-decoration: underline; }
.vk-footer__legaltext { display: grid; gap: 1px; }

/* THE SUPPORT WIDGET SITS ON TOP OF THIS ROW.
 *
 * Measured at a 1990px viewport: the social row occupies 1766-1942 and the
 * floating widget 1922-1974, so the launcher covered the last icon outright.
 * The widget is fixed to the viewport corner and is not ours to move from a
 * footer stylesheet, so the row keeps clear of it instead. 64px is the
 * launcher's 52px plus its inset, and it only applies from the width where the
 * row is right-aligned in the first place. */
.vk-footer__social { display: flex; gap: 16px; align-items: center; }
@media (min-width: 901px) {
  .vk-footer__social { padding-right: 64px; }
}
.vk-footer__social a {
  color: rgba(28, 12, 40, 0.78);
  display: inline-flex;
  transition: color 0.18s ease, transform 0.18s ease;
}
.vk-footer__social a:hover { transform: translateY(-2px); }
.vk-footer__social svg { width: 16px; height: 16px; }

/* EACH NETWORK IN ITS OWN COLOUR.
 *
 * Six identical grey glyphs are a row of shapes; in their own colours they are
 * recognisable at a glance, which is the entire job of a social row. Targeted by
 * aria-label rather than by adding a class, so this reaches every page that already
 * carries the footer without touching a single line of markup - the labels are
 * already there because they have to be for screen readers.
 *
 * Every one of these is the network's published brand colour. Instagram's mark is
 * officially a gradient; a flat #E4405F is its documented solid fallback and is what
 * a 16px glyph can actually show. X and Threads are both black, which reads
 * correctly on this light plate. */
.vk-footer__social a[aria-label*="X ("],
.vk-footer__social a[aria-label*="Twitter"] { color: #0B0B0B; }
.vk-footer__social a[aria-label*="LinkedIn"]  { color: #0A66C2; }
.vk-footer__social a[aria-label*="YouTube"]   { color: #FF0000; }
.vk-footer__social a[aria-label*="Instagram"] { color: #E4405F; }
.vk-footer__social a[aria-label*="Facebook"]  { color: #1877F2; }
.vk-footer__social a[aria-label*="Threads"]   { color: #0B0B0B; }
.vk-footer__social a:hover { filter: brightness(1.15) saturate(1.2); }

/* ══ THE WORDMARK AS LIQUID GLASS ═══════════════════════════════════════════
 *
 * "vedika is blank or idk bad ... maybe make vedika text shiny liquid jelly glass".
 * It was a flat #1A0E28 fill at 0.9 opacity - a solid slab of ink sitting on the
 * most colourful surface on the site, which is exactly why it read as blank.
 *
 * Glass is not one colour, it is a RAMP plus a specular. So the fill becomes a
 * multi-stop gradient clipped to the glyphs: bright at the top where light enters,
 * deep through the middle where the body of the material is, then lifting again at
 * the base where the plate's own colour bounces back up into it. The two violet
 * stops are the footer's own --vk-cosmic family, so the letters pick up the plate
 * they sit on rather than fighting it.
 *
 * The specular travels. A highlight that never moves is a painted-on shine; one that
 * drifts reads as a surface catching light. 14s, which is slow enough to be noticed
 * rather than watched. This animates a paint property, which this file elsewhere
 * treats as expensive - the difference is that this is ONE element, not one per card,
 * so the cost that rules out per-widget gradients does not apply here.
 *
 * -webkit-text-fill-color is required alongside color:transparent - WebKit ignores
 * the colour property for clipped text without it and the glyphs come back solid. */
/* TWO STACKED BACKGROUND LAYERS ON THE ELEMENT ITSELF - not a pseudo-element.
 *
 * First attempt put the specular on a ::before using `content: attr(data-word)`, so
 * the highlight could be clipped to a second copy of the glyphs. No page carries a
 * data-word attribute, so that pseudo-element would have rendered an empty string
 * and the sheen would simply never have appeared - a silent no-op that looks like a
 * working rule in source. Both layers now live on the one element, which needs no
 * markup change on 400+ pages and cannot fail that way.
 *
 * Layer 1 is the travelling specular, layer 2 the glass body. Only the first layer's
 * position animates; `background-size` differs per layer so the sheen can be wider
 * than the box and sweep through it. */
.vk-footer__wordmark {
  background-image:
    linear-gradient(104deg,
      transparent 34%,
      rgba(255, 255, 255, 0.88) 46%,
      rgba(255, 255, 255, 0.34) 53%,
      transparent 63%),
    linear-gradient(178deg,
      #6B4FA8 0%, #34205A 16%, #1A0E28 42%,
      #241338 62%, #4A2F7D 84%, #8A6FC4 100%);
  background-size: 260% 100%, 100% 100%;
  background-position: 150% 0, 0 0;
  background-repeat: no-repeat, no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  /* the bevel: a light edge below and a dark one above reads as thickness */
  filter: drop-shadow(0 2px 1px rgba(255, 255, 255, 0.42))
          drop-shadow(0 -1px 1px rgba(26, 14, 40, 0.30));
  animation: vk-wordmark-sheen 14s ease-in-out infinite;
}
/* only the specular layer moves; the glass body stays put */
@keyframes vk-wordmark-sheen {
  0%   { background-position: 150% 0, 0 0; }
  55%  { background-position: -60% 0, 0 0; }
  100% { background-position: 150% 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
  .vk-footer__wordmark { animation: none; background-position: 42% 0, 0 0; }
}

/* THE FULL STOP GOES.
 *
 * At the wordmark's 295px it rendered as a large purple block - a square, because a
 * period in a monospace face fills a full advance width. The font fix above stops it
 * being a square, but at that size a violet dot the height of a fist is still the
 * loudest thing in the footer, and the reference wordmark this was built from carries
 * no punctuation at all. The element is inside an aria-hidden decorative wordmark, so
 * nothing is removed from the accessible name or the content - only from the picture. */
.vk-footer__wordmark b { display: none; }

@media (max-width: 900px) {
  .vk-footer__main { grid-template-columns: minmax(0, 1fr); }
  .vk-footer__statement { max-width: none; }
}
@media (max-width: 560px) {
  .vk-footer__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .vk-footer__legal { justify-content: flex-start; }
}

/* ── VDS TOKEN BRIDGE ──
 *
 * The site already had a token system (css/vedika-tokens.css + css/vedika-ds.css,
 * loaded on the homepage, pricing, login, dashboard and ~15 more pages) and its
 * geometry disagreed with this one by a few pixels. That disagreement is what "random
 * wrong size capsules" and mismatched cards actually were.
 *
 * Rather than override the components that read those tokens - which meant a
 * specificity fight on every page, since each page's own rules outrank a plain
 * selector - the two token VALUES are restated here. Every component that already
 * reads them lands on the shared geometry automatically:
 *
 *   .auth-shell            border-radius: var(--vds-radius-lg)      (login, dashboard)
 *   .vds-pill              min-height: var(--vds-tap-comfortable)
 *                          border-radius: var(--vds-radius-pill)    (site-wide)
 *   .vds-cardgrid__card    and the rest of the vds card family
 *
 * This file is linked AFTER vedika-tokens.css on every page that has both, so these
 * win on order at identical specificity. No !important anywhere.
 *
 * 44px is the accessibility FLOOR for a tap target, not a ceiling, so raising it to
 * 60px cannot regress it — the control only gets easier to hit. */
:root {
  --vds-radius-lg: 24px;          /* was 20px — the shared sheet radius */
  --vds-tap-comfortable: 60px;    /* was 44px — the shared capsule height */
}

/* ── CODE BLOCK, LIGHT ──
 *
 * Distinct from `.vk-code`, which is this file's original DARK terminal block
 * (background var(--vk-space), light text, syntax-colour spans) and keeps that job.
 * Naming this one `.vk-code` turned 524 blocks on light pages black - the exact defect it
 * was meant to fix.
 *
 * Applied via a class and never as a bare `pre` rule: 524 <pre> tags across 141 pages had
 * no class at all, so every rule written as pre[class*="language-"] missed them - including
 * mcp.html's own `background: #f8fafc !important` - and they fell through to Prism's theme,
 * a leftover dark rule, or the browser default. That is why code looked black on light
 * pages whose own stylesheet said otherwise. A bare `pre` rule here would repeat the
 * mistake that repainted 364 pages this morning.
 *
 * overflow-x: auto is load-bearing. One long unbroken line in a <pre> is the classic cause
 * of a page scrolling sideways on a phone; the block scrolls inside itself so the document
 * never does. */
/* STRUCTURE ONLY - NO BACKGROUND, NO COLOUR, AND THAT IS THE WHOLE LESSON HERE.
 *
 * The first version of this rule set background #F7F7FA and colour #1C1B22. Measured
 * afterwards: most pages ALREADY have a working code theme of their own - sdks.html,
 * webhooks.html and quickstart.html all render dark blocks at 11-12:1, which is fine and
 * deliberate. Setting a colour here only competed with them, and on api-reference.html it
 * lost in the worst way: the page's dark background survived, my dark text landed on it,
 * and the contrast measured 1.07:1 - unreadable text I introduced.
 *
 * So this component now contributes only what was genuinely missing and cannot conflict:
 * padding, radius, mono family, tab size, and the horizontal scroll. Pages keep their own
 * palette. Pages with no palette fall back to the browser default, which is black on white
 * and readable.
 *
 * The narrow defect that started this - mcp.html showing black blocks on a light page -
 * is fixed by the page's OWN `background: #f8fafc !important` rule finally reaching these
 * tags, now that they carry a class at all. Verified: mcp.html blocks measure
 * rgb(248,250,252) at 20.07:1. */
.vk-codeblock {
  margin: 0 0 1rem;
  padding: 16px 18px;
  border-radius: var(--vk-r-sm);
  font-family: var(--vk-font-mono);
  font-size: 13px;
  line-height: 1.62;
  tab-size: 2;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* Inline <code> usually carries a chip style - its own background, padding and colour.
 * Inside a block that chip stacks a second box on every line, so it is neutralised here
 * while inline code elsewhere on the page keeps it. */
.vk-codeblock code {
  background: none;
  padding: 0;
  border: 0;
  border-radius: 0;
  color: inherit;   /* inherit, never assert - see the note above */
  font-family: inherit;
  font-size: inherit;
  white-space: pre;
}
.vk-codeblock::-webkit-scrollbar { height: 8px; }
.vk-codeblock::-webkit-scrollbar-thumb { background: rgba(12, 12, 14, 0.18); border-radius: 4px; }
