/* =====================================================================
   Kubx design system. Component styling follows kree8.studio: a floating off-white sheet on a
   flat field, slate ink, Phudu extrabold uppercase headings, glossy white primary pills, solid
   ink secondary pills, white/70 cards with a masked gradient border, embossed double hairlines,
   150-200ms ease-in-out micro-interactions. Two themes from one set of tokens: light (:root, the
   Social Media page: html[data-theme="light"]) and dark (the block after :root: every other
   public page and the admin). Design tokens (type, radii, easing) are in :root and hold for both.
   Since 2026-10-18 the public pages take their type and accents after teamhive.org (owner): Panchang
   uppercase headings, Inter body, small letter-spaced labels in the accent, squarer buttons, one blue
   accent #1E40FF on every page (see "public site: headings, labels and buttons" at the end).
   No framework: plain CSS.
   Designer: colours, fonts, radii and easings are the variables in this first block.
   ===================================================================== */
:root {
  /* palette (RGB triplets so alpha can be applied with rgb(var(--x) / .5)) */
  --field: 236 236 236;       /* flat canvas #ECECEC behind the sheet */
  --c-bg: 246 246 246;        /* sheet #F6F6F6 */
  --c-surface: 255 255 255;   /* cards */
  --c-surface2: 238 238 238;  /* tiles, icon buttons #EEEEEE */
  --c-line: 222 222 222;      /* hairlines #DEDEDE */
  --c-ink: 10 10 10;          /* headings #0A0A0A */
  --c-ink2: 40 40 40;         /* body text #282828 */
  --c-ink3: 82 82 82;         /* secondary text #525252 */
  --c-muted: 115 115 115;     /* #737373 */
  --c-accent: 30 64 255;      /* the one accent of every page, #1E40FF (owner, 2026-10-18) */
  --accent-text: 30 64 255;   /* the accent as text: the accent itself on light, a lighter step on dark (6:1 on the dark sheet) */
  --accent-on-dark: color-mix(in srgb, rgb(var(--c-accent)) 64%, #fff); /* the accent lightened for text on dark backdrops (video heroes) */
  --c-graphite: 38 38 38;     /* dark grey #262626 */
  --c-ok: 35 127 0;           /* success green #237F00 */
  --c-fire: 234 88 12;        /* orange #EA580C: warnings (message length meter) */
  /* section tones: a .tone-* class sets --tone, which .toned uses for the small marker in front of a section's eyebrow
     (panels stay neutral: see .band) */
  --h-blue: 30 64 255; --h-violet: 109 40 217; --h-teal: 8 145 178; --h-green: 35 127 0; --h-fire: 234 88 12; --h-gold: 202 138 4;
  --c-err: 220 38 38;
  /* Roles that differ between the light and the dark theme. Use these, not #fff or the ink colours, for:
     --paper: raised surfaces (cards, pills on a track, inputs)   --edge: the light edge / emboss line on them
     --c-dark, --c-dark2: ink used as a FILL (dark panels, solid buttons, toasts, shadows); --c-ink* are for text
     --shade: the colour of the soft lavender drop shadows */
  --paper: 255 255 255; --edge: 255 255 255; --shade: 170 170 170;
  --c-dark: 10 10 10; --c-dark2: 38 38 38;
  --pop: 255 255 255; /* a floating menu (the dropdown list) */
  --dots-rest: .45; /* background dot field (kx.field): how strong the dots are at rest; the cursor and click rings are at full strength */

  /* type (both themes, public pages): Panchang for headings (self-hosted, @font-face below), Inter for text, Geist Mono
     for codes and figures (Google Fonts link in the site layout). The admin keeps Geist (html[data-division="admin"]). */
  --font-display: 'Panchang', 'Inter', system-ui, sans-serif; --font-body: 'Inter', system-ui, sans-serif; --font-mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --r-sheet: 24px; --r-card: 16px; --r-input: 12px; --r-btn: 12px;

  /* easing.dev curves */
  --ease-out: cubic-bezier(0, 0, .2, 1);
  --ease-in-out: cubic-bezier(.4, 0, .2, 1);
  --ease-snappy: cubic-bezier(.19, 1, .22, 1);
  --ease-swift: cubic-bezier(.175, .885, .32, 1.1);
  --ease-overshoot: cubic-bezier(.175, .885, .32, 1.275);
  --ease-knob: cubic-bezier(.2, .8, .2, 1);
  --ease-expo: cubic-bezier(.16, 1, .3, 1);
  --dur-quick: 150ms; --dur-base: 200ms;

  /* kree8 shadows: stacked pale lavender, plus a glossy inset on pills */
  --sh-sheet: 0 1.67px 4.18px rgb(var(--shade) / .31), 0 8.37px 8.37px rgb(var(--shade) / .27), 0 17.57px 10.88px rgb(var(--shade) / .16), 0 31.8px 12.55px rgb(var(--shade) / .05), 0 50.21px 14.23px rgb(var(--shade) / .01);
  --sh-pill: 0 1.67px 4.18px rgb(var(--shade) / .31), 0 17.57px 10.88px rgb(var(--shade) / .16), 0 31.8px 12.55px rgb(var(--shade) / .05), 0 50.21px 14.23px rgb(var(--shade) / .01), inset 0 7px 32px rgb(var(--paper));
  --sh-card: 0 3px 4px rgb(0 0 0 / .06);
  --sh-card-hover: 0 16px 25px -12px rgb(var(--c-dark2) / .27);
}
/* ---------- dark theme: every page but Social Media (Game Development, the legal pages on a game start, the admin) ----------
   Rules written for the dark theme are scoped html:not([data-theme="light"]). The site layout sets data-theme="light"
   on division social only (Social Media and its case studies; owner, 2026-10-18), and kx.router copies it on a swap;
   the admin never sets it.
   Design rules that hold for both themes are scoped html[data-division] instead (same weight; see "both themes").
   Same roles, dark values. Every surface and text colour is a pure grey (R = G = B, no hue, no tint): the page is
   built from small steps in brightness only, near black like teamhive.org: field 5 < sheet 10 < wells 17 < band ~19 <
   paper 26, and text from near-white down to muted. Colour is left to the accents and tones, a step brighter than in the light
   theme so they read as text on dark. Component rules that need more than a token are further down
   ("dark theme: components"). */
html:not([data-theme="light"]) {
  color-scheme: dark;
  --field: 5 5 5; --c-bg: 10 10 10; --paper: 26 26 26; --c-surface: 26 26 26; --c-surface2: 17 17 17; --c-line: 40 40 40; --edge: 64 64 64;
  --c-ink: 245 245 245; --c-ink2: 208 208 208; --c-ink3: 166 166 166; --c-muted: 122 122 122;
  --c-dark: 0 0 0; --c-dark2: 48 48 48; --c-graphite: 48 48 48;
  --dots-rest: .34; /* background dot field (kx.field): how bright the dots are at rest; under the cursor and in a click ring they are at full strength */
  --c-accent: 30 64 255; --accent-text: 120 140 255; --c-ok: 82 200 92; --c-fire: 251 133 60; --c-err: 239 68 68;
  --h-blue: 120 140 255; --h-violet: 151 117 250; --h-teal: 34 190 220; --h-green: 82 200 92; --h-fire: 251 133 60; --h-gold: 234 179 8;
  --shade: 0 0 0;
  --pop: 48 48 48; /* a floating menu (the dropdown list): one step above the card it opens over */
  --sh-sheet: 0 2px 6px rgb(0 0 0 / .4), 0 24px 48px -20px rgb(0 0 0 / .6);
  --sh-pill: 0 2px 5px rgb(0 0 0 / .4), 0 14px 22px -12px rgb(0 0 0 / .55);
  --sh-card: 0 3px 6px rgb(0 0 0 / .25);
  --sh-card-hover: 0 18px 30px -14px rgb(0 0 0 / .7);
}
/* the admin keeps its own type (Geist), the public pages have Panchang and Inter */
html[data-division="admin"] { --font-display: 'Geist', 'Inter', system-ui, sans-serif; --font-body: 'Geist', 'Inter', system-ui, sans-serif; }
/* Panchang (Indian Type Foundry, Fontshare; free for commercial use), self-hosted so no third font host is called */
/* one weight, semibold (owner, 2026-10-18: the extra bold was too thick); any heavier request falls back to it */
@font-face { font-family: 'Panchang'; src: url('../fonts/panchang-600.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; color-scheme: light; }
body {
  margin: 0; min-height: 100dvh;
  font-family: var(--font-body); font-size: 16px; line-height: 1.6; font-weight: 500;
  color: rgb(var(--c-ink2)); background: rgb(var(--field));
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  transition: background-color .6s var(--ease-in-out);
  padding: 0 clamp(8px, 2vw, 24px);
}
img, video, canvas, svg, iframe { display: block; max-width: 100%; }
[hidden] { display: none !important; }
.grid > *, .hero-grid > *, .row-between > * { min-width: 0; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4, p, ul, ol, dl, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
:focus-visible { outline: 2px solid rgb(var(--c-accent)); outline-offset: 3px; border-radius: 6px; }
::selection { background: rgb(var(--c-accent) / .2); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip { position: fixed; top: 8px; left: 8px; z-index: 200; padding: .5rem 1rem; background: rgb(var(--c-dark2)); color: #fff; border-radius: 999px; transform: translateY(-200%); }
.skip:focus { transform: none; }

/* ---------- background field (kx.field): WebGL dot grid behind the sheet, reacts to the cursor ----------
   --dots-rest (0..1, default 1) dims the dots at rest; the cursor highlight and click rings stay at full strength. */
.bg-fx { position: fixed; inset: 0; z-index: -1; width: 100%; height: 100%; pointer-events: none; opacity: 0; transition: opacity 1.2s var(--ease-in-out); }
/* on the public pages the field lies OVER the page, see-through, under the top bar (owner, 2026-10-25: "just overlay
   the dots from the side and lower their opacity and size towards the center"): every section is solid to the window's
   edge, and the dots, full at the window's sides, shrink and fade to nothing by the content column, so they never sit
   on the copy and no background gradient is left to band */
html:not([data-division="admin"]) .bg-fx { z-index: 55; }
.bg-fx.is-on { opacity: 1; }
/* the fallback when WebGL is missing or fails (kx.field): the same 11px grid of dots as plain CSS, still, in the ink
   colour at the field's resting strength */
.bg-fx.is-css { opacity: 1; background: radial-gradient(circle at center, rgb(128 128 128 / calc(.3 * var(--dots-rest, 1))) 0 1px, transparent 1.6px) 0 0 / 11px 11px;
  -webkit-mask-image: linear-gradient(90deg, #000, transparent calc(50% - 560px), transparent calc(50% + 560px), #000); mask-image: linear-gradient(90deg, #000, transparent calc(50% - 560px), transparent calc(50% + 560px), #000); } /* the WebGL field's stand-in: the same grey dots, only at the sides */

/* ---------- layout ---------- */
.sheet {
  position: relative; max-width: 1100px; margin: 84px auto 24px; background: rgb(var(--c-bg)); color: rgb(var(--c-ink2));
  border-radius: var(--r-sheet); box-shadow: var(--sh-sheet); overflow: clip;
}
.wrap { padding-inline: clamp(16px, 4vw, 48px); }
.section { padding-block: clamp(48px, 7vw, 88px); }
.section-tight { padding-block: clamp(28px, 4vw, 48px); }
/* kree8 embossed rule: white highlight over a hairline */
.rule { height: 0; border-top: 1px solid rgb(var(--c-line)); box-shadow: 0 1px 0 rgb(var(--edge) / .5); }
.grid { display: grid; gap: 14px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.row { display: flex; align-items: center; gap: 12px; }
.row-between { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.stack > * + * { margin-top: 16px; }
.center { text-align: center; }
.mt-2 { margin-top: 8px; } .mt-3 { margin-top: 12px; } .mt-4 { margin-top: 16px; } .mt-6 { margin-top: 24px; } .mt-8 { margin-top: 32px; } .mt-10 { margin-top: 40px; }
.max-sm { max-width: 34rem; } .max-md { max-width: 44rem; } .max-lg { max-width: 56rem; } .mx-auto { margin-inline: auto; }
.hide-sm { display: none; }
@media (min-width: 640px) { .hide-sm { display: initial; } .only-sm { display: none !important; } }
@media (max-width: 1023px) { .grid-5 { grid-template-columns: repeat(2, 1fr); } .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 767px)  { .grid-3 { grid-template-columns: 1fr; } .grid-2 { grid-template-columns: 1fr; } }
@media (max-width: 479px)  { .grid-4 { grid-template-columns: repeat(2, 1fr); } .grid-5 { grid-template-columns: 1fr; } }

/* ---------- typography (kree8: Phudu extrabold uppercase, Inter 500 body at 160%) ---------- */
.display { font-family: var(--font-display); font-weight: 800; text-transform: uppercase; letter-spacing: -.02em; line-height: .96; color: rgb(var(--c-ink)); }
.h1 { font-size: clamp(2.2rem, 6vw, 4.4rem); letter-spacing: -.03em; }
.h2 { font-size: clamp(1.5rem, 2.8vw, 2rem); letter-spacing: -.02em; line-height: 1.05; }
.h3 { font-family: var(--font-display); font-weight: 700; font-size: 1.15rem; letter-spacing: -.01em; line-height: 1.15; color: rgb(var(--c-ink)); text-transform: uppercase; }
.lead { font-size: clamp(1.05rem, 1.4vw, 1.25rem); line-height: 1.6; letter-spacing: -.018em; color: rgb(var(--c-ink2)); }
.eyebrow { font-size: .78rem; letter-spacing: .02em; text-transform: uppercase; font-weight: 600; color: rgb(var(--c-ink2) / .4); }
/* eyebrow + heading module: tight pairing (overrides the mt-* utilities used in templates) */
.eyebrow + .h2, .eyebrow + .h3, .eyebrow + .h2.mt-2, .eyebrow + .h2.mt-3 { margin-top: 6px; }
.eyebrow + .h1, .eyebrow + .h1.mt-4, .hero-copy .eyebrow + .h1 { margin-top: 10px; }
.muted { color: rgb(var(--c-muted)); }
.secondary { color: rgb(var(--c-ink3)); }
.small { font-size: .875rem; }
.mono { font-family: var(--font-mono); font-size: .8rem; letter-spacing: .02em; }
.accent { color: rgb(var(--c-accent)); }
.link { color: rgb(var(--c-accent)); font-weight: 600; display: inline-flex; align-items: center; gap: .35rem; transition: opacity var(--dur-quick) var(--ease-in-out); }
.link:hover { opacity: .7; }
.is-morphing { font-variant-numeric: tabular-nums; }

/* ---------- top bar + division switch ---------- */
/* view-transition-name: on a full page load (see @view-transition below) the bar is captured on its own, so it stays
   put instead of sliding and fading with the page. In-site navigation goes through kx.router and never repaints it. */
.topbar { position: fixed; inset: 0 0 auto; z-index: 60; padding: 14px clamp(8px, 2vw, 24px) 0; pointer-events: none; view-transition-name: topbar; }
/* the bar "swallows" the page: content scrolling under it fades into the field colour above the pill. The fade ends
   at the pill's lower edge (it used to run 36px below it with a masked blur, which on the dark pages drew a dark band
   with a hard edge under the bar; owner, 2026-10-18) */
.topbar::before {
  content: ''; position: absolute; inset: 0 0 8px; z-index: -1; pointer-events: none;
  background: linear-gradient(rgb(var(--field)) 0%, rgb(var(--field) / .85) 45%, rgb(var(--field) / 0) 100%);
}
.topbar-inner {
  pointer-events: auto; max-width: 1100px; margin: 0 auto; height: 58px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
  padding: 0 8px 0 18px; border-radius: 999px; background: rgb(var(--c-bg) / .9); border: 1px solid rgb(var(--edge) / .6);
  box-shadow: var(--sh-sheet); backdrop-filter: blur(18px) saturate(1.3); -webkit-backdrop-filter: blur(18px) saturate(1.3);
}
.brand { font-family: var(--font-display); font-weight: 800; letter-spacing: -.02em; font-size: 1.15rem; color: rgb(var(--c-ink)); display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; transition: opacity var(--dur-quick) var(--ease-in-out); }
.brand:hover { opacity: .7; }
.brand-dot { width: 9px; height: 9px; border-radius: 50%; background: rgb(var(--c-accent)); transition: background-color .5s var(--ease-in-out); }
/* the Kubx Studio logo of the footer and the admin (partials/logo.php): the four-tile mark and the two-line lettering
   beside it, in the text colour. --logo-h is the mark's height: a multiple of 9 keeps its tiles (4/9) and the gaps
   between them (1/9) on whole pixels. The gap between the two halves is the space the logo leaves between mark and
   lettering. */
.brand:has(.brand-logo) { gap: calc(var(--logo-h, 36px) * .247); }
.brand-logo { flex: none; height: var(--logo-h, 36px); width: auto; overflow: visible; }
/* the top bar has the one-row KUBX logo (partials/logo-bar.php): its strokes are a quarter of its height, so a
   multiple of 4 keeps them on whole pixels */
.topbar .brand { --logo-h: 20px; }
/* an uploaded logo (custom_logo()): one <img> per colour scheme, in its own colours; the other scheme's is hidden */
.brand-logo-img { height: var(--logo-h, 36px); width: auto; max-width: min(240px, 50vw); object-fit: contain; }
/* in the top bar an uploaded logo is sized to the bar (58px), not to the thin built-in KUBX row */
.topbar .brand-logo-img { height: 30px; max-width: min(200px, 42vw); }
html[data-theme="light"] .logo-on-dark, html:not([data-theme="light"]) .logo-on-light { display: none; }
.switch {
  position: relative; display: inline-flex; align-items: center; padding: 4px; border-radius: 999px; background: rgb(var(--c-surface2));
  border: 1px solid rgb(var(--c-line)); isolation: isolate; justify-self: center;
}
.switch-thumb {
  position: absolute; top: 0; left: 0; z-index: 0; opacity: 0; border-radius: 999px; background: rgb(var(--paper));
  box-shadow: 0 1.67px 4.18px rgb(var(--shade) / .31), 0 8px 10px -6px rgb(var(--shade) / .5), inset 0 4px 12px rgb(var(--paper)); border: 1px solid rgb(var(--edge) / .4);
  transition: transform .42s var(--ease-swift), width .42s var(--ease-swift), height .2s, opacity .2s;
  will-change: transform, width; /* no view-transition-name: a thumb lifted into its own layer is painted over the labels and icons */
}
.switch.is-pressing .switch-thumb { transition-duration: .12s; transition-timing-function: var(--ease-knob); }
.switch-item {
  position: relative; z-index: 1; padding: 8px 16px; border-radius: 999px; font-size: .9rem; font-weight: 500; letter-spacing: 0; white-space: nowrap;
  color: rgb(var(--c-ink3)); transition: color var(--dur-base) var(--ease-in-out), opacity var(--dur-quick) var(--ease-in-out), transform .12s var(--ease-knob); -webkit-tap-highlight-color: transparent; user-select: none;
}
.switch-item:hover { opacity: .7; }
.switch-item:active { transform: scale(.97); }
.switch-item.is-on { color: rgb(var(--c-ink)); font-weight: 600; opacity: 1; }
.switch-item .ico { display: inline-block; width: 16px; height: 16px; margin-right: 8px; vertical-align: -3px; opacity: .55; transition: opacity var(--dur-base), color var(--dur-base); }
.switch-item:hover .ico, .switch-item:active .ico { opacity: 1; } /* the item itself dims on hover; the icon must not fade twice */
/* .switch-label[data-text]: the label reserves the width of its bold (active) state, so making an item active
   never resizes it or shifts its neighbours */
.switch-label { display: inline-grid; }
.switch-label::after { content: attr(data-text); height: 0; overflow: hidden; visibility: hidden; font-weight: 600; pointer-events: none; user-select: none; }
.switch-item.is-on .ico { opacity: 1; }
.switch-item.is-on .ico { color: rgb(var(--accent-text)); } /* the open page's icon in the accent (one blue on every page) */
/* radio version (Settings > Start page): no JS, the checked radio takes the thumb's look */
.seg-radio .switch-item { cursor: pointer; }
.seg-radio .switch-item input { position: absolute; opacity: 0; inset: 0; margin: 0; pointer-events: none; }
.seg-radio .switch-item:has(input:checked) { color: rgb(var(--c-ink)); font-weight: 600; background: rgb(var(--paper)); box-shadow: 0 1.67px 4.18px rgb(var(--shade) / .31), 0 8px 10px -6px rgb(var(--shade) / .5); }
.seg-radio .switch-item:has(input:focus-visible) { outline: 2px solid rgb(var(--c-accent)); outline-offset: 2px; }
.switch-item .short { display: none; }
.topbar-cta { justify-self: end; display: inline-flex; gap: 8px; }
@media (max-width: 820px) {
  .topbar-inner { grid-template-columns: auto 1fr auto; padding-left: 14px; }
  .switch-item { padding: 8px 12px; font-size: .84rem; }
  .switch-item .long { display: none; } .switch-item .short { display: inline-grid; }
  .switch-item[data-key="home"] .ico { margin-right: 0; } /* Home is icon-only on small screens */
  .topbar-cta .btn { padding: 9px 14px; font-size: .84rem; }
}
.switch-item .ico { max-width: none; } /* the reset's max-width: 100% let an icon-only item (Home on small screens) collapse to nothing */
/* segmented toggle (kx.segmented): the same pill + sliding thumb as the division switch, for in-page choices */
.seg .switch-item { padding: 8px 14px; font-size: .86rem; }
.seg-sm .switch-item { padding: 5px 12px; font-size: .78rem; }

/* ---------- buttons (kree8: glossy white primary pill, solid ink secondary, small icon buttons) ---------- */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center; gap: .55rem; padding: 15px 22px; border-radius: 50px;
  font-weight: 500; font-size: .95rem; line-height: 1; white-space: nowrap; cursor: pointer; border: 1px solid transparent;
  transition: transform var(--dur-quick) var(--ease-knob), box-shadow var(--dur-base) var(--ease-in-out), background-color var(--dur-base) var(--ease-in-out), color var(--dur-base), opacity var(--dur-base) var(--ease-in-out);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: scale(.97); }
.btn svg { width: 16px; height: 16px; flex: none; transition: transform var(--dur-base) var(--ease-in-out); }
.btn:hover svg.arrow { transform: translateX(3px); }
.btn-primary { background: #fff; color: rgb(var(--c-dark2)); border-color: rgb(255 255 255 / .4); box-shadow: inset 0 7px 32px #fff, var(--sh-pill); } /* white in both themes */
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgb(var(--shade) / .4), 0 22px 20px -10px rgb(var(--shade) / .5), inset 0 7px 32px #fff; }
.btn-solid { background: rgb(var(--c-dark2)); color: #fff; }
.btn-solid:hover { opacity: .85; }
/* aura button ("Work with us", "Send message"): liquid-metal fill and glow in the accent (the blue of every page).
   The one place that uses a colour gradient.
   ::before carries the fill: bands of the accent (lighter, plain, darker) sweep across as --aura-angle turns, under a
   gloss; ::after stays free for the loading spinner. */
@property --aura-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.btn-aura {
  --aura: rgb(var(--c-accent));
  --aura-hi: color-mix(in srgb, rgb(var(--c-accent)) 60%, #fff);
  --aura-lo: color-mix(in srgb, rgb(var(--c-accent)) 56%, #08121c);
  isolation: isolate; overflow: hidden; color: #fff; background: var(--aura-lo); border-color: rgb(255 255 255 / .24); text-shadow: 0 1px 2px rgb(0 0 0 / .35);
  box-shadow: 0 0 0 1px rgb(var(--c-accent) / .2), 0 2px 5px rgb(var(--c-accent) / .35), 0 10px 24px -8px rgb(var(--c-accent) / .8);
}
.btn-aura::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background:
    linear-gradient(180deg, rgb(255 255 255 / .36) 0%, rgb(255 255 255 / 0) 48%, rgb(0 0 0 / .22) 100%),
    radial-gradient(70% 130% at 18% -10%, rgb(255 255 255 / .38), transparent 62%),
    conic-gradient(from var(--aura-angle) at 50% 130%, var(--aura-lo), var(--aura), var(--aura-hi), var(--aura), var(--aura-lo), var(--aura-hi), var(--aura), var(--aura-lo));
  animation: aura-turn 7s linear infinite;
}
@keyframes aura-turn { to { --aura-angle: 360deg; } }
.btn-aura:hover { transform: translateY(-1px); box-shadow: 0 0 0 1px rgb(var(--c-accent) / .3), 0 3px 8px rgb(var(--c-accent) / .45), 0 14px 30px -8px rgb(var(--c-accent) / .95); }
.btn-aura:active { transform: scale(.97); }
.btn-aura.is-loading::after { border-color: rgb(255 255 255 / .35); border-top-color: #fff; }
/* not ready to send (contact() in site.js disables the button until the form is filled in): flat grey, no aura, no glow */
.btn:disabled:not(.is-loading) { cursor: not-allowed; }
.btn-aura:disabled:not(.is-loading) { background: rgb(var(--c-line)); color: rgb(var(--c-muted)); border-color: transparent; box-shadow: none; text-shadow: none; transform: none; }
.btn-aura:disabled:not(.is-loading)::before { display: none; }
.btn-aura:disabled:not(.is-loading) svg.arrow { transform: none; }
.btn-ghost { border-color: rgb(var(--c-line)); color: rgb(var(--c-ink2)); background: rgb(var(--c-surface2)); }
.btn-ghost:hover { background: rgb(var(--paper)); }
.btn-sm { padding: 10px 16px; font-size: .85rem; }
.btn-icon { width: 40px; height: 40px; padding: 0; border-radius: 10px; background: rgb(var(--c-surface2)); border: 1px solid rgb(var(--edge) / .4); color: rgb(var(--c-ink2)); display: inline-grid; place-items: center; transition: transform var(--dur-quick), background var(--dur-quick); }
.btn-icon:hover { background: rgb(var(--paper)); }
.btn-icon:active { transform: scale(.95); }
.btn-block { width: 100%; }
.btn.is-loading { color: transparent !important; pointer-events: none; }
.btn.is-loading::after { content: ''; position: absolute; width: 18px; height: 18px; border-radius: 50%; border: 2px solid rgb(var(--c-ink2) / .25); border-top-color: rgb(var(--c-ink2)); animation: spin .8s linear infinite; }
.btn-solid.is-loading::after { border-color: rgb(255 255 255 / .35); border-top-color: #fff; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- cards (kree8: white/70, rounded-xl, masked gradient border, soft shadow) ---------- */
.card {
  position: relative; background: rgb(var(--paper) / .7); border-radius: var(--r-card); box-shadow: var(--sh-card);
  transition: transform var(--dur-base) var(--ease-in-out), box-shadow var(--dur-base) var(--ease-in-out);
}
.card::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none;
  background: linear-gradient(98deg, rgb(var(--edge) / .9) 30%, rgb(var(--edge) / .1) 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.card-pad { padding: clamp(18px, 2.4vw, 26px); }
.card-hover { transition-duration: .25s; }
.card-hover:hover { transform: translateY(-6px); box-shadow: var(--sh-card-hover); }
.card-solid { background: rgb(var(--paper)); }
.tile { border-radius: 12px; background: rgb(var(--c-surface2)); border: 1px solid rgb(var(--edge) / .4); }

/* border beam: a light pulse travelling around the card edge (add .beam; --beam-color sets the colour).
   Drawn by a conic highlight masked to a 1.5px ring; the angle is a registered custom property so it animates. */
@property --beam-angle { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
.beam { position: relative; --beam-color: rgb(var(--c-accent)); --beam-speed: 11s; }
.beam::after {
  content: ''; position: absolute; inset: 0; z-index: 2; border-radius: inherit; padding: 2px; pointer-events: none; /* inside the box, so overflow:hidden cards keep it */
  background: conic-gradient(from var(--beam-angle), transparent 0deg, transparent 250deg, var(--beam-color) 320deg, transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: beam-spin var(--beam-speed) linear infinite;
}
@keyframes beam-spin { to { --beam-angle: 360deg; } }
.beam-game, .beam-social { --beam-color: rgb(var(--c-accent)); }
/* division colours for inline text: both divisions share the one accent since 2026-10-18 */
.c-game, .c-social { color: rgb(var(--accent-text)); }
/* the headline's second colour (two_tone(), owner 2026-10-25): the blue that reads well on the page's sheet (the accent
   on the light page, the lighter step on the dark one), in both themes */
.display .hl { color: rgb(var(--accent-text)); }

/* inline icons (helpers.php icon()): sized by context */
svg.ico { width: 14px; height: 14px; flex: none; vertical-align: -2px; }
.chip svg.ico { width: 12px; height: 12px; margin-right: 1px; opacity: .85; }
.kv dt svg.ico { width: 12px; height: 12px; margin-right: 5px; opacity: .75; }
.eyebrow-ico { display: inline-flex; align-items: center; gap: 6px; }
.eyebrow-ico svg.ico { width: 14px; height: 14px; }
.link svg.ico { width: 14px; height: 14px; }
.h-ico { display: flex; align-items: center; gap: 12px; }
.h-ico .stat-ico { width: 38px; height: 38px; }
.table .actions svg.ico { width: 13px; height: 13px; margin-right: 4px; }
.arrow-box svg.ico { width: 18px; height: 18px; }
.adm-burger svg.ico { width: 20px; height: 20px; }
.chip-copy svg.ico { margin-right: 0; opacity: .6; }

/* chips */
.chip { display: inline-flex; align-items: center; gap: .4rem; padding: 5px 10px; border-radius: 999px; font-size: .72rem; font-weight: 600; letter-spacing: .02em; background: rgb(var(--c-surface2)); color: rgb(var(--c-ink2)); border: 1px solid rgb(var(--edge) / .5); }
.chip-mono { font-family: var(--font-mono); letter-spacing: .06em; }
.chip-live { background: rgb(var(--c-ok) / .1); color: rgb(var(--c-ok)); border-color: transparent; }
.chip-live::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.8); } } /* opacity + transform only */ /* outwards only: the ring grows and fades, then starts again from the dot (owner, 2026-10-18) */
.chip-white { background: rgb(var(--paper) / .92); }

/* ---------- hero + character art ---------- */
/* the hero is a panel of its own: rounded like the panels below it (.band, .cinema) and kept apart from the next one by
   the same gap, so two panels never meet square edge to rounded corner */
.hero { border-radius: var(--r-sheet); position: relative; padding-block: clamp(48px, 7vw, 96px) clamp(36px, 5vw, 64px); min-height: clamp(420px, 42vw, 560px); display: flex; flex-direction: column; justify-content: center; overflow: hidden; }
.hero-copy { position: relative; z-index: 1; max-width: 36rem; }
.hero-copy > * + * { margin-top: 24px; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; }
/* hero backdrop (partials/hero-media.php): looping video on the right, fading out to the left into the headline */
.hero-media { position: absolute; inset: 0 0 0 42%; z-index: 0; pointer-events: none;
  /* fully transparent under the headline, solid from about the middle of the backdrop; an eased curve (smoothstep in
     stops), since a straight ramp with a corner showed as a hard vertical line where it started (2026-10-25) */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 8%, rgb(0 0 0 / .04) 16%, rgb(0 0 0 / .13) 24%, rgb(0 0 0 / .28) 32%, rgb(0 0 0 / .47) 40%, rgb(0 0 0 / .66) 48%, rgb(0 0 0 / .82) 56%, rgb(0 0 0 / .93) 64%, #000 72%); mask-image: linear-gradient(to right, transparent 0%, transparent 8%, rgb(0 0 0 / .04) 16%, rgb(0 0 0 / .13) 24%, rgb(0 0 0 / .28) 32%, rgb(0 0 0 / .47) 40%, rgb(0 0 0 / .66) 48%, rgb(0 0 0 / .82) 56%, rgb(0 0 0 / .93) 64%, #000 72%); }
.hero-media video, .hero-media img { width: 100%; height: 100%; object-fit: cover; object-position: center; }
/* cover variant (home): video fills the hero edge to edge behind centred copy; a flat ink scrim keeps the text readable, no fade */
.hero-media.is-cover { inset: 0; -webkit-mask-image: none; mask-image: none; background: rgb(var(--c-dark)); }
.hero-media.is-cover::after { content: ''; position: absolute; inset: 0; background: rgb(var(--c-dark) / .5); }
/* wide 16:9 frame; the box still grows if the copy needs more room */
.hero-cover { min-height: 480px; aspect-ratio: 16 / 9; align-items: center; text-align: center; }
.hero-cover .hero-copy { max-width: 46rem; margin-inline: auto; }
.hero-cover .hero-actions { justify-content: center; }
.hero-cover .max-sm { margin-inline: auto; }
.hero-cover .display, .hero-cover .lead { color: #fff; }
.hero-cover .accent { color: var(--accent-on-dark); } /* the plain accent is too dark to read over the video */
.hero-cover .eyebrow { color: rgb(255 255 255 / .72); }
.hero-cover .btn-solid { background: rgb(255 255 255 / .16); border: 1px solid rgb(255 255 255 / .35); color: #fff; }
.hero-cover .btn-solid:hover { opacity: 1; background: rgb(255 255 255 / .26); }
@media (max-width: 900px) {
  .hero { min-height: 0; }
  .hero-media { inset: 0; opacity: .55; -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgb(0 0 0 / .5) 45%, transparent 85%); mask-image: linear-gradient(to bottom, #000 0%, rgb(0 0 0 / .5) 45%, transparent 85%); }
  .hero-copy { padding-top: 26vw; }
  .hero-media.is-cover { opacity: 1; -webkit-mask-image: none; mask-image: none; }
  .hero-cover { min-height: 480px; aspect-ratio: auto; }
  .hero-cover .hero-copy { padding-top: 0; }
}

/* ---------- Social Media hero: the phone with a TikTok screen (partials/hero-phone.php) ----------
   A tilted phone on the right of the hero, its lower part cut off by the hero's edge (after the owner's mock-up). The
   frame is drawn in CSS: a brushed-silver band, a black bezel, the screen with the short playing, the side buttons.
   Everything on the screen is sized in cqw (a hundredth of the phone's width), so the UI scales with the phone. The
   platform's own red is used for the follow badge, as in TikTok. */
.hero.has-phone { min-height: clamp(540px, 50vw, 690px); } /* tall enough that only the phone's last few millimetres are cut off: the caption stays readable */
/* its copy starts at the top, not centred in the taller box, so the label and the headline stand at the same height as
   on Game Development and do not jump when the switch swaps the pages (owner, 2026-10-25) */
.hero.has-phone { justify-content: flex-start; }
/* the owner's mockup (an image with a see-through screen) over a flat screen plane: .phone-mask is the mockup's box,
   clipped to the screen's exact shape (phone-screen-mask.png); .phone-screen inside it is the 1000 x 2180 plane with
   the video and the TikTok overlay, warped onto the screen's corners by phoneScreens() in site.js */
/* the phone peeks out from under the dark video section below (owner, 2026-10-18): the hero lets it overflow, the next
   panel is stacked above it, so its foot disappears under that panel instead of being cut off with its shadow */
.hero.has-phone { overflow: visible; }
.hero.has-phone + .cinema, .hero.has-phone + section { position: relative; z-index: 2; }
.hero-phone { position: absolute; z-index: 1; top: clamp(30px, 3.4vw, 50px); right: clamp(12px, 3.6vw, 70px); width: clamp(330px, 36vw, 500px); /* about the last tenth (the curve and the port) under the dark section */ aspect-ratio: 1726 / 2507; pointer-events: none; }
.phone-frame { filter: drop-shadow(-16px 22px 26px rgb(0 0 0 / .26)); }
.hero.has-phone .hero-copy { z-index: 1; }
/* the reactions: each one pops out at the phone's right edge with a wiggle, floats up and fades, one after another */
.phone-fx { position: absolute; z-index: 3; left: 34%; top: 30%; width: 28%; height: 28%; } /* the middle of the screen's visible part: where a thumb taps to like */
/* the taps land on one fixed spot (b, the finger's ripple); each emoji (i in its span) pops up close to it, a little
   off each time, and all fly off along the same curve, so they read as one stream of likes (phoneFx() sets --x / --y /
   --dx on the span) */
.phone-fx > span { --x: 50%; --y: 50%; --dx: 105px; position: absolute; left: var(--x); top: var(--y); width: 0; height: 0; }
.phone-fx b { position: absolute; left: calc(50% - 30px); top: calc(50% - 30px); width: 60px; /* always the same spot: the middle of the tap area */ height: 60px; border-radius: 50%; background: rgb(255 255 255 / .22); box-shadow: 0 0 0 2px rgb(255 255 255 / .75), 0 0 18px rgb(255 255 255 / .35); opacity: 0;
  animation: phone-tap 1.75s var(--ease-out) calc(var(--k) * .25s) infinite; }
@keyframes phone-tap { 0% { opacity: .95; transform: scale(.25); } 8% { opacity: .85; } 28% { opacity: 0; transform: scale(1.6); } 100% { opacity: 0; transform: scale(1.6); } } /* about half a second, like a finger lifting off */
.phone-fx i { position: absolute; left: 0; top: 0; margin: -.5em 0 0 -.5em; font-style: normal; font-size: clamp(2.6rem, 4.2vw, 4rem); line-height: 1; opacity: 0; filter: drop-shadow(0 8px 14px rgb(0 0 0 / .35));
  animation: phone-react-x 1.75s cubic-bezier(.4, 0, .9, .6) calc(var(--k) * .25s) infinite, phone-react-y 1.75s cubic-bezier(.55, 0, 1, .35) calc(var(--k) * .25s) infinite, phone-react-pop 1.75s linear calc(var(--k) * .25s) infinite; } /* seven of them a quarter second apart: four a second */
/* the reaction (owner, 2026-10-18): one seamless move from the first frame. It pops in where the screen was tapped
   with a single wiggle while it already flies off: sideways at a gently rising speed (translate), upwards faster and
   faster (transform), growing as it comes towards the viewer (scale), along an exponential curve; it fades out high
   up. Three animations of separate properties run together; phoneFx() picks the spot and the side each round */
@keyframes phone-react-x { from { translate: 0 0; } to { translate: 105px 0; } } /* a fixed value: var() in keyframes would keep it on the main thread */
@keyframes phone-react-y { from { transform: translateY(0); } to { transform: translateY(-560%); } }
@keyframes phone-react-pop {
  0% { opacity: 0; scale: .2; rotate: -14deg; }
  7% { opacity: 1; scale: 1.15; rotate: 8deg; }
  15% { scale: 1; rotate: 0deg; }
  72% { opacity: 1; }
  100% { opacity: 0; scale: 1.55; rotate: 0deg; }
}
@media (prefers-reduced-motion: reduce) { .phone-fx { display: none; } }
.phone-frame { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; }
.phone-mask { position: absolute; inset: 0; z-index: 1; overflow: hidden; -webkit-mask: url('../img/phone-screen-mask.png') 0 0 / 100% 100% no-repeat; mask: url('../img/phone-screen-mask.png') 0 0 / 100% 100% no-repeat; }
.phone-screen { position: absolute; left: 0; top: 0; width: 1000px; height: 2180px; overflow: hidden; background: #000; color: #fff; font-family: var(--font-body); transform-origin: 0 0; container-type: inline-size; visibility: hidden; }
.phone-screen.is-placed { visibility: visible; } /* shown once phoneScreens() has put it on the screen */
.phone-screen video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.tt-shade { position: absolute; inset: 0; background: linear-gradient(180deg, rgb(0 0 0 / .45) 0%, transparent 22%, transparent 50%, rgb(0 0 0 / .62) 88%); }
.tt-status { position: absolute; inset: 3.4cqw 7cqw auto; display: flex; align-items: center; justify-content: space-between; font-size: 4.4cqw; font-weight: 600; letter-spacing: -.01em; }
.tt-sys { display: flex; align-items: center; gap: 1.6cqw; }
.tt-signal { display: flex; align-items: flex-end; gap: .6cqw; height: 3.2cqw; }
.tt-signal i { width: .9cqw; border-radius: .3cqw; background: #fff; }
.tt-signal i:nth-child(1) { height: 35%; } .tt-signal i:nth-child(2) { height: 55%; } .tt-signal i:nth-child(3) { height: 78%; } .tt-signal i:nth-child(4) { height: 100%; }
.tt-battery { position: relative; width: 6.4cqw; height: 3.2cqw; border-radius: 1cqw; border: .35cqw solid rgb(255 255 255 / .6); }
.tt-battery::before { content: ''; position: absolute; inset: .45cqw; width: 70%; border-radius: .4cqw; background: #fff; }
.tt-battery::after { content: ''; position: absolute; right: -1.2cqw; top: 50%; width: .7cqw; height: 1.4cqw; translate: 0 -50%; border-radius: 0 .4cqw .4cqw 0; background: rgb(255 255 255 / .6); }
.tt-tabs { position: absolute; inset: 14cqw 0 auto; display: flex; justify-content: center; gap: 4.4cqw; font-size: 4.3cqw; font-weight: 600; text-shadow: 0 1px 2px rgb(0 0 0 / .35); }
.tt-tabs span { color: rgb(255 255 255 / .62); }
.tt-tabs b { position: relative; font-weight: 700; }
.tt-tabs b::after { content: ''; position: absolute; left: 50%; bottom: -1.8cqw; width: 7cqw; height: .7cqw; translate: -50% 0; border-radius: 1cqw; background: #fff; }
.tt-views { position: absolute; left: 4cqw; top: 24cqw; display: inline-flex; align-items: center; gap: 1.4cqw; padding: 1.2cqw 2.6cqw; border-radius: 4cqw; background: rgb(0 0 0 / .38); font-size: 3.3cqw; font-weight: 600; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
.tt-views svg.ico { width: 2.8cqw; height: 2.8cqw; }
.tt-rail { position: absolute; right: 2.6cqw; bottom: 44cqw; display: flex; flex-direction: column; align-items: center; gap: 4.2cqw; }
.tt-avatar { position: relative; width: 11cqw; height: 11cqw; margin-bottom: 1.6cqw; border-radius: 50%; border: .5cqw solid #fff; background: #000; }
.tt-avatar img, .tt-disc img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.tt-avatar i { position: absolute; left: 50%; bottom: -2.2cqw; width: 4.6cqw; height: 4.6cqw; translate: -50% 0; border-radius: 50%; display: grid; place-items: center; background: #fe2c55; font-style: normal; font-size: 3.8cqw; font-weight: 700; line-height: 1; }
.tt-act { display: grid; justify-items: center; gap: .6cqw; }
.tt-act svg.ico { width: 8.6cqw; height: 8.6cqw; filter: drop-shadow(0 1px 2px rgb(0 0 0 / .35)); }
.tt-act small { font-size: 3cqw; font-weight: 600; text-shadow: 0 1px 2px rgb(0 0 0 / .4); }
/* the column goes wild (owner, 2026-10-18): the heart is liked (TikTok red) and beats with every tap of the stream,
   four times a second; comment, save and share each pulse (save also flashes yellow) on their own beat; the counts count up to the
   real figure and bump on every change (phoneFx() in site.js). Still under reduced motion. */
.tt-act svg.ico { transform-origin: 50% 55%; }
.tt-act.is-like svg.ico { color: #fe2c55; animation: tt-beat .25s cubic-bezier(.2, .9, .3, 1.3) infinite; }
.tt-act.is-comment svg.ico { animation: tt-pulse .9s var(--ease-in-out) infinite; }
.tt-act.is-save svg.ico { animation: tt-save 1.3s var(--ease-in-out) .3s infinite; }
.tt-act.is-share svg.ico { animation: tt-pulse 1.7s var(--ease-in-out) .6s infinite; } /* a pulse like the others, no turn */
@keyframes tt-beat { 0% { scale: 1; } 35% { scale: 1.3; } 100% { scale: 1; } }
@keyframes tt-pulse { 0%, 100% { scale: 1; } 30% { scale: 1.22; } }
@keyframes tt-save { 0%, 100% { scale: 1; } 25% { scale: 1.25; } }
/* the glows and the yellow save flash: a soft disc behind each icon that only fades and grows (compositor-friendly) */
.tt-act::before { content: ''; position: absolute; left: 50%; top: 0; width: 12cqw; height: 12cqw; margin-left: -6cqw; margin-top: -1.7cqw; border-radius: 50%; pointer-events: none; opacity: 0; background: radial-gradient(circle, rgb(var(--glow, 255 255 255) / .55), transparent 68%); }
.tt-act.is-like { --glow: 254 44 85; } .tt-act.is-save { --glow: 255 212 0; }
.tt-act.is-like::before { animation: tt-glow .25s cubic-bezier(.2, .9, .3, 1.3) infinite; }
.tt-act.is-comment::before { animation: tt-glow .9s var(--ease-in-out) infinite; }
.tt-act.is-save::before { background: radial-gradient(circle, rgb(255 212 0 / .9), rgb(255 212 0 / .35) 45%, transparent 70%); animation: tt-glow 1.3s var(--ease-in-out) .3s infinite; }
.tt-act.is-share::before { animation: tt-glow 1.7s var(--ease-in-out) .6s infinite; }
@keyframes tt-glow { 0%, 100% { opacity: 0; scale: .7; } 30% { opacity: 1; scale: 1.1; } }
.tt-act small.is-bump { animation: tt-bump .22s var(--ease-out); }
/* numbers going up (owner, 2026-10-18): "+1"s float off the heart with every tap (four a second, one per i, a quarter
   second apart) and now and then off the comment icon; the totals themselves stop at the video's real figures */
.tt-act { position: relative; }
.tt-plus { position: absolute; left: 50%; top: 0; width: 0; height: 0; pointer-events: none; }
.tt-plus i { position: absolute; left: 0; top: 0; translate: -50% 0; font-style: normal; font-size: 3.4cqw; font-weight: 800; color: #fff; text-shadow: 0 1px 3px rgb(0 0 0 / .6); white-space: nowrap; opacity: 0; animation: tt-plus 1s var(--ease-out) infinite; }
.tt-plus i:nth-child(2) { animation-delay: .25s; } .tt-plus i:nth-child(3) { animation-delay: .5s; } .tt-plus i:nth-child(4) { animation-delay: .75s; }
.tt-plus.is-slow i { animation-duration: 2.2s; animation-delay: .4s; }
.tt-act.is-like .tt-plus i { color: #ffd1dc; }
@keyframes tt-plus { 0% { opacity: 0; transform: translate(0, 0) scale(.6); } 12% { opacity: 1; transform: translate(-1cqw, -3cqw) scale(1.1); } 100% { opacity: 0; transform: translate(-5cqw, -14cqw) scale(.9); } }
@media (prefers-reduced-motion: reduce) { .tt-plus { display: none; } }
@keyframes tt-bump { 40% { scale: 1.25; color: #fff; } }
@media (prefers-reduced-motion: reduce) { .tt-act svg.ico, .tt-act small { animation: none !important; } }
.tt-disc { width: 10cqw; height: 10cqw; padding: 2cqw; border-radius: 50%; background: radial-gradient(circle, #333 0 38%, #111 40% 100%); animation: tt-spin 5s linear infinite; }
@keyframes tt-spin { to { rotate: 360deg; } }
.tt-caption { position: absolute; left: 4cqw; right: 20cqw; bottom: 26cqw; display: grid; gap: 1.6cqw; font-size: 3.7cqw; line-height: 1.3; text-shadow: 0 1px 2px rgb(0 0 0 / .4); }
.tt-caption b { font-size: 4.2cqw; font-weight: 700; }
.tt-caption p span { font-weight: 700; }
/* the title and the hashtags (Settings → Social Media phone, up to 6) stop at three lines, so they never climb over the video */
.tt-caption p:not(.tt-sound) { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.tt-sound { display: flex; align-items: center; gap: 1.6cqw; overflow: hidden; }
.tt-sound > span:first-child { flex: none; }
.tt-sound-run { overflow: hidden; white-space: nowrap; -webkit-mask-image: linear-gradient(90deg, #000 75%, transparent); mask-image: linear-gradient(90deg, #000 75%, transparent); }
.tt-sound-run span { display: inline-block; padding-right: 8cqw; font-weight: 400 !important; animation: tt-run 9s linear infinite; }
@keyframes tt-run { from { translate: 30% 0; } to { translate: -100% 0; } }
@media (max-width: 1000px) { .hero.has-phone .hero-copy { max-width: calc(100% - clamp(330px, 36vw, 500px) + 20px); } }
/* tablets and phones: the phone comes after the copy, centred, its lower part faded out where the hero ends */
@media (max-width: 760px) {
  .hero.has-phone { min-height: 0; }
  .hero.has-phone .hero-copy { max-width: none; padding-top: 0 !important; }
  .hero-phone { position: relative; order: 2; top: auto; right: auto; width: min(360px, 84vw); margin: 28px auto calc(-1 * (clamp(24px, 4vw, 48px) + 12vw)); } /* the foot with the curve goes under the dark section */
}

/* ---------- stats ---------- */
.stat { padding: 16px 18px; display: flex; align-items: center; gap: 14px; }
/* Icon tile (helpers.php icon_badge()). The tile itself stays flat and neutral grey. The icon on it is
   coloured by what it stands for (.ico-tone-* from icon_tone(): heart red, bolt amber, map green, ...), shaded from a
   light top to a dark bottom (the #ico-<tone> gradients in the layout) and lifted off the tile by a shadow in its own
   colour plus a thin light edge. .ico-glint is a white copy of the same icon shown only through a narrow slanted band
   (a mask); sliding that band across makes a glint that stays inside the icon's shape. It runs once when the tile
   appears and again on hover. Icons without a tone keep the page colour. */
.stat-ico { --ico: var(--c-accent); position: relative; flex: none; width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; background: rgb(var(--c-ink2) / .07); /* neutral grey on every page: the colour belongs to the icon */ color: rgb(var(--ico)); }
.stat-ico svg { grid-area: 1 / 1; width: 22px; height: 22px; }
.stat-ico svg:not(.ico-glint) { filter: drop-shadow(0 -.6px 0 rgb(var(--edge) / .75)) drop-shadow(0 2px 2px rgb(var(--ico) / .45)); }
.ico-tone-red { --ico: 225 29 72; } .ico-tone-amber { --ico: 245 158 11; } .ico-tone-gold { --ico: 202 138 4; } .ico-tone-orange { --ico: 234 88 12; }
.ico-tone-blue { --ico: 37 99 235; } .ico-tone-violet { --ico: 124 58 237; } .ico-tone-pink { --ico: 219 39 119; } .ico-tone-green { --ico: 22 163 74; } .ico-tone-teal { --ico: 13 148 136; }
.ico-tone-red svg:not(.ico-glint) { fill: url(#ico-red) currentColor; } .ico-tone-amber svg:not(.ico-glint) { fill: url(#ico-amber) currentColor; } .ico-tone-gold svg:not(.ico-glint) { fill: url(#ico-gold) currentColor; }
.ico-tone-orange svg:not(.ico-glint) { fill: url(#ico-orange) currentColor; } .ico-tone-blue svg:not(.ico-glint) { fill: url(#ico-blue) currentColor; } .ico-tone-violet svg:not(.ico-glint) { fill: url(#ico-violet) currentColor; }
.ico-tone-pink svg:not(.ico-glint) { fill: url(#ico-pink) currentColor; } .ico-tone-green svg:not(.ico-glint) { fill: url(#ico-green) currentColor; } .ico-tone-teal svg:not(.ico-glint) { fill: url(#ico-teal) currentColor; }
.stat-ico .ico-glint { fill: #fff; pointer-events: none; -webkit-mask: linear-gradient(115deg, transparent 42%, #000 50%, transparent 58%) 130% 0 / 320% 100% no-repeat; mask: linear-gradient(115deg, transparent 42%, #000 50%, transparent 58%) 130% 0 / 320% 100% no-repeat; }
.reveal.in .stat-ico .ico-glint, .h-ico .stat-ico .ico-glint { animation: ico-glint 1.2s var(--ease-in-out) .5s both; }
.stat:hover .stat-ico .ico-glint, .h-ico:hover .stat-ico .ico-glint { animation: ico-glint-again 1s var(--ease-in-out) both; } /* a second name, so hovering restarts it */
@keyframes ico-glint { from { -webkit-mask-position: 130% 0; mask-position: 130% 0; } to { -webkit-mask-position: -30% 0; mask-position: -30% 0; } }
@keyframes ico-glint-again { from { -webkit-mask-position: 130% 0; mask-position: 130% 0; } to { -webkit-mask-position: -30% 0; mask-position: -30% 0; } }
.stat-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.stat.center { flex-direction: column; justify-content: center; gap: 4px; } /* case-study metrics: no icon, stacked */
.stat-val { font-family: var(--font-display); font-weight: 800; font-size: clamp(1.7rem, 3vw, 2.5rem); letter-spacing: -.02em; line-height: 1; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
.stat-label { font-size: .82rem; color: rgb(var(--c-muted)); }

/* ---------- division cards ---------- */
/* vertical layout: image across the top fading down into the copy, which sits at the bottom of the card */
.div-card { padding: clamp(24px, 3vw, 36px); overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end; min-height: 430px; }
.div-card .h2 { margin-top: 6px; }
.div-card > * { position: relative; z-index: 1; }
.div-card .bg { position: absolute; inset: 0 0 auto; height: 62%; z-index: 0; border-radius: inherit; overflow: hidden; }
.div-card .bg img { width: 100%; height: 100%; object-fit: cover; object-position: center top; opacity: .95; transform: scale(1.02); transition: transform .6s var(--ease-in-out);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgb(0 0 0 / .8) 38%, rgb(0 0 0 / .18) 72%, transparent 92%); mask-image: linear-gradient(to bottom, #000 0%, rgb(0 0 0 / .8) 38%, rgb(0 0 0 / .18) 72%, transparent 92%); }
.div-card:hover .bg img { transform: scale(1.06); }
.div-card .secondary { max-width: 24rem; padding-right: 56px; }
.div-card .arrow-box { position: absolute; right: 22px; bottom: 22px; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-surface2)); color: rgb(var(--c-ink2)); transition: background var(--dur-base) var(--ease-in-out), color var(--dur-base), transform var(--dur-base) var(--ease-in-out); }
.div-card:hover .arrow-box { background: rgb(var(--c-dark2)); color: #fff; transform: translateX(3px); }

/* ---------- media cards ---------- */
.media { position: relative; overflow: hidden; background-color: rgb(var(--c-surface2)); background-image: radial-gradient(rgb(var(--c-ink2) / .14) 1px, transparent 1.5px); background-size: 14px 14px; }
.media img, .media video, .media iframe { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease-in-out), filter .3s var(--ease-in-out); }
.card-hover:hover .media img { transform: scale(1.05); }
/* game-card trailer (games.video_url: a file or Vimeo's player): site.js plays it while the card is hovered, and it fades in
   over the cover once it really plays (.is-playing on its box), so an empty frame or a loading screen never shows */
.media .media-trailer { position: absolute; inset: 0; opacity: 0; transition: opacity var(--dur-base) var(--ease-in-out); }
.media.is-playing > .media-trailer, .media-trailer.is-only { opacity: 1; }
.ar-16-9 { aspect-ratio: 16 / 9; } .ar-16-10 { aspect-ratio: 16 / 10; } .ar-9-16 { aspect-ratio: 9 / 16; }
.card > .media:first-child { border-radius: var(--r-card) var(--r-card) 0 0; }
.badge { position: absolute; z-index: 2; }
.badge-tl { left: 12px; top: 12px; } .badge-tr { right: 12px; top: 12px; } .badge-bl { left: 12px; bottom: 12px; }
.kv { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin-top: 14px; padding-top: 14px; border-top: 1px solid rgb(var(--c-line)); }
.kv dt { display: flex; align-items: center; font-size: .74rem; color: rgb(var(--c-muted)); } /* icon and label on one line */
.kv dd { margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.2rem; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
/* island cards (game-card.php .game-figs): plays, all-time peak, minutes played. Each figure is a stack (owner,
   2026-10-25): its icon on a small round chip, the number, the label under it, so all three numbers share one line and a
   label never wraps above its number. The icons have a colour each from the theme's tones (plays the accent, the peak
   the fire tone, minutes the green), on a neutral chip; the numbers stay in the text colour. */
.kv-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 12px; }
.game-figs > div { display: grid; grid-template-areas: "ico" "val" "lab"; justify-items: start; align-content: start; gap: 0; min-width: 0; }
.game-figs dt { display: contents; }
.game-figs .kv-ico { grid-area: ico; display: grid; place-items: center; width: 26px; height: 26px; margin-bottom: 9px; border-radius: 8px; background: rgb(var(--c-ink) / .06); color: rgb(var(--fig, var(--accent-text))); } /* a neutral grey chip (owner, 2026-10-26: a tinted one, like the headline numbers', was worse here) */
.game-figs .kv-ico svg.ico { width: 13px; height: 13px; margin: 0; opacity: 1; }
.game-figs dd { grid-area: val; font-size: 1.25rem; line-height: 1.05; white-space: nowrap; }
.game-figs .kv-lab { grid-area: lab; margin-top: 4px; font-size: .72rem; line-height: 1.3; color: rgb(var(--c-muted)); }
/* since 2026-10-26 (owner): active players green (the headline's Live players: same controller, same green), minutes blue, all-time peak orange with a rising arrow */
.game-figs .is-live { --fig: 74 222 128; }
.game-figs .is-minutes { --fig: var(--accent-text); }
.game-figs .is-peak { --fig: var(--c-fire); }
html[data-theme="light"] .game-figs .is-live { --fig: 22 163 74; } /* a brighter green on the light page (#16A34A; the success green #237F00 read too dark there, owner 2026-10-25) */
.live-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 14px; }
.live-row canvas { flex: 1; height: 34px; min-width: 0; }
.live-row .live-n { display: inline-flex; align-items: center; gap: .45em; font-family: var(--font-mono); font-size: .78rem; color: rgb(var(--c-ink)); white-space: nowrap; }
/* live marker: the pulsing dot of .chip-live on its own, after the number */
.live-dot { flex: none; width: 7px; height: 7px; margin-left: 2px; border-radius: 50%; background: rgb(var(--c-ok)); animation: pulse 1.6s ease-in-out infinite; }
/* brighter on every page (owner, 2026-10-18): a vivid green with a light core and a wider, livelier ring, so the
   "live" dots read at a glance on dark panels and buttons; on the light page a thin darker edge keeps it crisp */
.live-dot { --live: 46 230 110; position: relative; display: inline-block; width: 8px; height: 8px; background: none; animation: none; }
/* the dot and its ring are two pseudo-elements on the same 8px box, both on their own layer (translateZ), so they are
   rasterised the same way and the ring grows exactly from the dot's centre (owner, 2026-10-25: the pulse sat off
   centre: a 7px dot on fractional pixels beside a ring on its own layer, and a light core drawn off centre) */
.live-dot::before, .live-dot::after { content: ''; position: absolute; inset: 0; border-radius: 50%; transform: translateZ(0); }
.live-dot::before { z-index: 1; background: radial-gradient(circle at 50% 50%, #b9ffd0 0 20%, rgb(var(--live)) 52%); }
.live-dot::after { background: rgb(var(--live) / .6); animation: live-ring 1.8s cubic-bezier(0, 0, .2, 1) infinite; } /* the ring grows out and fades: transform + opacity only */
@keyframes live-ring { 0% { transform: translateZ(0) scale(1); opacity: .9; } 75%, 100% { transform: translateZ(0) scale(3.2); opacity: 0; } }
@keyframes live-pulse { 0% { box-shadow: 0 0 0 0 rgb(var(--live) / .65); } 75%, 100% { box-shadow: 0 0 0 8px rgb(var(--live) / 0); } } /* outwards only, never back in */
html[data-theme="light"] .live-dot { --live: 22 200 90; }
html[data-theme="light"] .live-dot::before { box-shadow: inset 0 0 0 1px rgb(10 120 50 / .35); } /* the thin darker edge, on the dot itself so it scales with it */
.photo { background: rgb(var(--paper)); padding: 8px; border-radius: 11px; box-shadow: 0 3px 4px rgb(0 0 0 / .25); }
.photo img { border-radius: 6px; width: 100%; }

/* ---------- aligned cards (.card-rows): the same part of every card in a row starts at the same height ----------
   Put .card-rows on the .grid (or .carousel-track) that holds the cards. Each card then spans --card-rows row tracks
   of that grid and passes them down (subgrid) to its .card-pad, so track 1 is the media and every direct child of
   .card-pad gets a track of its own, as tall as the tallest one in the row: titles line up, texts line up, and
   whatever follows (a divider, the footer) starts on one line however long the copy above it is.
   Rules for a card: render the same parts in the same order in every card (an optional part stays in as an empty
   element), and wrap everything from the divider down in one last child, .card-foot, whose .card-foot-end is
   pinned to the bottom. --card-rows = media + number of .card-pad children: 4 by default, .card-rows-5 for five. */
.card-rows { --card-rows: 4; row-gap: 0; margin-bottom: -14px; } /* row spacing moves onto the cards: a gap would also open between the parts */
.card-rows > * { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: subgrid; grid-row: span var(--card-rows); margin-bottom: 14px; }
.card-rows-5 { --card-rows: 5; }
.card-rows-3 { --card-rows: 3; } /* case study cards since 2026-10-25: cover, title, summary */
.card-rows > * > .card-pad { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: subgrid; grid-row: 2 / -1; }
.card-rows .card-pad > .link { justify-self: start; align-self: end; }
.card-foot { display: flex; flex-direction: column; gap: 14px; }
.card-foot > * + * { margin-top: 0; } /* spacing inside the footer comes from the gap */
.card-foot-end { margin-top: auto; align-self: flex-start; }
/* one-row variant for the carousel: the tracks are declared, every card spans all of them */
.carousel-track.card-rows { grid-template-rows: repeat(var(--card-rows), auto); row-gap: 0; margin-bottom: -30px; }
.carousel-track.card-rows > * { grid-row: 1 / -1; margin-bottom: 0; }

/* ---------- carousel (scroll-snap track + arrow buttons; kx.carousel) ---------- */
.carousel-track {
  display: grid; grid-auto-flow: column; grid-auto-columns: calc((100% - 2 * 14px) / 3); gap: 14px;
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none;
  padding: 6px 6px 30px; margin: -6px -6px -30px; /* room for the hover lift + shadow inside the scroll box */
  scroll-padding-inline: 6px; /* snap the cards to the content edge, not to the padded box: without it they sit 6px left of everything above */
}
.carousel-body.mt-8 { margin-top: 32px; }
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track > * { scroll-snap-align: start; }
.carousel-track:focus-visible { outline-offset: 6px; }
.carousel[data-cols="4"] .carousel-track { grid-auto-columns: calc((100% - 3 * 14px) / 4); }
.carousel-body { position: relative; }
/* arrows flank the track, vertically centred on it (they sit in the section's side padding on desktop) */
.carousel-btn { position: absolute; top: 50%; z-index: 3; width: 44px; height: 44px; margin-top: -34px; border-radius: 50%; display: inline-grid; place-items: center; background: rgb(var(--paper)); color: rgb(var(--c-ink2)); border: 1px solid rgb(var(--edge) / .4); box-shadow: var(--sh-pill); transition: transform var(--dur-quick) var(--ease-knob), opacity var(--dur-base), box-shadow var(--dur-base) var(--ease-in-out); }
.carousel-btn-prev { left: -22px; } .carousel-btn-next { right: -22px; }
.carousel-btn svg { width: 18px; height: 18px; }
.carousel-btn:hover { box-shadow: var(--sh-card-hover); transform: scale(1.05); }
.carousel-btn:active { transform: scale(.95); }
.carousel-btn:disabled { opacity: 0; pointer-events: none; }
.carousel:not(.is-scrollable) .carousel-btn { display: none; }
@media (max-width: 1023px) { .carousel[data-cols="4"] .carousel-track { grid-auto-columns: calc((100% - 14px) / 2); } }
@media (max-width: 767px)  { .carousel[data-cols="3"] .carousel-track { grid-auto-columns: 84%; } .carousel-btn-prev { left: 4px; } .carousel-btn-next { right: 4px; } }
@media (max-width: 479px)  { .carousel[data-cols="4"] .carousel-track { grid-auto-columns: 68%; } }

/* ---------- video stage (home: centred video with stacked neighbours, autoplay progression; kx.videoStage) ---------- */
.vstage-wrap { position: relative; --vs-w: min(280px, 58vw); --vs-step: calc(var(--vs-w) * .82); }
.vstage { position: relative; height: calc(var(--vs-w) * 16 / 9); perspective: 1400px; outline-offset: 8px; touch-action: pan-y; user-select: none; }
.vslide {
  position: absolute; top: 0; left: 50%; width: var(--vs-w); aspect-ratio: 9 / 16; margin-left: calc(var(--vs-w) / -2); cursor: pointer;
  --o: 0; --a: 0;
  transform: translateX(calc(var(--o) * var(--vs-step))) translateZ(calc(var(--a) * -180px)) rotateY(calc(var(--o) * -9deg)) scale(calc(1 - var(--a) * .06));
  opacity: calc(1 - var(--a) * .3);
  transition: transform .7s var(--ease-expo), opacity .5s var(--ease-in-out), filter .5s var(--ease-in-out);
  filter: saturate(calc(1 - var(--a) * .35));
}
.vslide.is-active { cursor: default; }
.vslide.is-far { opacity: 0; pointer-events: none; }
.vslide .media { height: 100%; border-radius: var(--r-card); box-shadow: var(--sh-card-hover); }
.vslide .media iframe { pointer-events: none; }
/* YouTube / TikTok players without their chrome (video_embed()'s 'auto' player, FramePlayer in kx.js): the page plays
   and stops them, so they take no clicks (a press goes to the slide: sound on / off), and the poster (or a dark panel)
   covers the platform's own loading screen until the video plays. YouTube still draws its title and logo along the
   edges for a moment, so it is shown a little larger and those edges fall outside the card. */
.media .vembed { position: absolute; inset: 0; border: 0; pointer-events: none; }
.media .vembed[data-embed="youtube"] { transform: scale(1.18); }
.vcard .media .vembed { inset: auto 0; top: 50%; height: auto; aspect-ratio: 9 / 16; translate: 0 -50%; } /* 9:16 in the card's band, cropped like the files */
/* Vimeo: the frame takes the video's own shape (--ar, asked from the player by FramePlayer; 9:16 until it answers) and
   is scaled so the picture inside it (--car, from its saved still with baked-in black bars trimmed: vimeo_fit_attr())
   covers the box: the player never shows black bars and the video is cropped like a <video> file */
:is(.media, .hero-media, .phone-screen, .season-art-box):has(> .vembed[data-embed="vimeo"]) { container-type: size; }
.hero-media .vembed, .phone-screen .vembed, .season-art-box .vembed { position: absolute; border: 0; pointer-events: none; }
.season-art-box { overflow: hidden; } /* the season banner's Vimeo video (waiting.php): placed like the picture, .season-art */
.is-playing > .vembed-cover { opacity: 0; } /* the still over a backdrop or the phone fades once the player runs */
:is(.media, .hero-media, .phone-screen, .season-art-box) .vembed[data-embed="vimeo"], .vcard .media .vembed[data-embed="vimeo"] { inset: auto; left: 50%; top: 50%; translate: -50% -50%; aspect-ratio: auto; max-width: none; width: max(100cqw * var(--ar, .5625) / var(--car, var(--ar, .5625)), 100cqh * var(--ar, .5625)); height: max(100cqh, 100cqw / var(--car, var(--ar, .5625))); }
.vembed-cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: rgb(var(--c-dark)); pointer-events: none; transition: opacity .45s var(--ease-in-out); }
.media.is-playing .vembed-cover { opacity: 0; }
/* paused by a press while it is the focused video (owner, 2026-10-26): the paused frame stays, also while the second
   press starts it again (the still flashed in that moment), and the still only comes back once it is no longer the
   focused one (kx.videoStage clears .keep-frame when the stage moves on) */
.keep-frame.has-played > .vembed-cover { opacity: 0; transition: none; }
.chip-plat { padding-inline: 7px; }
.chip-plat svg { width: 14px; height: 14px; }
.vslide-cap { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 2; display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border-radius: 12px; background: rgb(var(--paper) / .92); color: rgb(var(--c-ink)); font-size: .82rem; line-height: 1.3; opacity: 0; translate: 0 6px; transition: opacity var(--dur-base) var(--ease-in-out), translate var(--dur-base) var(--ease-in-out); }
.vslide-cap span { font-size: .74rem; color: rgb(var(--c-muted)); }
.vslide.is-active .vslide-cap { opacity: 1; translate: 0 0; }
/* the TikTok-style overlay on a slide (video-stage.php, the client's mock-up): a shade at the top and the foot so white
   reads on any picture, the platform icons top left, the numbers stacked on the right, account and title at the foot.
   Shown on the centre slide only; the sound control keeps the top right corner. */
/* a video paused by a press (owner, 2026-10-26; kx.videoStage hold(), vcards() in site.js): a round play icon in its middle */
.v-paused { position: absolute; z-index: 4; left: 50%; top: 50%; width: 64px; height: 64px; translate: -50% -50%; display: grid; place-items: center; border-radius: 50%; background: rgb(0 0 0 / .55); color: #fff; opacity: 0; scale: .8; pointer-events: none; transition: opacity .2s var(--ease-in-out), scale .25s var(--ease-overshoot); }
.v-paused svg.ico { width: 26px; height: 26px; margin-left: 3px; }
.is-paused .v-paused { opacity: 1; scale: 1; }
.media:has(> .v-paused) { cursor: pointer; }
.vs-shade { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgb(0 0 0 / .42) 0%, transparent 18%, transparent 52%, rgb(0 0 0 / .72) 100%); opacity: 0; transition: opacity var(--dur-base) var(--ease-in-out); }
.vs-top { position: absolute; top: 12px; left: 12px; z-index: 2; display: flex; align-items: center; gap: 6px; }
.vs-plat { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; color: #fff; background: rgb(0 0 0 / .38); box-shadow: inset 0 1px 0 rgb(255 255 255 / .14); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); transition: background-color var(--dur-base), transform var(--dur-quick) var(--ease-knob); }
.vs-plat svg { width: 15px; height: 15px; }
a.vs-plat:hover { background: rgb(0 0 0 / .6); transform: translateY(-1px); }
.vs-lang { padding: 6px 10px; border-radius: 9px; background: rgb(0 0 0 / .38); color: #fff; font-size: .72rem; font-weight: 600; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.vs-who { position: relative; display: flex; align-items: flex-end; min-height: 26px; }
.vs-rail { position: absolute; right: -2px; bottom: calc(100% + 12px); z-index: 2; /* above the account line, clear of the title (owner, 2026-10-20) */ display: flex; flex-direction: column; align-items: center; gap: 14px; color: #fff; pointer-events: none; }
.vs-act { display: grid; justify-items: center; gap: 3px; }
.vs-act svg.ico { width: 26px; height: 26px; filter: drop-shadow(0 1px 2px rgb(0 0 0 / .45)); }
.vs-act small { font-size: .72rem; font-weight: 700; text-shadow: 0 1px 2px rgb(0 0 0 / .55); }
.vslide-cap.vs-cap { left: 12px; right: 12px; bottom: 12px; gap: 8px; padding: 0; border-radius: 0; background: none; color: #fff; }
.vslide-cap.vs-cap span { font-size: inherit; color: inherit; } /* the old caption's grey small spans do not apply here */
.vs-account { display: inline-flex; align-items: center; gap: 8px; font-size: .9rem !important; color: #fff !important; text-shadow: 0 1px 2px rgb(0 0 0 / .5); }
.vs-account b { font-weight: 700; }
a.vs-account { align-self: flex-start; border-radius: 999px; transition: opacity var(--dur-quick) var(--ease-in-out); }
a.vs-account:hover b { text-decoration: underline; text-underline-offset: 3px; }
/* the account picture: a thin dark edge only (the red LIVE ring that pulsed around it since 2026-10-18 was removed on
   2026-10-26, owner) */
.vs-account .avatar { box-shadow: 0 0 0 2px rgb(0 0 0 / .55); }
.vs-title { margin: 0; display: grid; gap: 3px; padding: 9px 12px; border-radius: 10px; background: rgb(0 0 0 / .55); font-size: .86rem; line-height: 1.35; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.vs-title b { font-weight: 600; }
.vslide-cap.vs-cap .vs-desc { font-size: .76rem; color: rgb(255 255 255 / .8); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } /* the caption, two lines at most */
.vs-top, .vs-rail { opacity: 0; transition: opacity var(--dur-base) var(--ease-in-out); }
.vslide.is-active :is(.vs-shade, .vs-top, .vs-rail) { opacity: 1; }
.vslide:not(.is-active) .vs-top { pointer-events: none; }
.vstage-bar { width: calc(var(--vs-w) * .5); height: 5px; margin: 16px auto 0; border-radius: 3px; background: rgb(var(--c-line)); overflow: hidden; }
.vstage-bar i { display: block; height: 100%; background: rgb(var(--c-accent)); transform-origin: left; transform: scaleX(0); }
.vstage-wrap .carousel-btn { margin-top: -32px; display: inline-grid; }
/* sound controls: a dark pill over the top right corner of the centre video (speaker button, volume slider while unmuted).
   Only shown while the pointer is over the centre video or the pill itself, while it has keyboard focus, and for a
   moment after a press (.is-peek, set by kx.videoStage: touch screens have no hover). */
.vstage-sound { position: absolute; z-index: 30; top: 12px; left: calc(50% + var(--vs-w) / 2 - 12px); translate: -100% 0; display: flex; align-items: center; gap: 2px; padding: 4px; border-radius: 999px; background: rgb(var(--c-graphite) / .78); color: #fff; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: 0 6px 14px -8px rgb(0 0 0 / .6);
  opacity: 0; scale: .92; pointer-events: none; transition: opacity var(--dur-base) var(--ease-in-out), scale var(--dur-base) var(--ease-in-out); }
.vstage-wrap:has(.vslide.is-active:hover) .vstage-sound, .vstage-sound:hover, .vstage-sound:focus-within, .vstage-sound.is-peek { opacity: 1; scale: 1; pointer-events: auto; }
.vstage-sound-btn { flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; color: inherit; transition: background-color var(--dur-quick) var(--ease-in-out), transform var(--dur-quick) var(--ease-knob); }
.vstage-sound-btn:hover { background: rgb(255 255 255 / .16); }
.vstage-sound-btn:active { transform: scale(.92); }
.vstage-sound-btn svg.ico { width: 16px; height: 16px; }
.vstage-sound.is-muted .is-on, .vstage-sound:not(.is-muted) .is-off, .vstage-sound.is-muted .vstage-sound-range { display: none; }
.vstage-sound-range { width: 76px; height: 4px; margin: 0 4px 0 10px; accent-color: #fff; cursor: pointer; }
/* the slider folds away to just the speaker once the control is left (owner, 2026-10-25: "once you unfocus the sound
   control, minimize the slider back to just the icon"): it opens while the pill is hovered or holds the focus (a tap on
   the speaker focuses it, site.js), a one-off ease, not a running animation */
.vstage-sound:not(.is-muted) .vstage-sound-range { transition: width .28s var(--ease-in-out), margin .28s var(--ease-in-out), opacity .2s ease; }
.vstage-sound:not(.is-muted):not(:hover):not(:focus-within) .vstage-sound-range { width: 0 !important; margin-inline: 0; opacity: 0; pointer-events: none; }
@media (max-width: 767px) { .vstage-wrap { --vs-step: calc(var(--vs-w) * .7); } }
/* reveal: kx.videoStage arms the stage, then fans the neighbours out from behind the centre video when it scrolls into view */
.vstage-wrap.is-armed:not(.is-shown) .vslide:not(.is-active) { transform: translateZ(-320px) scale(.82); opacity: 0; }
.vstage-wrap.is-armed:not(.is-shown) .vslide.is-active { transform: scale(.94); opacity: 0; }
.vstage-wrap.is-shown:not(.is-settled) .vslide { transition-delay: calc(var(--a) * 70ms); }
.vstage-ambient { display: none; }

/* ---------- cinema panel (home short-form): dark screening room, ambient glow from the playing video, deeper 3D ---------- */
.cinema { position: relative; isolation: isolate; overflow: hidden; border-radius: var(--r-sheet); background: rgb(var(--c-dark)); color: rgb(255 255 255 / .8); }
.cinema::before { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; box-shadow: inset 0 0 180px 40px rgb(0 0 0 / .55); } /* vignette */
.cinema .display { color: #fff; }
.cinema .eyebrow { color: rgb(255 255 255 / .55); }
.cinema .link { color: #fff; }
.cinema .vstage-wrap { --vs-w: min(330px, 62vw); --vs-step: calc(var(--vs-w) * .8); }
.cinema .vstage { perspective: 1100px; }
/* Neighbours stay opaque and are dimmed by a scrim (::after) instead of an animated filter: a filter repaints the
   video on every frame of the slide change, an opacity change is done by the compositor. */
.cinema .vslide {
  transform: translateX(calc(var(--o) * var(--vs-step))) translateZ(calc(var(--a) * -260px)) rotateY(calc(var(--o) * -16deg)) scale(calc(1 - var(--a) * .05));
  opacity: 1; filter: none; will-change: transform, opacity;
}
.cinema .vslide.is-far { opacity: 0; } /* restated: the rule above would otherwise show the slides that wrap around the ring */
.cinema .vslide::after { content: ''; position: absolute; inset: 0; z-index: 3; border-radius: var(--r-card); background: rgb(var(--c-dark)); opacity: calc(var(--a) * .36); pointer-events: none; transition: opacity .6s var(--ease-in-out); }
.cinema .vslide .media { box-shadow: 0 30px 60px -24px rgb(0 0 0 / .8); }
.cinema .vslide.is-active .media { box-shadow: 0 50px 90px -30px rgb(0 0 0 / .9), 0 0 0 1px rgb(255 255 255 / .14), 0 0 70px -10px rgb(var(--c-accent) / .55); }
/* soft floor shadow under the centre video */
.cinema .vstage::after { content: ''; position: absolute; left: 50%; bottom: -18px; width: calc(var(--vs-w) * .9); height: 36px; translate: -50% 0; border-radius: 50%; background: rgb(0 0 0 / .7); filter: blur(22px); z-index: -1; pointer-events: none; }
.cinema .vstage-bar { background: rgb(255 255 255 / .14); margin-top: 28px; }
/* the colour of the playing video behind the stage instead of plain black (owner, 2026-10-25; it was switched off by
   "No glows" until then): kx.videoStage paints the centre video (or, for an embed, its still, which the page already
   has) onto a tiny canvas a few times a second, softened inside the canvas; the browser only scales it up and fades
   its edges with a fixed mask, so no filter is redrawn and nothing extra is downloaded. Lite: once a second. */
.cinema .vstage-ambient { display: block; position: absolute; inset: -18% -30%; z-index: -2; pointer-events: none; -webkit-mask-image: radial-gradient(closest-side, #000 45%, rgb(0 0 0 / .8) 60%, rgb(0 0 0 / .45) 75%, rgb(0 0 0 / .15) 88%, transparent 100%); mask-image: radial-gradient(closest-side, #000 45%, rgb(0 0 0 / .8) 60%, rgb(0 0 0 / .45) 75%, rgb(0 0 0 / .15) 88%, transparent 100%); } /* eased edge, no ring where the ramp starts */
.vstage-ambient canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 1.6s var(--ease-in-out); }
.vstage-ambient canvas.is-on { opacity: .85; animation: amb-drift 16s var(--ease-in-out) infinite alternate; }
/* the stage's videos are Vimeo players, whose pixels a page may not read: their colour comes from each video's still and
   changes with the slide, so the light drifts and breathes slowly on its own while a video plays (2026-10-25; a video
   file on this server is read frame by frame instead). Transform only. */
@keyframes amb-drift { 0% { transform: scale(1) translate(0, 0); } 50% { transform: scale(1.12) translate(-3%, 2%); } 100% { transform: scale(1.04) translate(3%, -2%); } }
html.lite .vstage-ambient canvas.is-on { animation: none; }
@media (prefers-reduced-motion: reduce) { .vstage-ambient canvas.is-on { animation: none; } }
@media (max-width: 767px) { .cinema { border-radius: 22px; } .cinema .vstage-wrap { --vs-step: calc(var(--vs-w) * .62); } }

/* clickable cards + the dead zone around the copy chip */
[data-href] { cursor: pointer; }
.badge-zone { padding: 14px; margin: -14px; cursor: default; } /* 14px of "nothing happens" around the chip */
.badge-zone.badge-tr { right: 12px; top: 12px; margin: 0; padding: 14px; transform: translate(14px, -14px); }
/* "Play on Fortnite": full-width dark grey pill at the foot of a game card, arrow in a round chip on the right */
.game-card-link {
  align-self: stretch; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 6px 6px 16px; border-radius: 999px;
  background: rgb(var(--c-graphite)); color: #fff; font-size: .85rem; font-weight: 600; line-height: 1;
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .14), 0 6px 12px -8px rgb(var(--c-graphite) / .7);
  transition: transform var(--dur-quick) var(--ease-knob), background-color var(--dur-base) var(--ease-in-out), box-shadow var(--dur-base) var(--ease-in-out);
}
.game-card-link-ico { flex: none; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; background: rgb(255 255 255 / .14); transition: background-color var(--dur-base) var(--ease-in-out), color var(--dur-base) var(--ease-in-out); }
.game-card-link-ico svg.ico { width: 13px; height: 13px; transition: transform var(--dur-base) var(--ease-in-out); }
.game-card-link:hover { background: rgb(var(--c-graphite) / .88); box-shadow: inset 0 1px 0 rgb(255 255 255 / .14), 0 10px 16px -8px rgb(var(--c-graphite) / .8); }
.game-card-link:hover .game-card-link-ico { background: #fff; color: rgb(var(--c-graphite)); }
.game-card-link:hover .game-card-link-ico svg.ico { transform: translate(1px, -1px); }
.game-card-link:active { transform: scale(.98); }

/* copy chip (island code) */
.chip-copy { cursor: pointer; gap: .35rem; transition: background var(--dur-quick), transform var(--dur-quick) var(--ease-knob); }
.chip-copy svg { width: 12px; height: 12px; opacity: .6; }
.chip-copy:hover { background: rgb(var(--paper)); } .chip-copy:hover svg { opacity: 1; }
.chip-copy:active, .chip-copy.is-copied { background: rgb(var(--c-ink)); color: rgb(var(--c-bg)); border-color: rgb(var(--c-ink)); } /* pressed: inverted */
.chip-copy:active { transform: scale(.96); }
.chip-copy.is-copied svg { opacity: 1; }

/* avatars (helpers.php avatar()): image or initials in a circle; --av sets the size */
.avatar { --av: 40px; width: var(--av); height: var(--av); flex: none; border-radius: 50%; overflow: hidden; display: inline-grid; place-items: center; background: rgb(var(--c-surface2)); border: 1px solid rgb(var(--edge) / .6); box-shadow: var(--sh-card); }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-ini { background: rgb(var(--c-accent)); color: #fff; font-family: var(--font-display); font-weight: 800; font-size: calc(var(--av) * .38); letter-spacing: .02em; transition: background-color .5s var(--ease-in-out); }
.client-card { display: flex; flex-direction: column; gap: 16px; padding: 18px; min-width: 0; container-type: inline-size; }
.client-head { display: flex; align-items: center; gap: 12px; min-width: 0; }
.client-name { flex: 1; min-width: 0; overflow-wrap: anywhere; } /* a long name wraps instead of being cut */
/* the description under the name row, across the whole card (owner, 2026-10-25: two rows instead of two columns) */
.client-desc { margin-top: -6px; font-size: .84rem; line-height: 1.45; color: rgb(var(--c-muted)); }
.client-links { flex: none; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; max-width: 60%; }
.client-links .social-btn { width: 32px; height: 32px; box-shadow: none; background: rgb(var(--c-surface2)); }
.client-links .social-btn svg { width: 15px; height: 15px; }
/* the client's maps: a row of thumbnails inside the card, edge to edge, faded at the ends, that slides on by one map
   every few seconds (mapRows() in site.js moves the track with translate; the set is printed twice so it can wrap).
   Under reduced motion it stands still and scrolls by hand, without the copy. */
.client-maps { margin-top: auto; margin-inline: -18px; overflow: hidden; touch-action: pan-x pan-y; -webkit-mask-image: linear-gradient(90deg, transparent, #000 18px, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(90deg, transparent, #000 18px, #000 calc(100% - 28px), transparent); }
.client-maps-track { display: flex; width: max-content; padding-left: 18px; }
.client-maps-track li { flex: 0 0 calc(66.6cqw - 12px); margin-right: 10px; } /* one and a half in view: cqw of the card (.client-card is the container) */
.client-maps.is-still li[aria-hidden] { display: none; } /* every map fits: nothing to slide */
/* one map (client-card.php .is-single): shown once and still, same size as in the moving rows, no fade at the ends */
.client-maps.is-single { -webkit-mask-image: none; mask-image: none; }
.client-map { display: grid; gap: 7px; -webkit-touch-callout: none; user-select: none; -webkit-user-drag: none; }
.client-map-thumb { display: block; aspect-ratio: 16 / 9; border-radius: 10px; }
.client-map-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .78rem; font-weight: 500; color: rgb(var(--c-ink2)); transition: color var(--dur-base) var(--ease-in-out); }
a.client-map:hover .client-map-thumb img { transform: scale(1.06); }
a.client-map:hover .client-map-name { color: rgb(var(--c-ink)); }
@media (prefers-reduced-motion: reduce) {
  .client-maps { overflow-x: auto; scrollbar-width: none; }
  .client-maps-track { padding-right: 10px; }
  .client-maps-track li[aria-hidden] { display: none; }
}
/* platform brand colours, used on hover by the client badges and the footer social buttons */
.is-youtube { --brand: #ff0000; } .is-tiktok { --brand: #010101; } .is-x { --brand: #000; } .is-instagram { --brand: #e1306c; }
.is-twitch { --brand: #9146ff; } .is-discord { --brand: #5865f2; } .is-fortnite { --brand: #1e90ff; }

/* live strip (partials/live-strip.php, above the island cards): two labelled groups on one grid, "Live now" and
   "Last 7 days" (the period is said once, in the group label). Row 1 holds the two labels, row 2 every figure, row 3
   what each figure counts; the cells take rows 2 and 3 from the strip (subgrid), so all figures share one line and
   one size, the live count included. The sparkline closes the 7-day group. */
.live-strip { display: grid; grid-template-columns: auto repeat(3, minmax(0, 1fr)) clamp(120px, 17%, 190px); grid-template-rows: auto auto auto; gap: 8px clamp(20px, 3.4vw, 48px); align-items: end; padding: clamp(18px, 2.4vw, 26px) clamp(18px, 2.6vw, 30px); background: rgb(var(--paper)); overflow: hidden; }
.live-strip-head { grid-row: 1; display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-family: var(--font-mono); font-size: .7rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-ink2)); white-space: nowrap; }
.live-strip-head.is-live { grid-column: 1; }
.live-strip-head.is-live .live-dot { margin: 0; }
.live-strip-head.is-week { grid-column: 2 / -1; }
.live-strip-range { color: rgb(var(--c-muted)); }
.live-strip-cell { grid-row: 2 / 4; display: grid; grid-template-rows: subgrid; min-width: 0; }
.live-strip-cell.is-live { grid-column: 1; padding-right: clamp(4px, 1.4vw, 20px); }
.live-strip-stats { display: contents; }
.live-strip-num { display: block; margin: 0; font-family: var(--font-display); font-weight: 800; font-size: clamp(1.7rem, 3vw, 2.3rem); line-height: 1; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
.live-strip-lab { display: flex; align-items: center; gap: 6px; font-size: .76rem; line-height: 1.3; color: rgb(var(--c-muted)); }
.live-strip-lab svg.ico { flex: none; width: 13px; height: 13px; color: rgb(var(--c-ink3)); }
.live-strip-spark { grid-column: -2 / -1; grid-row: 2 / 4; align-self: center; min-width: 0; }
.live-strip-spark canvas { width: 100%; height: 48px; }
.live-strip-none { grid-column: 2 / -1; grid-row: 2 / 4; align-self: center; }
@media (max-width: 900px) { /* the sparkline gets its own row under the figures */
  .live-strip { grid-template-columns: auto repeat(3, minmax(0, 1fr)); }
  .live-strip-spark { grid-column: 1 / -1; grid-row: 4; margin-top: 8px; }
}
@media (max-width: 640px) { /* phones: the live figure on top, the three 7-day figures in a row under their label */
  .live-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: none; column-gap: 14px; }
  .live-strip-head.is-live, .live-strip-cell.is-live { grid-column: 1 / -1; }
  .live-strip-head.is-week { grid-column: 1 / -1; grid-row: 4; margin-top: 16px; }
  .live-strip-stats .live-strip-cell { grid-row: 5 / 7; }
  .live-strip-spark { grid-row: 7; }
  .live-strip-none { grid-column: 1 / -1; grid-row: 4; margin-top: 10px; }
  .live-strip-lab { align-items: flex-start; font-size: .7rem; }
  .live-strip-num { font-size: clamp(1.25rem, 6.4vw, 1.7rem); } /* three figures side by side must fit a 320px screen */
  .live-strip-lab svg.ico { margin-top: 2px; }
}
/* ---------- case studies (partials/case-list.php: wide feature + pairs) ---------- */
.case-wide { display: grid; grid-template-columns: 1fr 1fr; overflow: hidden; }
.case-wide > .case-wide-media:first-child { min-height: 300px; height: 100%; border-radius: var(--r-card) 0 0 var(--r-card); } /* beats .card > .media:first-child */
.case-wide-body { padding: clamp(28px, 4.5vw, 60px); display: flex; flex-direction: column; justify-content: center; }
.case-wide-metrics { display: flex; flex-wrap: wrap; gap: 10px 28px; padding-top: 18px; border-top: 1px solid rgb(var(--c-line)); }
.case-wide-metrics div { display: flex; flex-direction: column; gap: 2px; }
.case-wide-metrics dd { margin: 0; font-family: var(--font-display); font-weight: 800; font-size: 1.5rem; line-height: 1; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
.case-wide-metrics dt { font-size: .74rem; color: rgb(var(--c-muted)); }
/* an eyebrow as a tag (a centred heading has no line to hang the square marker on): a small pill, no symbol */
.toned > .wrap > .eyebrow.eyebrow-tag, .eyebrow.eyebrow-tag { display: inline-flex; padding: 6px 12px; border-radius: 999px; background: rgb(var(--c-ink) / .07); color: rgb(var(--c-ink2)); }
.toned > .wrap > .eyebrow.eyebrow-tag::before { content: none; }
.eyebrow.eyebrow-tag + .h2, .eyebrow.eyebrow-tag + .h2.mt-3 { margin-top: 16px; }
/* case study page: the story as numbered cards, the ask beside the map */
.case-parts { margin-top: clamp(32px, 5vw, 56px); }
.case-parts-n { font-family: var(--font-mono); font-size: .7rem; letter-spacing: .09em; color: rgb(var(--c-muted)); }
.case-parts p { font-size: .95rem; line-height: 1.55; color: rgb(var(--c-ink3)); }
.case-ask { display: flex; flex-direction: column; justify-content: center; align-items: flex-start; }
/* case study page: the cover under the headline */
.case-cover { margin-top: clamp(24px, 4vw, 40px); border-radius: var(--r-card); }
/* case study page: the parts (challenge, approach, result, the map) are set apart by space alone, no rules between them */
.case-part { margin-top: clamp(44px, 6vw, 72px); }
@media (max-width: 767px) {
  .case-wide { grid-template-columns: 1fr; }
  .case-wide > .case-wide-media:first-child { min-height: 0; height: auto; aspect-ratio: 16 / 9; border-radius: var(--r-card) var(--r-card) 0 0; }
}

/* ---------- marquee (kree8 logo strip) ---------- */
.marquee { overflow: hidden; -webkit-mask: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.marquee-track { display: flex; gap: 12px; width: max-content; animation: marquee 50s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }
/* a partner: a small card, logo large on top, name and kind under it (partials/partners.php) */
.logo-tile { position: relative; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; width: 184px; padding: 22px 16px 18px; border-radius: var(--r-card); background: rgb(var(--paper)); box-shadow: var(--sh-card); text-align: center; transition: transform var(--dur-base) var(--ease-in-out), box-shadow var(--dur-base) var(--ease-in-out); }
.logo-tile .avatar { margin-bottom: 6px; }
.logo-tile-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; font-size: .95rem; color: rgb(var(--c-ink)); }
.logo-tile-type { font-size: .66rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.logo-tile-go { position: absolute; top: 10px; right: 10px; width: 24px; height: 24px; border-radius: 8px; display: grid; place-items: center; background: rgb(var(--c-surface2)); color: rgb(var(--c-ink3)); transition: background-color var(--dur-base), color var(--dur-base); }
.logo-tile-go svg.ico { width: 11px; height: 11px; }
a.logo-tile:hover { transform: translateY(-4px); box-shadow: var(--sh-card-hover); }
a.logo-tile:hover .logo-tile-go { background: rgb(var(--c-accent)); color: #fff; }
.marquee-track { padding-block: 8px 14px; } /* room for the hover lift and the shadow */
/* Game Development: the partner panel sits well apart from the call to action above it */
.cta-band + .partners-band, section:has(> .cta-band) + .partners-band { margin-top: clamp(32px, 5vw, 64px); }

/* ---------- workflow (partials/workflow.php): a story-style stepper ----------
   One bar per step across the top; the open step's bar fills (pipe-fill) while its panel is on the stage, then
   pipeline() in site.js opens the next (it advances on the bar's animationend, so pausing the animation holds the
   step: .is-paused while hovered, focused or off screen). Steps are buttons: press one to jump, press the stage to
   skip ahead. Panels are stacked in one grid cell and cross-fade with a short rise; the icon pops in and floats.
   The step icons are coloured (--ico from .ico-tone-*). The line that fills is white, and the stage below is neutral
   with depth, in the manner of the liquid-metal button: a top-lit panel, a brushed-metal number with a slow sheen,
   and a scene set into the panel like a small screen, its parts raised in metal, with colour only where it says
   something (see .sc). */
.pipeline { --ico: var(--c-accent); }
.pipeline :is(.pipeline-step, .pipeline-panel) svg:not(.ico-glint) { fill: currentColor; } /* flat icons: the .ico-tone-* classes are used for their colour only, not for the shaded fill of the old icon tiles */
.pipeline-steps { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: clamp(6px, 1vw, 14px); }
.pipeline-step { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; width: 100%; padding: 4px 0 8px; text-align: left; color: rgb(var(--c-muted)); outline-offset: 6px; transition: color .35s var(--ease-in-out); }
.pipeline-step:hover { color: rgb(var(--c-ink2)); }
.pipeline-step.is-done { color: rgb(var(--c-ink3)); }
.pipeline-step.is-active { color: rgb(var(--c-ink)); }
.pipeline-bar { display: block; width: 100%; height: 3px; border-radius: 2px; background: rgb(var(--c-line)); overflow: hidden; transition: height .25s var(--ease-knob), background-color .3s; }
.pipeline-step:hover .pipeline-bar { background: rgb(var(--c-ink) / .28); }
.pipeline-step.is-active .pipeline-bar { height: 4px; }
.pipeline-bar i { display: block; height: 100%; border-radius: inherit; background: rgb(var(--c-ink)); transform: scaleX(0); transform-origin: left; }
.pipeline-step.is-done .pipeline-bar i { transform: scaleX(1); }
.pipeline-step.is-active .pipeline-bar i { animation: pipe-fill var(--pipe-dur, 3.4s) linear forwards; }
.pipeline.is-paused .pipeline-step.is-active .pipeline-bar i { animation-play-state: paused; }
.pipeline.is-static .pipeline-step.is-active .pipeline-bar i { animation: none; transform: scaleX(1); } /* reduced motion: no run, the open step is simply marked */
@keyframes pipe-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.pipeline-name { display: inline-flex; align-items: center; gap: 8px; font-size: .95rem; font-weight: 500; letter-spacing: -.015em; white-space: nowrap; }
.pipeline-name svg.ico { width: 15px; height: 15px; transition: transform .55s var(--ease-overshoot); }
.pipeline-step.is-active .pipeline-name svg.ico { transform: scale(1.3) rotate(-10deg); color: rgb(var(--ico)); }
.pipeline-step.is-done .pipeline-name svg.ico { color: rgb(var(--ico)); }
.pipeline-step:active .pipeline-name { transform: scale(.97); }
.pipeline-stage { display: grid; gap: 10px; margin-top: 22px; touch-action: pan-y; user-select: none; }
.pipeline-panel { position: relative; isolation: isolate; overflow: hidden; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: clamp(18px, 4vw, 52px); min-height: clamp(180px, 20vw, 230px); padding: clamp(22px, 4vw, 48px); border-radius: var(--r-card);
  background: rgb(var(--paper)); box-shadow: var(--sh-card); } /* a flat card like the others (owner, 2026-10-18: no gradient, no heavy drop shadow) */
.js .pipeline-stage { cursor: pointer; }
.js .pipeline-panel { grid-area: 1 / 1; opacity: 0; visibility: hidden; translate: 0 16px; filter: blur(8px); transition: opacity .3s var(--ease-in-out), translate .3s var(--ease-in-out), filter .3s var(--ease-in-out), visibility 0s .3s; }
.js .pipeline-panel.is-on { opacity: 1; visibility: visible; translate: 0 0; filter: none; transition: opacity .5s var(--ease-out) .12s, translate .7s var(--ease-expo) .12s, filter .5s var(--ease-out) .12s, visibility 0s; }
.pipeline-big { font-family: var(--font-display); font-weight: 500; font-size: clamp(3.6rem, 10vw, 7.5rem); letter-spacing: -.07em; line-height: .85; font-variant-numeric: tabular-nums; color: rgb(var(--c-ink));
  padding: .05em .12em .07em .02em; margin: -.05em -.08em -.07em -.02em; /* room for the digits: the fill is clipped to the box, and the tight tracking pulls the last digit past its edge */
  color: rgb(var(--c-ink) / .16); } /* flat: a large quiet number, no metal gradient, no shadow */
.pipeline-n { font-family: var(--font-mono); font-size: .7rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-muted)); }
/* the step's title: a card title in Inter (the headline font is for section headlines only) */
.pipeline-title { margin-top: 8px; font-family: var(--font-body); font-weight: 600; font-size: clamp(1.35rem, 2.3vw, 1.8rem); letter-spacing: -.02em; line-height: 1.15; color: rgb(var(--c-ink)); }
.pipeline-copy .lead { margin-top: 8px; max-width: 30rem; }
/* the scene of a step (.sc, built in workflow.php): a small screen set into the panel, where the step happens. Parts
   are dark metal (--sc-dim) or light metal (--sc-lit), each lifted by a shadow. Colour is used where it means
   something: the record dot is red (--sc-rec), a tick is green (--sc-ok), the platforms light up in their own
   colours, and the one part a scene is about (the bar that was found, the clip being cut, the rounds going up) is in
   the step's own colour (--sc-hot, from --ico). Each colour is shaded top to bottom like the metal, never flat. The
   animations only exist while the panel is open, so each scene starts again when its step comes round. --k is the
   position of a part among its siblings (stagger). */
.sc { --sc-lit: linear-gradient(180deg, #fff, #cfcfcf 48%, #8f8f8f); --sc-dim: linear-gradient(180deg, #5c5c5c, #383838); --sc-drop: 0 5px 8px -3px rgb(0 0 0 / .75);
  --sc-hot: linear-gradient(180deg, color-mix(in srgb, rgb(var(--ico)) 60%, #fff), rgb(var(--ico)) 52%, color-mix(in srgb, rgb(var(--ico)) 66%, #000));
  --sc-ok: linear-gradient(180deg, #86f0a3, #30d158 52%, #1b9038); --sc-rec: linear-gradient(180deg, #ff9088, #ff3b30 52%, #b91d14);
  position: relative; isolation: isolate; display: flex; width: clamp(128px, 20vw, 232px); height: clamp(96px, 13vw, 150px); border-radius: 16px; overflow: hidden;
  background: linear-gradient(180deg, #0b0b0b, #181818); box-shadow: inset 0 3px 12px rgb(0 0 0 / .8), inset 0 -10px 18px -12px rgb(255 255 255 / .06); }
.sc::after { content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none; background: linear-gradient(155deg, rgb(255 255 255 / .09) 0%, rgb(255 255 255 / 0) 38%); } /* the glass over it */
.sc :nth-child(1) { --k: 0; } .sc :nth-child(2) { --k: 1; } .sc :nth-child(3) { --k: 2; } .sc :nth-child(4) { --k: 3; } .sc :nth-child(5) { --k: 4; } .sc :nth-child(6) { --k: 5; } .sc :nth-child(7) { --k: 6; } .sc :nth-child(8) { --k: 7; } .sc :nth-child(9) { --k: 8; }
@keyframes sc-grow { from { transform: scaleY(0); } }
@keyframes sc-found { from { filter: grayscale(1) brightness(.45); } }
/* research: a chart, a line scanning across it, the bar it stops at takes the step's colour */
.sc-research { align-items: flex-end; justify-content: center; gap: 7%; padding: 15% 14% 13%; }
.sc-research i { flex: 1; height: var(--h); border-radius: 4px 4px 1px 1px; background: var(--sc-dim); box-shadow: var(--sc-drop); transform-origin: bottom; }
.sc-research i:nth-child(1) { --h: 34%; } .sc-research i:nth-child(2) { --h: 56%; } .sc-research i:nth-child(3) { --h: 42%; } .sc-research i:nth-child(4) { --h: 90%; background: var(--sc-hot); } .sc-research i:nth-child(5) { --h: 62%; }
.sc-research b { position: absolute; top: 10%; bottom: 10%; left: 10%; width: 2px; border-radius: 1px; background: #fff; box-shadow: 0 0 0 3px rgb(255 255 255 / .08); opacity: 0; }
.is-on .sc-research i { animation: sc-grow .6s var(--ease-expo) calc(var(--k) * 70ms) both; }
.is-on .sc-research i:nth-child(4) { animation: sc-grow .6s var(--ease-expo) 210ms both, sc-found .45s var(--ease-out) 1.5s both; }
.is-on .sc-research b { animation: sc-scan 1.3s var(--ease-in-out) .4s both; }
@keyframes sc-scan { 0% { left: 10%; opacity: 0; } 15%, 85% { opacity: 1; } 100% { left: 67%; opacity: 0; } }
/* recording: the dot blinks, the seconds count up, the sound level moves */
@property --sc-sec { syntax: '<integer>'; inherits: false; initial-value: 0; }
.sc-record { flex-direction: column; align-items: center; justify-content: center; gap: 13px; }
.sc-record em { display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: .68rem; font-weight: 600; font-style: normal; letter-spacing: .14em; color: #fff; }
.sc-record em i { width: 9px; height: 9px; border-radius: 50%; background: var(--sc-rec); box-shadow: var(--sc-drop); }
.sc-record em b { font-weight: 500; color: rgb(255 255 255 / .5); counter-reset: sec var(--sc-sec); }
.sc-record em b::after { content: '00:0' counter(sec); }
.sc-record > span { display: flex; align-items: center; gap: 4px; height: 36%; }
.sc-record > span i { width: 4px; height: 100%; transform: scaleY(.26); border-radius: 2px; background: var(--sc-lit); box-shadow: var(--sc-drop); } /* drawn full height, scaled: the level animates on the compositor */
.is-on .sc-record em i { animation: sc-blink 1.1s steps(2, jump-none) infinite; }
.is-on .sc-record em b { animation: sc-count 9s steps(9, jump-none) infinite; }
.is-on .sc-record > span i { animation: sc-level .7s var(--ease-in-out) calc(var(--k) * -130ms) infinite alternate; }
@keyframes sc-blink { to { opacity: .15; } }
@keyframes sc-count { to { --sc-sec: 9; } }
@keyframes sc-level { from { transform: scaleY(.26); } to { transform: scaleY(1); } }
/* editing: clips drop onto two tracks (the one being cut in the step's colour), a playhead runs over them */
.sc-edit { flex-direction: column; justify-content: center; gap: 9px; padding: 0 12%; }
.sc-edit > span { display: flex; gap: 5px; height: 19%; }
.sc-edit > span i { flex: var(--f, 1); border-radius: 5px; background: var(--sc-dim); box-shadow: var(--sc-drop); transform-origin: left; }
.sc-edit > span:nth-child(1) i:nth-child(1) { --f: 2; } .sc-edit > span:nth-child(1) i:nth-child(2) { --f: 3; background: var(--sc-hot); } .sc-edit > span:nth-child(1) i:nth-child(3) { --f: 1.4; }
.sc-edit > span:nth-child(2) i:nth-child(1) { --f: 1.2; } .sc-edit > span:nth-child(2) i:nth-child(2) { --f: 2.4; background: linear-gradient(180deg, #a6a6a6, #6a6a6a); }
.sc-edit b { position: absolute; z-index: 1; top: 20%; bottom: 20%; left: 12%; width: 2px; border-radius: 1px; background: #fff; box-shadow: 0 0 0 3px rgb(0 0 0 / .35); }
.is-on .sc-edit > span i { animation: sc-clip .7s var(--ease-expo) calc(var(--k) * 110ms) both; }
.is-on .sc-edit b { animation: sc-play 2.4s linear .6s infinite; }
@keyframes sc-clip { from { opacity: 0; transform: translateX(-16px) scaleX(.5); } }
@keyframes sc-play { to { left: 88%; } }
/* review: three lines get their green tick, one after the other */
.sc-review { flex-direction: column; justify-content: center; gap: 10%; padding: 0 15%; }
.sc-review > span { display: flex; align-items: center; gap: 10px; }
.sc-review svg.ico { flex: none; width: 19px; height: 19px; padding: 4px; border-radius: 50%; background: var(--sc-ok); box-shadow: var(--sc-drop); color: #052310; }
.sc-review i { flex: 1; height: 8px; border-radius: 4px; background: var(--sc-dim); box-shadow: var(--sc-drop); }
.sc-review > span:nth-child(2) i { max-width: 62%; } .sc-review > span:nth-child(3) i { max-width: 82%; }
.is-on .sc-review svg.ico { animation: sc-tick .5s var(--ease-overshoot) calc(var(--k) * 420ms + 350ms) both; }
@keyframes sc-tick { from { scale: 0; rotate: -70deg; } }
/* publishing: three platforms come up and switch on, each in its own colour */
.sc-publish { align-items: center; justify-content: center; gap: 7%; }
.sc-publish > span { width: 24%; aspect-ratio: 1; border-radius: 28%; display: grid; place-items: center; background: #474747 linear-gradient(180deg, rgb(255 255 255 / .2), rgb(255 255 255 / 0) 45%, rgb(0 0 0 / .28)); box-shadow: var(--sc-drop); color: #fff; }
.sc-publish > span:nth-child(1) { --pf: #25f4ee; --pf-fg: #04191b; } .sc-publish > span:nth-child(2) { --pf: #ff0033; } .sc-publish > span:nth-child(3) { --pf: #e1306c; } /* TikTok, YouTube, Instagram */
.sc-publish svg { width: 48%; height: 48%; }
.is-on .sc-publish > span { animation: sc-launch .7s var(--ease-overshoot) calc(var(--k) * 240ms + 200ms) both, sc-post .35s var(--ease-out) calc(var(--k) * 240ms + 750ms) forwards; }
@keyframes sc-launch { from { opacity: 0; translate: 0 70%; scale: .5; } }
@keyframes sc-post { to { background-color: var(--pf, #8a8a8a); color: var(--pf-fg, #fff); } }
/* iteration: each round's bar ends a little higher, the loop turns once */
.sc-iterate { align-items: flex-end; justify-content: center; gap: 7%; padding: 20% 15% 13%; }
.sc-iterate i { flex: 1; height: calc(26% + var(--k) * 18%); border-radius: 4px 4px 1px 1px; background: var(--sc-hot); box-shadow: var(--sc-drop); opacity: calc(.34 + var(--k) * .165); transform-origin: bottom; }
.sc-iterate svg.ico { position: absolute; top: 12%; left: 11%; width: 20px; height: 20px; color: rgb(var(--ico)); }
.is-on .sc-iterate i { animation: sc-grow .7s var(--ease-expo) calc(var(--k) * 150ms + 200ms) both; }
.is-on .sc-iterate svg.ico { animation: sc-loop 1.3s var(--ease-in-out) .2s both; }
@keyframes sc-loop { from { rotate: -360deg; opacity: 0; } }
.sc-icon { align-items: center; justify-content: center; color: rgb(var(--ico)); }
.sc-icon svg.ico { width: 38%; height: 38%; }
/* realistic mock-ups of the steps (.sc-mock > .mk, workflow.php; owner, 2026-10-20: "a visual representation of a step
   instead of placeholder animations"): the screen each step happens on, drawn small, with the site's own videos in it.
   The screen is a size container and everything inside is in em of a font size set in cqw, so a mock scales as one
   picture from phone to desktop. Dark UI greys; colour only where it means something (red record dot, green ticks, the
   platforms' colours, the accent for what the step is about). Transform and opacity only, while the panel is open; none
   in lite mode or with reduced motion: the resting state is the finished picture, the animations run from it. */
.sc.sc-mock { display: block; width: clamp(240px, 30vw, 400px); height: auto; aspect-ratio: 16 / 10; container-type: inline-size; background: #0d0d0d; }
.mk { position: absolute; inset: 0; display: grid; min-height: 0; font-family: var(--font-body); font-size: 2.9cqw; line-height: 1.25; color: #ececec; text-align: left; }
.mk img { display: block; width: 100%; height: 100%; object-fit: cover; background: #1c1c1c; }
/* a step's own screen (Admin → Workflow steps → Own HTML): the owner's HTML in a sandboxed iframe filling the screen;
   it takes no clicks, so a press or swipe on it still moves the stage on */
.sc-own iframe { position: absolute; inset: 0; display: block; width: 100%; height: 100%; border: 0; pointer-events: none; }
.mk svg { flex: none; width: 1em; height: 1em; }
.mk-bar { display: flex; align-items: center; gap: .45em; min-width: 0; height: 2.5em; padding: 0 .9em; background: #171717; font-size: .9em; color: rgb(255 255 255 / .7); white-space: nowrap; }
.mk-bar > i { flex: none; width: .65em; height: .65em; border-radius: 50%; background: #3b3b3b; }
.mk-title { min-width: 0; margin-left: .5em; overflow: hidden; text-overflow: ellipsis; font-family: var(--font-mono); font-size: .9em; }
.mk-title em { margin-left: .6em; padding: .1em .45em; border-radius: .35em; background: #262626; font-style: normal; color: rgb(255 255 255 / .55); }
.mk-chip { display: inline-flex; align-items: center; gap: .35em; padding: .3em .6em; border-radius: .5em; background: rgb(var(--c-accent)); font-size: .78em; font-weight: 600; color: #fff; white-space: nowrap; }
@keyframes mk-rise { from { opacity: 0; transform: translateY(14%); } }
@keyframes mk-pop { from { opacity: 0; transform: scale(.4); } }
@keyframes mk-dim { to { opacity: .4; } }
@keyframes mk-clip { from { opacity: 0; transform: translateX(-30%); } }
/* research: the map's name is typed into a search, the most-viewed videos come up, the top one is picked */
.mk-research { grid-template-rows: auto auto minmax(0, 1fr); }
.mk-search { flex: 1; min-width: 0; display: flex; align-items: center; gap: .5em; height: 1.75em; margin-left: .5em; padding: 0 .75em; border-radius: .6em; background: #242424; color: #fff; }
.mk-search svg { opacity: .55; }
.mk-type { position: relative; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mk-type::after { content: ''; position: absolute; inset: 0; background: #242424; transform: translateX(101%); } /* slides off the text to the right, letter by letter */
.is-on .mk-type::after { animation: mk-type 1s steps(16) .35s both; }
@keyframes mk-type { from { transform: translateX(0); } }
.mk-label { padding: .85em .95em .5em; font-family: var(--font-mono); font-size: .7em; font-weight: 500; letter-spacing: .1em; text-transform: uppercase; color: rgb(255 255 255 / .45); }
.mk-results { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .6em; min-height: 0; padding: 0 .9em .9em; }
.mk-res { position: relative; min-height: 0; border-radius: .6em; overflow: hidden; }
.mk-views { position: absolute; left: .45em; bottom: .45em; display: inline-flex; align-items: center; gap: .3em; padding: .2em .45em; border-radius: .45em; background: rgb(0 0 0 / .62); font-size: .8em; font-weight: 600; color: #fff; }
.mk-res .mk-chip { position: absolute; top: .45em; left: .45em; }
.is-on .mk-res { animation: mk-rise .55s var(--ease-expo) calc(1.1s + var(--i) * 110ms) both; }
.is-on .mk-res:not(.is-top) { animation: mk-rise .55s var(--ease-expo) calc(1.1s + var(--i) * 110ms) both, mk-dim .4s var(--ease-out) 2.1s forwards; }
.is-on .mk-res .mk-chip { animation: mk-pop .45s var(--ease-overshoot) 2.05s both; }
/* recording: a capture of gameplay with viewfinder corners, REC and the running seconds, the map, the sound level */
.mk-record { background: #000; }
.mk-record::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgb(0 0 0 / .45), rgb(0 0 0 / 0) 30%, rgb(0 0 0 / 0) 60%, rgb(0 0 0 / .6)); } /* a dark scrim, so the labels read on any frame */
.mk-record > img { position: absolute; inset: 0; }
.mk-record > span { position: absolute; z-index: 1; }
/* one scale for the whole overlay (owner, 2026-10-26: "a bit cramped, unify the spacings"): the corner guides --g in from
   every edge, every label --in from every edge (well inside the guides), all labels one size and height; in cqw of the
   screen, so the four sides are equal and it scales with the screen */
.mk-record { --g: 3.5cqw; --in: 6.5cqw; } /* a touch more compact (owner, 2026-10-26), still clear of the guides */
.mk-guides { inset: var(--g); } /* the labels below sit inside these corners */
.mk-guides i { position: absolute; width: 7cqw; aspect-ratio: 1; border: 0 solid rgb(255 255 255 / .7); }
.mk-guides i:nth-child(1) { top: 0; left: 0; border-width: 2px 0 0 2px; } .mk-guides i:nth-child(2) { top: 0; right: 0; border-width: 2px 2px 0 0; }
.mk-guides i:nth-child(3) { bottom: 0; left: 0; border-width: 0 0 2px 2px; } .mk-guides i:nth-child(4) { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
.mk-rec, .mk-spec, .mk-map { display: inline-flex; align-items: center; height: 1.9em; padding: 0 .7em; border-radius: .5em; background: rgb(0 0 0 / .55); color: #fff; font-size: .8em; line-height: 1.25; white-space: nowrap; }
.mk-rec { top: var(--in); left: var(--in); gap: .45em; font-family: var(--font-mono); font-weight: 600; letter-spacing: .08em; }
.mk-rec > i { width: .7em; height: .7em; border-radius: 50%; background: #ff3b30; }
.mk-clock { display: inline-flex; margin-left: .3em; font-weight: 500; color: rgb(255 255 255 / .75); }
.mk-roll { display: inline-block; height: 1.25em; line-height: 1.25em; overflow: hidden; }
.mk-roll > span { display: flex; flex-direction: column; }
.mk-roll b { font-weight: inherit; }
.is-on .mk-rec > i { animation: sc-blink 1.1s steps(2, jump-none) infinite; }
.is-on .mk-roll > span { animation: mk-roll 10s steps(10) infinite; } /* the last digit counts the seconds */
@keyframes mk-roll { to { transform: translateY(-100%); } }
.mk-spec { top: var(--in); right: var(--in); font-family: var(--font-mono); color: rgb(255 255 255 / .8); }
.mk-map { left: var(--in); bottom: var(--in); gap: .45em; max-width: 58%; font-weight: 600; }
.mk-map > span { overflow: hidden; text-overflow: ellipsis; }
.mk-level { right: var(--in); bottom: var(--in); display: flex; align-items: flex-end; gap: .22em; height: 1.52em; } /* as tall as a label (1.9em of .8em) */
.mk-level i { width: .32em; height: 100%; border-radius: .16em; background: #fff; transform: scaleY(.3); transform-origin: bottom; }
.is-on .mk-level i { animation: mk-level .6s var(--ease-in-out) calc(var(--k) * -110ms) infinite alternate; }
@keyframes mk-level { to { transform: scaleY(1); } }
/* editing: the cut's 9:16 preview with its caption popping in word by word, clips dropping onto the timeline, the
   sound under them, the captions track in the accent, a playhead running over it all */
.mk-edit { grid-template-rows: auto minmax(0, 1fr); }
.mk-editor { display: grid; grid-template-columns: 31% minmax(0, 1fr); gap: .75em; min-height: 0; padding: .75em; }
.mk-preview { position: relative; min-height: 0; border-radius: .55em; overflow: hidden; }
.mk-tl { position: relative; display: grid; grid-template-rows: auto minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, .5fr); /* clips lead, captions a thin strip */ gap: .45em; min-width: 0; min-height: 0; padding: .5em; border-radius: .55em; background: #141414; overflow: hidden; }
.mk-ruler { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: .62em; color: rgb(255 255 255 / .4); }
.mk-track { display: flex; gap: .25em; min-height: 0; }
.mk-v > span { flex: var(--f, 1) 1 0; min-width: 0; border-radius: .3em; overflow: hidden; }
.mk-v img { opacity: .9; }
.mk-a { align-items: center; gap: .14em; padding: 0 .3em; border-radius: .3em; background: #1e1e1e; }
.mk-a i { flex: 1; height: 80%; border-radius: 1px; background: rgb(255 255 255 / .5); transform: scaleY(var(--h)); }
.mk-cc > span { flex: var(--f, 1) 1 0; border-radius: .3em; background: rgb(var(--c-accent)); }
.mk-head { position: absolute; top: .3em; bottom: .3em; left: .5em; right: .5em; pointer-events: none; }
.mk-head::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 2px; border-radius: 1px; background: #fff; }
.is-on .mk-v > span { animation: mk-clip .55s var(--ease-expo) calc(.15s + var(--k) * 120ms) both; }
.is-on .mk-cc > span { animation: mk-clip .5s var(--ease-expo) calc(.7s + var(--k) * 120ms) both; }
.is-on .mk-head { animation: mk-sweep 3s linear .5s infinite; }
@keyframes mk-sweep { from { transform: translateX(0); } to { transform: translateX(100%); } }
/* review: the cut on a phone with a note pinned to its first second, four checks ticking off, then approved */
.mk-review { grid-template-columns: 34% minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); align-items: center; gap: 1.1em; padding: 1em 1.2em; }
.mk-phone { height: 100%; min-height: 0; aspect-ratio: 9 / 16; justify-self: center; padding: .28em; border-radius: 1.1em; background: #262626; }
.mk-phone-in { position: relative; display: block; height: 100%; border-radius: .85em; overflow: hidden; }
.mk-pin { position: absolute; top: 16%; left: 10%; display: inline-flex; align-items: center; gap: .3em; padding: .25em .5em; border-radius: .6em .6em .6em .15em; background: #fff; font-family: var(--font-mono); font-size: .7em; font-weight: 600; color: #111; }
.is-on .mk-pin { animation: mk-pop .4s var(--ease-overshoot) .3s both; }
/* the review talked over in a Discord channel (owner, 2026-10-26): Discord's dark chat colours, its default avatars (the
   logo on a coloured circle), each message rising in after the one before, the reviewer's last one with a reaction */
.mk-chat { align-self: stretch; display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; min-height: 0; padding: .6em .7em .7em; border-radius: .7em; background: #313338; }
.mk-dc-head { display: flex; align-items: center; gap: .35em; padding-bottom: .5em; border-bottom: 1px solid rgb(0 0 0 / .3); font-size: .86em; font-weight: 700; color: #f2f3f5; }
.mk-dc-head b { font-size: 1.25em; font-weight: 500; line-height: 1; color: #80848e; }
.mk-msgs { display: grid; align-content: center; gap: .75em; min-height: 0; overflow: hidden; padding-block: .5em; } /* centred under the channel name (owner, 2026-10-26: first it sat pushed to the bottom, then "more centered") */
.mk-msg { display: grid; grid-template-columns: 2.1em minmax(0, 1fr); column-gap: .6em; align-items: start; font-size: .88em; line-height: 1.3; } /* bigger, with short lines (owner, 2026-10-26) */
.mk-ava { grid-row: 1 / span 3; width: 2.1em; height: 2.1em; border-radius: 50%; display: grid; place-items: center; background: var(--ava); color: #fff; }
.mk .mk-ava svg { width: 1.25em; height: 1.25em; }
.mk-who { display: flex; align-items: baseline; gap: .45em; min-width: 0; white-space: nowrap; }
.mk-who b { font-weight: 600; color: #f2f3f5; }
.mk-who em { font-style: normal; font-size: .82em; color: #949ba4; }
.mk-say { color: #dbdee1; }
.mk-react { justify-self: start; display: inline-flex; align-items: center; gap: .3em; margin-top: .3em; padding: .12em .45em; border-radius: .45em; background: rgb(88 101 242 / .22); box-shadow: inset 0 0 0 1px #5865f2; font-size: .92em; font-weight: 600; color: #dee0fc; }
.mk .mk-react svg { width: .95em; height: .95em; color: #3ba55c; }
.is-on .mk-msg { animation: mk-msg .45s var(--ease-out) calc(.35s + var(--i) * 550ms) both; }
.is-on .mk-react { animation: mk-pop .45s var(--ease-overshoot) 2.5s both; }
@keyframes mk-msg { from { opacity: 0; transform: translateY(.7em); } }
/* publishing: one phone per platform, each with that platform's mark, then marked posted */
.mk-publish { grid-template-columns: repeat(3, minmax(0, 1fr)); align-content: center; gap: 6%; padding: 0 9%; }
.mk-post { display: grid; justify-items: center; gap: .55em; min-width: 0; }
.mk-post.is-tiktok { --pf: #25f4ee; --pf-fg: #04191b; } .mk-post.is-youtube { --pf: #ff0033; } .mk-post.is-instagram { --pf: #e1306c; }
.mk-screen { position: relative; width: 100%; aspect-ratio: 9 / 16; padding: .25em; border-radius: 1em; background: #262626; }
.mk-screen img { border-radius: .8em; }
.mk-plat { position: absolute; top: .65em; left: .65em; width: 1.75em; height: 1.75em; border-radius: .5em; display: grid; place-items: center; background: var(--pf); color: var(--pf-fg, #fff); }
.mk-plat svg { width: 58%; height: 58%; }
.mk-posted { display: inline-flex; align-items: center; gap: .3em; padding: .28em .6em; border-radius: .5em; background: #1f1f1f; font-size: .78em; font-weight: 600; color: #fff; }
.mk-posted svg { color: #30d158; }
.is-on .mk-post { animation: mk-rise .6s var(--ease-expo) calc(.1s + var(--i) * 180ms) both; }
.is-on .mk-posted { animation: mk-pop .4s var(--ease-overshoot) calc(1s + var(--i) * 300ms) both; }
/* iteration: views per video from the stored numbers, the best in the accent, and the loop to the next round */
.mk-iterate { grid-template-rows: auto minmax(0, 1fr) auto; }
.mk-rows { display: grid; align-content: center; gap: .55em; min-height: 0; padding: .6em 1em; }
.mk-row { display: grid; grid-template-columns: 1.85em 34% minmax(0, 1fr) 3.4em; align-items: center; gap: .65em; font-size: .82em; }
.mk-thumb { height: 3.3em; border-radius: .35em; overflow: hidden; }
.mk-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: rgb(255 255 255 / .7); }
.mk-meter { height: 1em; border-radius: .3em; background: #1e1e1e; overflow: hidden; }
.mk-meter i { display: block; height: 100%; background: #5e5e5e; transform: scaleX(var(--w)); transform-origin: left; }
.mk-row b { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; color: #fff; }
.mk-row.is-best .mk-name { font-weight: 600; color: #fff; }
.mk-row.is-best .mk-meter i { background: rgb(var(--c-accent)); }
.mk-next { justify-self: start; margin: 0 1em .95em; }
.is-on .mk-meter i { animation: mk-meter .8s var(--ease-expo) calc(.25s + var(--i) * 130ms) both; }
@keyframes mk-meter { from { transform: scaleX(0); } }
.is-on .mk-next { animation: mk-pop .45s var(--ease-overshoot) 1.5s both; }
.is-on .mk-next svg { animation: sc-loop 1.1s var(--ease-in-out) 1.6s both; }
.js .pipeline-panel.is-on .pipeline-art:has(.sc-mock) { animation: mk-in .7s var(--ease-expo) .15s both; } /* a screen rises in, it does not spin like the small scenes */
@keyframes mk-in { from { opacity: 0; translate: 0 18px; } }
html.lite :is(.mk, .mk *, .mk *::before, .mk *::after) { animation: none !important; }
@media (prefers-reduced-motion: reduce) { :is(.mk, .mk *, .mk *::before, .mk *::after) { animation: none !important; } }
.js .pipeline-panel.is-on .pipeline-big { animation: pipe-rise .7s var(--ease-expo) .1s both; }
.js .pipeline-panel.is-on .pipeline-art { animation: pipe-pop .75s var(--ease-overshoot) .2s both; }
@keyframes pipe-rise { from { opacity: 0; translate: 0 40%; } }
@keyframes pipe-pop { from { opacity: 0; scale: .5; rotate: -30deg; } }
@media (max-width: 899px) {
  .pipeline-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } /* bars and icons only: the name is in the panel */
  .pipeline-step { align-items: center; gap: 10px; }
  .pipeline-name svg.ico { width: 17px; height: 17px; }
  .pipeline-panel { grid-template-columns: minmax(0, 1fr) auto; }
  .pipeline-big { display: none; }
}
@media (max-width: 480px) {
  /* phones: the scene becomes the head of the panel, a full-width screen with the scene in its middle, and the copy
     gets the whole width under it (side by side, a long step title ran into the scene) */
  .pipeline-panel { grid-template-columns: minmax(0, 1fr); gap: 16px; min-height: 0; padding: 14px 14px 20px; }
  .pipeline-art { order: -1; display: grid; place-items: center; padding-block: 6px; border-radius: 14px; background: linear-gradient(180deg, #0b0b0b, #181818); box-shadow: inset 0 3px 12px rgb(0 0 0 / .8), inset 0 -10px 18px -12px rgb(255 255 255 / .06); }
  .pipeline-art .sc { width: 168px; height: 110px; background: none; box-shadow: none; border-radius: 0; }
  .pipeline-art .sc::after { content: none; }
  .js .pipeline-panel.is-on .pipeline-art { animation: none; }
  .js .pipeline-panel.is-on .pipeline-art .sc { animation: pipe-rise .7s var(--ease-expo) .15s both; }
  .pipeline-art:has(.sc-mock) { padding: 0; overflow: hidden; } /* a mock-up fills the screen edge to edge */
  .pipeline-art .sc.sc-mock { width: 100%; height: auto; }
  .pipeline-copy { padding-inline: 6px; }
}
/* who does what, under the stepper: the lines arrive one after the other, each icon pops in */
.js .scope.reveal .scope-list li { opacity: 0; translate: 0 8px; transition: opacity .45s var(--ease-out), translate .6s var(--ease-expo); transition-delay: calc(var(--i, 0) * 90ms + 220ms); }
.js .scope.reveal.in .scope-list li { opacity: 1; translate: 0 0; }
.js .scope.reveal .scope-list svg.ico { scale: 0; transition: scale .5s var(--ease-overshoot); transition-delay: calc(var(--i, 0) * 90ms + 340ms); }
.js .scope.reveal.in .scope-list svg.ico { scale: 1; }
/* ---------- charts ---------- */
.chart-box { height: 300px; }
/* the title of a chart card: what the bars are, large, with the dates it covers beside it */
.chart-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px 16px; flex-wrap: wrap; }
.chart-title { font-size: clamp(1.4rem, 2.4vw, 1.9rem); }
.chart-range { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: .8rem; color: rgb(var(--c-muted)); }
.legend span { display: inline-flex; align-items: center; gap: 10px; }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 50%; flex: none; }

/* ---------- forms ---------- */
.label { display: block; font-size: .82rem; font-weight: 600; color: rgb(var(--c-ink2)); margin-bottom: 6px; }
.field {
  width: 100%; border-radius: var(--r-input); border: 1px solid rgb(var(--c-line)); background: rgb(var(--paper)); padding: 12px 14px; color: rgb(var(--c-ink));
  transition: border-color var(--dur-quick) var(--ease-in-out), box-shadow var(--dur-base) var(--ease-in-out);
}
.field::placeholder { color: rgb(160 175 187); }
.field:hover { border-color: rgb(var(--c-muted)); }
.field:focus { outline: none; border-color: rgb(var(--c-accent)); box-shadow: 0 0 0 4px rgb(var(--c-accent) / .15); }
textarea.field { min-height: 120px; resize: vertical; }
select.field { appearance: none; background-image: linear-gradient(45deg, transparent 50%, rgb(var(--c-muted)) 50%), linear-gradient(135deg, rgb(var(--c-muted)) 50%, transparent 50%); background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 40px; }
.check { display: flex; align-items: center; gap: 10px; font-size: .9rem; cursor: pointer; }
.check input { width: 18px; height: 18px; accent-color: rgb(var(--c-accent)); }
/* numeric stepper (Numora-style) */
.num { display: flex; align-items: stretch; border: 1px solid rgb(var(--c-line)); border-radius: var(--r-input); background: rgb(var(--paper)); overflow: hidden; transition: border-color var(--dur-quick), box-shadow var(--dur-base) var(--ease-in-out); }
.num:focus-within { border-color: rgb(var(--c-accent)); box-shadow: 0 0 0 4px rgb(var(--c-accent) / .15); }
.num .field { border: 0; box-shadow: none; border-radius: 0; text-align: center; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.num-btn { width: 44px; display: grid; place-items: center; color: rgb(var(--c-ink2)); background: rgb(var(--c-surface2)); font-size: 1.1rem; user-select: none; transition: background var(--dur-quick); }
.num-btn:hover { background: rgb(var(--c-line)); }
.num-btn:active { transform: scale(.95); }
.num-preview { font-family: var(--font-mono); font-size: .76rem; color: rgb(var(--c-muted)); margin-top: 6px; min-height: 1em; }
.alert { border-radius: 12px; padding: 12px 14px; font-size: .9rem; border: 1px solid; }
.alert-ok { border-color: rgb(var(--c-ok) / .3); background: rgb(var(--c-ok) / .08); color: rgb(var(--c-ok)); }
.alert-err { border-color: rgb(var(--c-err) / .3); background: rgb(var(--c-err) / .08); color: rgb(var(--c-err)); }
.hp { position: absolute; left: -9999px; }

/* ---------- contact (partials/contact.php): the message form ---------- */
.contact { position: relative; scroll-margin-top: 90px; }
/* two columns (contact.php): the pitch and the other ways in on the left, the form on its own panel on the right */
.contact-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: clamp(28px, 5vw, 72px); align-items: start; }
@media (max-width: 899px) { .contact-grid { grid-template-columns: 1fr; } }
.contact-form { padding: clamp(20px, 3vw, 36px); background: rgb(var(--paper)); }
.contact-form .btn-block { margin-top: 24px; }
.contact-form .hp + * { margin-top: 0; } /* the hidden fields come first in the form: the first visible row gets no extra gap, so the panel's padding is the same at the top and the bottom */
/* the other ways in: one card per channel, icon tile on the left, what it is above where to find it */
.contact-ways { display: grid; gap: 10px; max-width: 30rem; }
.contact-way { display: flex; align-items: center; gap: 14px; padding: 12px 14px; border-radius: var(--r-card); background: rgb(var(--paper)); box-shadow: var(--sh-card); transition: transform var(--dur-quick) var(--ease-knob), box-shadow var(--dur-base) var(--ease-in-out); }
.contact-way:hover { transform: translateY(-2px); box-shadow: var(--sh-card-hover); }
.contact-way-ico { flex: none; width: 44px; height: 44px; border-radius: var(--r-btn); display: grid; place-items: center; background: rgb(var(--c-accent)); color: #fff; }
.contact-way-ico svg { width: 19px; height: 19px; }
.contact-way > span:last-child { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.contact-way small { font-size: .74rem; color: rgb(var(--c-muted)); }
.contact-way b { font-weight: 500; color: rgb(var(--c-ink)); overflow-wrap: anywhere; }
/* the send button jumps once, the moment the form is ready */
.btn.is-pop { animation: btn-pop .6s var(--ease-overshoot); }
@keyframes btn-pop { 40% { transform: scale(1.09); } }
/* message length meter (next to the "Project details" label): a ring that fills towards the minimum length, a green
   check once the message is long enough, amber then red as it nears the maximum. data-state and --mp (0..100) come
   from contact() in site.js; --mp is a registered property so the ring animates between values. */
@property --mp { syntax: '<number>'; inherits: true; initial-value: 0; }
.msg-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 6px; }
.msg-head .label { margin-bottom: 0; }
.msg-meter { --m: var(--c-accent); --mp: 0; display: inline-flex; align-items: center; gap: 7px; font-size: .74rem; font-weight: 600; color: rgb(var(--c-muted)); font-variant-numeric: tabular-nums; transition: --mp .35s var(--ease-out), color var(--dur-base) var(--ease-in-out); }
.msg-ring { position: relative; flex: none; width: 18px; height: 18px; display: grid; place-items: center; }
.msg-ring::before { content: ''; position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(rgb(var(--m)) calc(var(--mp) * 1%), rgb(var(--c-line)) 0); -webkit-mask: radial-gradient(closest-side, transparent 56%, #000 60%); mask: radial-gradient(closest-side, transparent 56%, #000 60%); }
.msg-ring svg.ico { position: relative; width: 10px; height: 10px; color: #fff; opacity: 0; scale: .4; transition: opacity var(--dur-base) var(--ease-in-out), scale .35s var(--ease-overshoot); }
.msg-meter[data-state="ok"] { --m: var(--c-ok); color: rgb(var(--c-ok)); }
.msg-meter[data-state="ok"] .msg-ring::before { -webkit-mask: none; mask: none; background: rgb(var(--c-ok)); }
.msg-meter[data-state="ok"] .msg-ring svg.ico { opacity: 1; scale: 1; }
.msg-meter[data-state="near"] { --m: var(--c-fire); color: rgb(var(--c-fire)); }
.msg-meter[data-state="full"] { --m: var(--c-err); color: rgb(var(--c-err)); }

/* ---------- footer ---------- */
.footer { padding-block: 32px 22px; }
.footer-links { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: .9rem; }
.footer-links a { color: rgb(var(--c-ink3)); transition: opacity var(--dur-quick) var(--ease-in-out); }
.footer-links a:hover { opacity: .7; }
.footer-legal { margin-top: 24px; font-size: .74rem; color: rgb(var(--c-muted)); }
.social { display: flex; flex-wrap: wrap; gap: 8px; }
.social-btn { width: 38px; height: 38px; border-radius: 50%; display: inline-grid; place-items: center; background: rgb(var(--paper)); color: rgb(var(--c-ink3)); border: 1px solid rgb(var(--edge) / .5); box-shadow: var(--sh-card); transition: color var(--dur-base), transform var(--dur-quick) var(--ease-knob), box-shadow var(--dur-base) var(--ease-in-out); }
.social-btn svg { width: 17px; height: 17px; }
.social-btn:hover { color: var(--brand, rgb(var(--c-accent))); transform: translateY(-2px); box-shadow: var(--sh-card-hover); }

/* ---------- toasts, progress ---------- */
.kx-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 100; background: rgb(var(--c-accent)); transform-origin: left; transform: scaleX(0); transition: transform .5s var(--ease-out), opacity .3s; }
.kx-toasts { position: fixed; right: 16px; bottom: 16px; z-index: 120; display: flex; flex-direction: column; gap: 8px; max-width: min(380px, calc(100vw - 32px)); }
.kx-toast { padding: 12px 16px; border-radius: 14px; background: rgb(var(--c-dark2)); color: #fff; font-size: .9rem; box-shadow: var(--sh-card-hover); opacity: 0; transform: translateY(12px) scale(.96); transition: opacity .3s, transform .4s var(--ease-overshoot); }
.kx-toast.in { opacity: 1; transform: none; }
.kx-toast.is-err { background: rgb(var(--c-err)); }

/* ---------- section tones (see --h-* in :root) ---------- */
.tone-blue { --tone: var(--h-blue); } .tone-violet { --tone: var(--h-violet); } .tone-teal { --tone: var(--h-teal); }
.tone-green { --tone: var(--h-green); } .tone-fire { --tone: var(--h-fire); } .tone-gold { --tone: var(--h-gold); }
/* .toned: the section's own eyebrow (not the ones inside its cards) gets a small diamond marker in the tone. Only the
   marker is coloured; the label keeps the neutral eyebrow grey, so a page does not turn into a rainbow. */
.toned > .eyebrow, .toned > .row-between > div > .eyebrow, .toned > .wrap > .eyebrow { display: flex; align-items: center; gap: 8px; }
.toned > .eyebrow::before, .toned > .row-between > div > .eyebrow::before, .toned > .wrap > .eyebrow::before { content: ''; flex: none; width: 7px; height: 7px; border-radius: 2px; background: rgb(var(--tone, var(--c-accent))); rotate: 45deg; }
.toned > .wrap.center > .eyebrow { justify-content: center; }
/* .band: the section sits on a neutral panel a shade darker than the sheet (the light counterpart of the dark .cinema
   panel), so sections are told apart by the panel, not by a different colour each */
.band { background: color-mix(in srgb, rgb(var(--c-ink)) 4%, rgb(var(--c-bg))); border-radius: var(--r-sheet); }
/* section transitions (owner, 2026-10-26: "no rounded corners between section transitions"): on the public pages the
   panels (.hero, .band, .cinema) are square and follow each other with no gap; the admin keeps its rounded panels */
html:not([data-division="admin"]) :is(.hero, .band, .cinema) { border-radius: 0; }
.cinema.toned > .eyebrow::before, .cinema.toned > .row-between > div > .eyebrow::before { background: color-mix(in srgb, rgb(var(--tone, var(--c-accent))) 60%, #fff); }
/* division cards on home: eyebrow and arrow in the division's own colour */
.div-card[class*="tone-"] .eyebrow { color: rgb(var(--tone)); }
.div-card[class*="tone-"]:hover .arrow-box { background: rgb(var(--tone)); }
@media (max-width: 767px) { .band { border-radius: 22px; } }

/* rank chips on island cards, coloured like loot rarity (game-card.php picks the tier from the genre rank) */
.chip-rank { --rank: var(--c-ink3); background: rgb(var(--rank)); color: #fff; border-color: rgb(255 255 255 / .35); box-shadow: 0 4px 10px -4px rgb(var(--rank) / .8); }
.rank-legendary { --rank: 217 119 6; } .rank-epic { --rank: var(--h-violet); } .rank-rare { --rank: var(--h-blue); } .rank-uncommon { --rank: var(--h-green); }

/* ---------- mid-page call to action (partials/cta.php): a dark band, so it stands out from the light sections ---------- */
.cta-band { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 20px 32px; padding: clamp(22px, 3.2vw, 36px); border-radius: var(--r-sheet); background: rgb(var(--c-dark)); color: rgb(255 255 255 / .78); box-shadow: 0 18px 34px -22px rgb(var(--c-dark) / .9); }
.cta-band .display { color: #fff; }
.cta-band-text { margin-top: 8px; max-width: 34rem; }
.cta-band-points { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 14px; font-size: .82rem; color: rgb(255 255 255 / .7); }
.cta-band-points li { display: inline-flex; align-items: center; gap: 6px; }
.cta-band-points svg.ico { color: color-mix(in srgb, rgb(var(--c-accent)) 55%, #fff); }
.cta-band-actions { display: flex; flex-direction: column; gap: 10px; min-width: 220px; }
.cta-band-alt { color: #fff; background: rgb(255 255 255 / .1); border-color: rgb(255 255 255 / .24); }
.cta-band-alt:hover { background: rgb(255 255 255 / .18); }
@media (max-width: 767px) { .cta-band { grid-template-columns: 1fr; border-radius: 22px; } .cta-band-actions { min-width: 0; } }
/* real deadlines only (Admin → Settings): the season countdown above the band's title, the availability note in the contact section */
.cta-band-tag { display: inline-flex; align-items: flex-start; gap: 8px; margin-bottom: 14px; padding: 7px 12px; border-radius: 999px; background: rgb(var(--c-ink) / .07); font-size: .8rem; line-height: 1.4; color: rgb(var(--c-ink2)); white-space: normal; }
.cta-band-tag svg.ico { flex: none; width: 14px; height: 14px; margin-top: 2px; color: rgb(var(--c-fire)); }
.cta-band-tag b { color: rgb(var(--c-ink)); font-weight: 600; font-variant-numeric: tabular-nums; }
.contact-open { gap: 8px; padding: 7px 12px; background: rgb(var(--c-ink) / .07); font-size: .8rem; color: rgb(var(--c-ink2)); white-space: normal; }
.contact-open .live-dot { margin: 0; flex: none; }
/* "The cost of waiting" season banner (partials/waiting.php, fomo() in site.js): a dark panel in both themes with key
   art behind the middle, the heading on the left and the clock on the right as four boxed figures with one line under
   them. The art fades out to the left and right, so the heading and the clock sit on plain dark. */
.season { position: relative; isolation: isolate; overflow: hidden; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 24px 40px; padding: clamp(24px, 4vw, 48px); border-radius: var(--r-sheet); background: #07080c; color: rgb(255 255 255 / .72); }
.season.no-clock { grid-template-columns: 1fr; }
.season-art { position: absolute; top: 0; bottom: 0; left: 26%; z-index: -1; width: 54%; max-width: none; height: 100%; object-fit: cover; opacity: .8; }
/* the fade into the panel at both sides of the art: an overlay in the panel colour (a mask is not applied to video everywhere) */
.season::after { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: linear-gradient(90deg, #07080c 26%, rgb(7 8 12 / .55) 40%, rgb(7 8 12 / .2) 54%, rgb(7 8 12 / .55) 68%, #07080c 80%); }
.season .display { color: #fff; max-width: 15em; }
.season .dim { color: rgb(255 255 255 / .5); }
.season-copy { max-width: 32rem; }
.season-side { display: grid; justify-items: end; gap: 12px; }
.season-clock-label { font-size: .7rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: rgb(255 255 255 / .6); }
.season-copy .season-label + .h2 { margin-top: 6px; } /* the label belongs to the heading: tight on top of it */
.season-clock { display: flex; gap: 10px; }
.season-clock span { position: relative; display: grid; justify-items: center; align-content: center; gap: 6px; min-width: 66px; padding: 14px 10px 12px; border-radius: var(--r-card); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  /* depth instead of an outline (owner, 2026-10-18): lit from the top (lighter top, darker foot), a shine on the top
     edge, a dark lip at the bottom and a short shadow under the tile, like a flip clock */
  background: linear-gradient(180deg, rgb(44 46 58 / .9) 0%, rgb(24 25 33 / .92) 50%, rgb(12 13 18 / .94) 100%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / .16), inset 0 -2px 0 rgb(0 0 0 / .4), 0 1px 0 rgb(255 255 255 / .04), 0 12px 20px -12px rgb(0 0 0 / .9); }
.season-clock span::after { content: ''; position: absolute; left: 0; right: 0; top: 42%; height: 1px; background: rgb(0 0 0 / .35); box-shadow: 0 1px 0 rgb(255 255 255 / .05); pointer-events: none; } /* the flip clock's seam across the figure */
.season-clock b { font-family: var(--font-body); font-weight: 600; font-size: clamp(1.6rem, 2.6vw, 2.2rem); line-height: 1; letter-spacing: -.02em; color: #fff; font-variant-numeric: tabular-nums; }
.season-clock small { font-size: .62rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: rgb(255 255 255 / .55); }
@media (max-width: 899px) {
  .season { grid-template-columns: 1fr; }
  .season-art { left: 0; width: 100%; opacity: .45; }
  .season::after { background: linear-gradient(180deg, #07080c 0%, rgb(7 8 12 / .5) 45%, rgb(7 8 12 / .7) 75%, #07080c 100%); }
  .season-side { justify-items: start; }
}
@media (max-width: 479px) {
  .season-clock { gap: 6px; width: 100%; }
  .season-clock span { flex: 1; min-width: 0; padding: 12px 4px 10px; }
  .season-clock small { font-size: .56rem; letter-spacing: .08em; }
}
/* "The cost of waiting" (partials/waiting.php): what a map misses without content, line by line against what Kubx
   does about it. The "without" card is the quiet one (dim lines, a dash in front), the "with" card is a step lighter
   and its lines tick in. Line n of one list answers line n of the other: pointing at a line lights up both. */
.gap-col.is-without { background: rgb(var(--c-surface2)); box-shadow: none; }
.gap-list { display: grid; gap: 4px; margin-inline: -10px; }
.gap-list li { display: flex; align-items: flex-start; gap: 12px; padding: 9px 10px; border-radius: 10px; font-size: .98rem; line-height: 1.4; transition: background-color var(--dur-base) var(--ease-in-out), color var(--dur-base) var(--ease-in-out), opacity .45s var(--ease-out), translate .6s var(--ease-expo); }
.gap-col.is-without li { color: rgb(var(--c-ink3)); }
.gap-col.is-with li { color: rgb(var(--c-ink2)); }
.gap-list svg.ico { flex: none; width: 15px; height: 15px; margin-top: 3px; color: rgb(var(--accent-text)); }
.gap-col.is-without svg.ico { color: rgb(var(--c-err)); } /* a red cross: nothing there */
.gap-col.is-with svg.ico { width: 20px; height: 20px; margin-top: 0; padding: 4px; box-sizing: border-box; border-radius: 50%; background: #22c55e; color: #fff; box-shadow: inset 0 1px 0 rgb(255 255 255 / .3), 0 2px 4px -1px rgb(0 0 0 / .25); } /* "With Kubx": white ticks in green circles, like "We handle" (owner, 2026-10-18) */
.js .gap-col.reveal li { opacity: 0; translate: 0 8px; transition-delay: 0s, 0s, calc(var(--i, 0) * 110ms + 240ms), calc(var(--i, 0) * 110ms + 240ms); }
.js .gap-col.reveal.in li { opacity: 1; translate: 0 0; }
.js .gap-col.reveal svg.ico { scale: 0; transition: scale .5s var(--ease-overshoot) calc(var(--i, 0) * 110ms + 380ms); }
.js .gap-col.reveal.in svg.ico { scale: 1; }
@media (hover: hover) {
  .gap:has(.gap-list li:nth-child(1):hover) .gap-list li:nth-child(1), .gap:has(.gap-list li:nth-child(2):hover) .gap-list li:nth-child(2),
  .gap:has(.gap-list li:nth-child(3):hover) .gap-list li:nth-child(3), .gap:has(.gap-list li:nth-child(4):hover) .gap-list li:nth-child(4) { background: rgb(var(--c-ink) / .06); }
  .gap:has(.gap-list li:hover) .gap-col.is-without li:not(:hover) { color: rgb(var(--c-muted)); }
  .gap:has(.gap-list li:nth-child(1):hover) .is-without li:nth-child(1), .gap:has(.gap-list li:nth-child(2):hover) .is-without li:nth-child(2),
  .gap:has(.gap-list li:nth-child(3):hover) .is-without li:nth-child(3), .gap:has(.gap-list li:nth-child(4):hover) .is-without li:nth-child(4) { color: rgb(var(--c-ink)); }
}

/* hero proof line (partials/hero-live.php), above the home headline: players in our islands right now, shown once the number has arrived.
   The height is reserved, so the hero does not grow when the chip appears. */
.hero-live { min-height: 30px; }
.hero-copy > .hero-live + .h1 { margin-top: 16px; } /* the count sits where an eyebrow would, close above the headline */
.hero-live .chip { padding: 6px 12px; font-size: .78rem; background: rgb(var(--paper) / .94); color: rgb(var(--c-ink)); }
.hero-live .chip::before { background: rgb(var(--c-ok)); }
.hero-live .chip b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* video caption (video-stage.php): where it ran (platform icon + channel, a link to the post) and the map it was for */
.vslide-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.vslide-src { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
.vslide-src a { display: inline-flex; align-items: center; gap: 5px; font-weight: 600; color: rgb(var(--c-ink2)); }
.vslide-src a:hover { color: rgb(var(--c-accent)); }
.vslide-src svg { width: 13px; height: 13px; flex: none; }
.vslide-map { display: flex; align-items: flex-start; gap: 5px; }
.vslide-map svg.ico { width: 12px; height: 12px; flex: none; margin-top: 2px; color: rgb(var(--c-ok)); }
.vslide-map b { color: rgb(var(--c-ink)); }
.vslide:not(.is-active) .vslide-cap { pointer-events: none; } /* a press on a neighbour brings it to the centre, also where its hidden caption lies */

/* video cards with their own controls (video-card.php: the localized examples) */
.vcard-grid { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.vcard .media { aspect-ratio: 22 / 25; } /* the shape of the picture inside the 9:16 file (720 x 820 of 720 x 1280): cover then crops exactly the blurred bands */
@media (max-width: 479px) { .vcard-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.vcard { overflow: hidden; }
.vcard-body { padding: 14px 16px; display: flex; flex-direction: column; gap: 4px; }
.vcard-body h3 { font-size: .92rem; font-weight: 600; color: rgb(var(--c-ink)); }
.vcard-src { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; color: rgb(var(--c-ink3)); vertical-align: -2px; }
.vcard-src:hover { color: rgb(var(--c-accent)); }
.vcard-src svg { width: 14px; height: 14px; }

/* before / after block of a case study (case-compare.php): two plain figures side by side with a hairline between
   them, each with its own label. No boxes, no arrow, no bar, no multiple: the two can be different measures. */
.compare { display: grid; grid-template-columns: repeat(2, minmax(0, auto)); justify-content: start; margin-top: 30px; } /* auto columns: on a narrow card the labels wrap, the pair stays on one line */
.compare-side { display: flex; flex-direction: column; gap: 8px; padding-right: clamp(24px, 4vw, 44px); }
.compare-side + .compare-side { padding-left: clamp(24px, 4vw, 44px); padding-right: 0; border-left: 1px solid rgb(var(--c-line)); }
.compare-tag { font-family: var(--font-mono); font-size: .68rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.compare-side.is-after .compare-tag { color: rgb(var(--c-accent)); }
.compare-val { font-family: var(--font-display); font-weight: 800; font-size: clamp(2.1rem, 4vw, 3rem); line-height: .95; letter-spacing: .01em; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
.compare-side:not(.is-after) .compare-val { color: rgb(var(--c-muted)); }
.compare-label { font-size: .82rem; line-height: 1.35; color: rgb(var(--c-ink3)); max-width: 13em; }
/* the note under a before / after pair (how long, what was done): a notification bubble a shade lighter than the card */
.compare-flag { display: inline-flex; align-items: center; gap: 11px; align-self: flex-start; margin-top: 22px; padding: 10px 16px 10px 11px; border-radius: 999px; background: rgb(var(--c-ink) / .07); font-size: .84rem; font-weight: 500; line-height: 1.35; color: rgb(var(--c-ink)); }
.compare-flag::before { content: 'i'; flex: none; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-ink) / .14); font-family: var(--font-mono); font-size: .72rem; font-weight: 600; color: rgb(var(--c-ink)); }
@media (max-width: 479px) { .compare-flag { border-radius: 16px; } } /* it wraps to two lines on a phone */
.js .reveal .compare-flag { opacity: 0; translate: 0 8px; scale: .96; transition: opacity .45s var(--ease-out) .45s, translate .6s var(--ease-expo) .45s, scale .6s var(--ease-overshoot) .45s; } /* pops in after its card */
.js .reveal.in .compare-flag { opacity: 1; translate: 0 0; scale: 1; }
/* the button of the featured case card (the whole card is the link) */
.case-cta { align-self: flex-start; margin-top: 26px; }
.case-wide:hover .case-cta { transform: translateY(-1px); }
.case-wide:hover .case-cta svg.arrow { transform: translateX(3px); }
/* the same two figures on a case card: set apart from the summary by space and size (figures in the heading weight,
   what they count small under them), so the eye finds the numbers before the sentence */
.compare-mini { display: flex; align-items: flex-start; gap: 14px; margin-top: 18px; }
.compare-mini > span { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.compare-mini b { font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; line-height: 1; color: rgb(var(--c-muted)); font-variant-numeric: tabular-nums; }
.compare-mini .is-after b { color: rgb(var(--c-ink)); }
.compare-mini small { font-size: .72rem; line-height: 1.3; color: rgb(var(--c-muted)); }
.compare-mini svg.ico { flex: none; width: 15px; height: 15px; margin-top: 6px; color: rgb(var(--c-ink3)); }
@media (max-width: 379px) { /* very narrow: one under the other, the hairline turns */
  .compare { grid-template-columns: 1fr; }
  .compare-side + .compare-side { padding: 20px 0 0; border-left: 0; border-top: 1px solid rgb(var(--c-line)); }
}

/* who does what, under the workflow pipeline (workflow.php) */
.scope-list { display: grid; gap: 12px; }
.scope-list li { display: flex; align-items: flex-start; gap: 10px; font-size: .95rem; line-height: 1.4; color: rgb(var(--c-ink2)); }
.scope-list svg.ico { flex: none; width: 16px; height: 16px; margin-top: 3px; color: rgb(var(--c-accent)); }

/* "What we do" on Game Development (partials/services.php, owner 2026-10-25 after a reference mock-up): the heading
   centred, then the service cards side by side, each its icon on a small neutral tile, the title, the description and
   its points with ticks. The highlighted one (the popular service, in the middle) is the accent fill with white text,
   its tag in the corner; no glow, no outline, the plain card shadow. Cards stack on phones. */
.services-head { max-width: 42rem; margin-inline: auto; text-align: center; }
.services-head .lead { margin-inline: auto; }
.services-grid { display: grid; grid-template-columns: repeat(min(var(--n, 3), 3), minmax(0, 1fr)); gap: 16px; margin-top: clamp(32px, 4vw, 52px); }
.service-card { position: relative; display: flex; flex-direction: column; gap: 12px; padding: clamp(22px, 2.4vw, 32px); min-width: 0; }
.service-ico { display: grid; place-items: center; width: 46px; height: 46px; border-radius: 13px; background: rgb(var(--c-ink) / .06); color: rgb(var(--accent-text)); }
.service-ico svg.ico { width: 20px; height: 20px; }
.service-title { margin-top: 8px; }
.service-desc { font-size: .92rem; line-height: 1.55; color: rgb(var(--c-muted)); }
.service-points { display: grid; gap: 11px; margin: 8px 0 0; padding: 0; list-style: none; }
.service-points li { display: flex; align-items: flex-start; gap: 10px; font-size: .9rem; line-height: 1.4; color: rgb(var(--c-ink2)); }
.service-points svg.ico { flex: none; width: 15px; height: 15px; margin-top: 2px; color: rgb(var(--accent-text)); }
.service-badge { position: absolute; top: 18px; right: 18px; padding: 5px 10px; border-radius: 999px; font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; background: rgb(var(--c-ink) / .07); color: rgb(var(--c-ink2)); }
.service-card.is-hot { background: rgb(var(--c-accent)); color: #fff; box-shadow: inset 0 1px 0 rgb(255 255 255 / .18), 0 18px 34px -24px rgb(0 0 0 / .6); }
.service-card.is-hot :is(.service-title, .service-points li) { color: #fff; }
.service-card.is-hot .service-desc { color: rgb(255 255 255 / .82); }
.service-card.is-hot :is(.service-ico, .service-badge) { background: rgb(255 255 255 / .16); color: #fff; }
.service-card.is-hot .service-points svg.ico { color: #fff; }
@media (max-width: 900px) { .services-grid { grid-template-columns: minmax(0, 1fr); max-width: 520px; margin-inline: auto; } }

/* "Switching to Kubx" on Social Media (partials/switching.php; owner 2026-10-25). A dark panel (.cinema) in both
   themes: the heading left, the line right, then the steps as cards a step lighter than the panel, white titles. Each
   card's scene shows its own sentence (inbox, same look, watched to the end) and plays once when the row comes into
   view, card after card (--i), then stays finished: no loop, no self-advancing (the owner found that clunky). One easing
   for everything (--sw-ease), transform and opacity only, finished at once in lite and under reduced motion. */
.switching { --sw-ease: cubic-bezier(.16, 1, .3, 1); --sw-ok: #4ade80; }
.switching-top { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); align-items: end; gap: 16px clamp(24px, 5vw, 72px); }
.switching .switching-lead { margin: 0; max-width: 34rem; font-size: clamp(1rem, 1.3vw, 1.12rem); line-height: 1.6; color: rgb(255 255 255 / .72); }
.switching .eyebrow { color: rgb(150 170 255); }
.switching .display .hl { color: rgb(150 170 255); }
.switching-steps { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); gap: 16px; margin: clamp(30px, 4vw, 46px) 0 0; padding: 0; list-style: none; }
.switching-step { --d: calc(var(--i) * .45s); display: flex; flex-direction: column; overflow: hidden; border-radius: var(--r-card); background: #1a1a1d; opacity: 0; transform: translateY(16px); transition: opacity .8s var(--sw-ease) var(--d), transform .8s var(--sw-ease) var(--d); }
.switching-steps.in .switching-step { opacity: 1; transform: none; }
.switching-copy { display: flex; flex-direction: column; gap: 8px; padding: 18px clamp(18px, 2vw, 24px) clamp(20px, 2.2vw, 26px); }
.switching .switching-step .h3 { color: #fff; }
.switching-text { font-size: .92rem; line-height: 1.55; color: rgb(255 255 255 / .68); }
.switching-num { display: grid; place-items: center; width: 34px; height: 34px; margin: -35px 0 6px; border-radius: 11px; background: rgb(var(--c-accent)); color: #fff; font-size: .82rem; font-weight: 700; font-variant-numeric: tabular-nums; box-shadow: 0 6px 14px -8px rgb(0 0 0 / .8); position: relative; }
/* the scene: a dark screen at the top of the card; its parts start a little after their card */
.sw-scene { position: relative; display: block; height: 208px; overflow: hidden; background: #0c0c0e; color: #fff; }
.sw-scene img { display: block; width: 100%; height: 100%; object-fit: cover; }
.sw-anim { opacity: 0; transition: opacity .6s var(--sw-ease), transform .8s var(--sw-ease); }
.switching-step.is-in .sw-anim { opacity: 1; transform: none; }
/* 1: your past videos land in the inbox, each with a tick */
.sw-inbox { display: grid; align-content: center; justify-items: center; gap: 14px; padding: 0 18px; }
.sw-inbox-head { display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px; border-radius: 999px; background: rgb(255 255 255 / .08); font-size: .74rem; font-weight: 700; color: rgb(255 255 255 / .88); }
.sw-inbox-head svg.ico { width: 14px; height: 14px; color: rgb(160 178 255); }
.sw-files { display: flex; gap: 12px; }
.sw-file { position: relative; width: 58px; aspect-ratio: 9 / 16; border-radius: 10px; overflow: visible; background: #2a2a2e; opacity: 0; transform: translateY(-26px) scale(.9); transition: opacity .5s var(--sw-ease), transform .8s var(--sw-ease); transition-delay: calc(var(--d) + .35s + var(--n) * .22s); }
.sw-file img { border-radius: inherit; }
.switching-step.is-in .sw-file { opacity: 1; transform: none; }
.sw-tick { position: absolute; right: -6px; bottom: -6px; display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--sw-ok); color: #04140a; box-shadow: 0 0 0 3px #0c0c0e; transform: scale(0); transition: transform .45s cubic-bezier(.3, 1.6, .5, 1); transition-delay: calc(var(--d) + .85s + var(--n) * .22s); }
.sw-tick svg.ico { width: 12px; height: 12px; stroke: currentColor; stroke-width: .6px; }
.switching-step.is-in .sw-tick { transform: none; }
/* 2: your video and the Kubx edit side by side, the same picture and caption, a tick between them */
.sw-match { display: flex; align-items: center; justify-content: center; gap: 16px; }
.sw-phone { display: grid; justify-items: center; gap: 8px; opacity: 0; transition: opacity .6s var(--sw-ease), transform .8s var(--sw-ease); transition-delay: calc(var(--d) + .35s); }
.sw-phone.is-yours { transform: translateX(14px); } .sw-phone.is-ours { transform: translateX(-14px); }
.switching-step.is-in .sw-phone { opacity: 1; transform: none; }
.sw-phone em { font-style: normal; font-size: .64rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: rgb(255 255 255 / .6); }
.sw-phone.is-ours em { color: rgb(170 186 255); }
.sw-screen { position: relative; display: block; width: 76px; aspect-ratio: 9 / 16; border-radius: 12px; overflow: hidden; background: #2a2a2e; box-shadow: 0 0 0 3px #26262a; }
.sw-phone.is-ours .sw-screen { box-shadow: 0 0 0 3px rgb(var(--c-accent)); }
.sw-same { display: grid; place-items: center; width: 32px; height: 32px; margin-bottom: 22px; border-radius: 50%; background: var(--sw-ok); color: #04140a; transform: scale(0); transition: transform .5s cubic-bezier(.3, 1.6, .5, 1); transition-delay: calc(var(--d) + 1.15s); }
.sw-same svg.ico { width: 16px; height: 16px; stroke: currentColor; stroke-width: .6px; }
.switching-step.is-in .sw-same { transform: none; }
/* 3: watched to the end, before and with Kubx */
.sw-watch { display: grid; align-content: center; gap: 14px; padding: 0 clamp(18px, 2vw, 26px); }
.sw-watch-head { font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: rgb(255 255 255 / .55); }
.sw-row { display: grid; grid-template-columns: 70px minmax(0, 1fr); align-items: center; gap: 12px; }
.sw-row em { font-style: normal; font-size: .76rem; font-weight: 700; color: rgb(255 255 255 / .75); }
.sw-row.is-after em { color: #fff; }
.sw-track { position: relative; height: 14px; border-radius: 8px; background: rgb(255 255 255 / .07); overflow: hidden; }
.sw-track i { position: absolute; left: 0; top: 0; bottom: 0; border-radius: inherit; transform: scaleX(0); transform-origin: left; transition: transform 1.2s var(--sw-ease); }
.sw-row.is-before i { width: 46%; background: rgb(255 255 255 / .32); transition-delay: calc(var(--d) + .4s); }
.sw-row.is-after i { width: 88%; background: rgb(var(--c-accent)); transition-delay: calc(var(--d) + .75s); transition-duration: 1.5s; }
.switching-step.is-in .sw-track i { transform: none; }
@media (max-width: 900px) {
  .switching-top { grid-template-columns: minmax(0, 1fr); }
  .switching-steps { grid-template-columns: minmax(0, 1fr); } /* full width (a 520px column pinned to the left looked broken on tablets; owner 2026-10-25) */
  .switching-step { --d: 0s; }
}
/* tablets: each card one row, its scene on the left beside the number, title and line */
@media (min-width: 601px) and (max-width: 900px) {
  .switching-step { flex-direction: row; align-items: stretch; }
  .switching-step > .sw-scene { flex: 0 0 46%; height: auto; min-height: 200px; }
  .switching-step > .switching-copy { flex: 1 1 auto; justify-content: center; padding: 22px 26px; }
  .switching-step .switching-num { margin: 0 0 6px; }
}
/* weak devices and reduced motion: everything stands finished at once */
/* the switching scenes run in lite too: one-off fades and slides, transform and opacity only (owner, 2026-10-25: they did
   not show when switching cards; lite had stood them finished) */
@media (prefers-reduced-motion: reduce) { .switching :is(.switching-step, .sw-file, .sw-tick, .sw-phone, .sw-same, .sw-track i) { transition: none; opacity: 1; transform: none; } }

/* ---------- phones: the switch in the top bar, bigger targets, tighter rhythm ---------- */
@media (hover: none) { .carousel-body > .carousel-btn { display: none; } } /* touch screens swipe the track; the arrows only covered the cards */
/* Touch: every control is a finger-sized target (about 44px) without looking any bigger. Buttons, the logo and the
   footer links get real height; text links, the channel links under a video, the island-code chip and the sound
   button keep their size and grow an invisible hit area around them (::after). */
@media (pointer: coarse) {
  .btn { min-height: 46px; } .btn-sm { min-height: 40px; }
  .brand { min-height: 44px; }
  .footer-links { row-gap: 0; } .footer-links a { padding-block: 11px; }
  .link, .vslide-src a, .vcard-src, .chip-copy, .vstage-sound-btn, .footer-legal-links a { position: relative; }
  .link::after, .vcard-src::after, .chip-copy::after, .vstage-sound-btn::after, .footer-legal-links a::after { content: ''; position: absolute; inset: -11px -8px; }
  .vslide-src a::after { content: ''; position: absolute; inset: -15px -8px; }
}
@media (max-width: 720px) {
  /* The division switch stays in the top bar: the logo on the left, the switch on the right as a compact pill toggle
     with short labels and no icons (owner, 2026-10-30, after reachclipping.com on a phone; it used to be a tab bar
     along the bottom edge). "Work with us" leaves the bar, the hero's "Send a message" is right under it. The bar is
     near solid instead of blurred, cheaper on a phone. */
  .topbar-inner { grid-template-columns: minmax(0, 1fr) auto; padding: 0 6px 0 14px; -webkit-backdrop-filter: none; backdrop-filter: none; background: rgb(var(--c-bg) / .97); }
  .topbar .switch { justify-self: end; }
  .topbar .brand { justify-self: start; min-width: 0; }
  .topbar .brand-logo-img { max-width: 36vw; }
  .topbar-cta { display: none; }
  .topbar .switch-item { padding: 9px 14px; }
  .topbar .switch-item .ico { display: none; }
  .topbar .switch-item::after { content: ''; position: absolute; inset: -8px 0; } /* a 50px finger target in the 58px bar */
  .kx-toasts { left: 12px; right: 12px; max-width: none; }
}
@media (max-width: 600px) {
  .section { padding-block: 36px; } .section-tight { padding-block: 22px; }
  .div-card { min-height: 390px; } .div-card .bg { height: 42%; } /* shorter picture: the copy must not run over it */
  .chart-box { height: 240px; }
  .hero-actions .btn { flex: 1 1 auto; }
  /* division heroes: the picture becomes a banner across the top and the copy starts below it, instead of text on top of the art */
  .hero-media:not(.is-cover) { inset: 0 0 auto; height: 46vw; opacity: 1; -webkit-mask-image: linear-gradient(to bottom, #000 35%, transparent); mask-image: linear-gradient(to bottom, #000 35%, transparent); }
  .hero:not(.hero-cover) .hero-copy { padding-top: 38vw; }
  .hero-media.is-cover::after { background: rgb(var(--c-dark) / .6); } /* the copy sits on the whole video here, not beside it */
  /* case study page: its three figures stay side by side (stacked they filled a whole screen), and three videos
     become a row to swipe instead of two and one left over */
  .stats-row.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
  html[data-division] .stats-row.grid-3 .stat-val { font-size: clamp(1.5rem, 8vw, 2.2rem); }
  .stats-row.grid-3 .stat-label { font-size: .78rem; line-height: 1.35; }
  .vcard-row:has(> :nth-child(3)) { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; margin-inline: calc(clamp(16px, 4vw, 48px) * -1); padding: 0 clamp(16px, 4vw, 48px) 14px; scroll-padding-inline: clamp(16px, 4vw, 48px); }
  .vcard-row:has(> :nth-child(3))::-webkit-scrollbar { display: none; }
  .vcard-row:has(> :nth-child(3)) > * { flex: 0 0 62%; scroll-snap-align: start; }
  .compare-tag, .logo-tile-type { font-size: .6875rem; } /* 11px: the smallest a label gets on a phone */
}
/* phones: the two hero buttons sit one above the other, full width, instead of squeezing into one row until they
   no longer fit. The width is set, not left to the column's stretch: once on an iPhone (owner, 2026-10-29) "See the
   work" stayed at its own width on the left under a full-width "Send a message", a stretch Safari did not redo after
   the hero copy (a size container) had first been laid out narrower; a percentage width is worked out on every layout */
@media (max-width: 480px) { .hero-actions { flex-direction: column; } .hero-actions .btn { flex: none; width: 100%; } }

/* ---------- arriving from the admin (?spot=, site.js): the section that was asked for pulses once ----------
   The one ring on the public site, on purpose: it only ever shows to someone who clicked "See it on the site". */
.spot-pulse { position: relative; z-index: 1; border-radius: var(--r-sheet); animation: spot-pulse 1.5s var(--ease-out) 1; }
@keyframes spot-pulse {
  0% { box-shadow: 0 0 0 0 rgb(var(--c-accent) / 0), 0 0 0 0 rgb(var(--c-accent) / 0); }
  25% { box-shadow: 0 0 0 3px rgb(var(--c-accent) / .95), 0 0 0 12px rgb(var(--c-accent) / .22); }
  100% { box-shadow: 0 0 0 3px rgb(var(--c-accent) / 0), 0 0 0 30px rgb(var(--c-accent) / 0); }
}

/* ---------- reveal + page transitions (kree8: opacity 0 / translateY(20px) → in) ---------- */
.js .reveal { opacity: 0; translate: 0 20px; transition: opacity .6s var(--ease-out), translate .7s var(--ease-expo), transform var(--dur-base) var(--ease-in-out), box-shadow var(--dur-base) var(--ease-in-out); } /* translate, not transform: keeps the .card-hover lift working */
.js .reveal.in { opacity: 1; translate: 0 0; }
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out .24s var(--ease-in-out) both; }
::view-transition-new(root) { animation: vt-in .45s var(--ease-expo) both; }
@keyframes vt-out { to { opacity: 0; transform: translateY(-8px); } }
@keyframes vt-in { from { opacity: 0; transform: translateY(14px); } }

/* ---------- dark theme: components ----------
   Colours the tokens alone do not cover, for the dark pages only (every page but Social Media: Game Development,
   the legal pages when Game Development is the start page, the admin). On the light page the plain rules further up apply, plus the block
   "light theme: Social Media" below. */
html:not([data-theme="light"]) .switch-thumb { box-shadow: 0 1px 3px rgb(0 0 0 / .45); }
/* the top bar on dark: a faint light top edge and a wide, soft shadow instead of the sheet's tight dark one */
html:not([data-theme="light"]) .topbar-inner { box-shadow: inset 0 1px 0 rgb(255 255 255 / .05), 0 14px 36px -18px rgb(0 0 0 / .7); }
/* inputs are wells, a shade below the card they sit on */
html:not([data-theme="light"]) .field, html:not([data-theme="light"]) .num { background: rgb(var(--c-surface2)); }
html:not([data-theme="light"]) .field::placeholder { color: rgb(var(--c-muted) / .8); }
/* panels: the video room is the darkest thing on the page; the call to action is the lightest step (plain paper)
   instead of a dark band, since everything around it is dark already. No tint: the button carries the colour. */
html:not([data-theme="light"]) .cta-band { background: rgb(var(--paper)); box-shadow: 0 18px 34px -22px rgb(0 0 0 / .9); }
/* icon tones as text and outlines on dark */
html:not([data-theme="light"]) .ico-tone-red { --ico: 251 113 133; } html:not([data-theme="light"]) .ico-tone-amber { --ico: 251 191 36; } html:not([data-theme="light"]) .ico-tone-gold { --ico: 250 204 21; } html:not([data-theme="light"]) .ico-tone-orange { --ico: 251 146 60; }
html:not([data-theme="light"]) .ico-tone-blue { --ico: 96 165 250; } html:not([data-theme="light"]) .ico-tone-violet { --ico: 167 139 250; } html:not([data-theme="light"]) .ico-tone-pink { --ico: 244 114 182; } html:not([data-theme="light"]) .ico-tone-green { --ico: 74 222 128; } html:not([data-theme="light"]) .ico-tone-teal { --ico: 45 212 191; }
/* the trophy on the rank chip (the neutral chip itself is shared, below), a step brighter on dark */
html:not([data-theme="light"]) .rank-legendary { --rank: 245 158 11; } html:not([data-theme="light"]) .rank-epic { --rank: 167 139 250; } html:not([data-theme="light"]) .rank-rare { --rank: 96 165 250; } html:not([data-theme="light"]) .rank-uncommon { --rank: 74 222 128; }
/* brand colours that are black: white on dark */
html:not([data-theme="light"]) .is-tiktok, html:not([data-theme="light"]) .is-x { --brand: #fff; }

/* ---------- both themes: dropdowns, monochrome modules, no glows, no outlines ----------
   Design rules (owner, 2026-10-07) that hold on the light page as much as on the dark ones. html[data-division] is
   on every page, in either theme, and weighs the same as the dark prefix html:not([data-theme="light"]), so these
   rules still beat the older plain rules they replace. Colours in them come from the tokens; where a light value
   differs it is set in "light theme: Social Media". */
/* Dropdowns (select.field). Closed: a field like the others with a chevron inside its right edge. Open: where the
   browser can draw the list itself (appearance: base-select, Chromium 135+) it is our own menu: a floating panel a
   step above the card, rounded rows, the current choice ticked in the accent, a short fade and slide, and the chevron
   turns over. Elsewhere the native list opens (dark, through color-scheme) under the same field and chevron.
   The icons are the Nucleo fill shapes used by icon() (chevron, check), as masks so they take a text colour. */
html[data-division] select.field { --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath transform='rotate(90 9 9)' fill='%23000' d='M13.28,8.47L7.03,2.22c-.293-.293-.768-.293-1.061,0s-.293,.768,0,1.061l5.72,5.72-5.72,5.72c-.293,.293-.293,.768,0,1.061,.146,.146,.338,.22,.53,.22s.384-.073,.53-.22l6.25-6.25c.293-.293,.293-.768,0-1.061Z'/%3E%3C/svg%3E"); --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath d='M6.75,15h-.002c-.227,0-.442-.104-.583-.281L2.165,9.719c-.259-.324-.207-.795,.117-1.054,.325-.259,.796-.206,1.054,.117l3.418,4.272L14.667,3.278c.261-.322,.732-.373,1.055-.111,.322,.261,.372,.733,.111,1.055L7.333,14.722c-.143,.176-.357,.278-.583,.278Z'/%3E%3C/svg%3E");
  appearance: none; cursor: pointer; padding-right: 42px; background-color: rgb(var(--c-surface2)); background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18'%3E%3Cpath transform='rotate(90 9 9)' fill='%238a8a8a' d='M13.28,8.47L7.03,2.22c-.293-.293-.768-.293-1.061,0s-.293,.768,0,1.061l5.72,5.72-5.72,5.72c-.293,.293-.293,.768,0,1.061,.146,.146,.338,.22,.53,.22s.384-.073,.53-.22l6.25-6.25c.293-.293,.293-.768,0-1.061Z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; background-size: 14px 14px; }
@supports (appearance: base-select) {
  html[data-division] select.field, html[data-division] select.field::picker(select) { appearance: base-select; }
  html[data-division] select.field { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-right: 14px; background-image: none; }
  html[data-division] select.field::picker-icon { content: ''; flex: none; width: 14px; height: 14px; background: rgb(var(--c-muted)); mask: var(--chev) center / contain no-repeat; transition: rotate .35s var(--ease-expo), background-color var(--dur-quick); }
  html[data-division] select.field:hover::picker-icon, html[data-division] select.field:open::picker-icon { background: rgb(var(--c-ink)); }
  html[data-division] select.field:open::picker-icon { rotate: 180deg; }
  html[data-division] select.field:open { border-color: rgb(var(--c-accent)); box-shadow: 0 0 0 4px rgb(var(--c-accent) / .15); }
  html[data-division] select.field::picker(select) { position-try-order: normal; /* below the field, above only when it does not fit */ max-height: min(46vh, 380px); overflow: auto; scrollbar-width: thin; margin: 6px 0; padding: 6px; border: 0; border-radius: 14px; background: rgb(var(--pop)); color: rgb(var(--c-ink2)); box-shadow: 0 18px 34px -12px rgb(0 0 0 / .8), 0 2px 6px rgb(0 0 0 / .35);
    opacity: 0; translate: 0 -6px; transition: opacity .16s var(--ease-out), translate .3s var(--ease-expo), overlay .3s allow-discrete, display .3s allow-discrete; }
  html[data-division] select.field:open::picker(select) { opacity: 1; translate: 0 0; }
  @starting-style { html[data-division] select.field:open::picker(select) { opacity: 0; translate: 0 -6px; } }
  html[data-division] select.field option { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 12px; border-radius: 9px; font-size: .95rem; cursor: pointer; outline: none; transition: background-color var(--dur-quick), color var(--dur-quick); }
  html[data-division] select.field option + option { margin-top: 2px; }
  html[data-division] select.field option:hover, html[data-division] select.field option:focus-visible { background: rgb(var(--c-ink) / .08); color: rgb(var(--c-ink)); }
  html[data-division] select.field option:checked { background: rgb(var(--c-ink) / .05); color: rgb(var(--c-ink)); font-weight: 500; }
  html[data-division] select.field option:checked:hover, html[data-division] select.field option:checked:focus-visible { background: rgb(var(--c-ink) / .1); }
  html[data-division] select.field option::checkmark { content: ''; order: 1; flex: none; width: 14px; height: 14px; background: rgb(var(--c-accent)); mask: var(--tick) center / contain no-repeat; }
  @media (prefers-reduced-motion: reduce) { html[data-division] select.field::picker(select), html[data-division] select.field::picker-icon { transition: none; } }
}
/* rank chips: a neutral chip like every other one; the rarity colour is on the trophy only */
html[data-division] .chip-rank { --rank: var(--c-ink3); background: rgb(var(--paper) / .92); color: rgb(var(--c-ink)); }
html[data-division] .chip-rank svg.ico { color: rgb(var(--rank)); opacity: 1; }
/* Monochrome modules (asked for by the owner): no module is filled or lit with a hue. A state is shown by a step in
   brightness and by its icon or label colour, not by tinting the box or putting a coloured glow under it. */
html[data-division] .cinema .vslide.is-active .media { box-shadow: 0 50px 90px -30px rgb(0 0 0 / .9); } /* no accent halo, no hairline ring: the glow behind it comes from the video itself */
/* No glows (asked for by the owner): the shapes are sharp, so nothing has a coloured halo or light behind it. What is
   left under a component is a plain dark drop shadow, or nothing. */
html[data-division] .btn-aura, html[data-division] .btn-aura:hover { box-shadow: 0 2px 6px rgb(0 0 0 / .45); } /* the liquid-metal fill stays, the accent glow under it goes */
html[data-division] .stat-ico svg:not(.ico-glint) { filter: none; } /* no coloured shadow behind icons */
html[data-division] .chip-rank { box-shadow: none; }
/* No outlines on modules (asked for by the owner): a card, panel, chip, pill or tile is told apart by its fill and
   its shadow, never by a line around it. Only inputs (.field) keep a border, and :focus-visible keeps its ring.
   The borders are made transparent instead of removed, so nothing changes size. */
html[data-division] .card::before, html[data-division] .beam::after { content: none; } /* the gradient edge on cards and the light travelling around it */
html[data-division] :is(.topbar-inner, .switch, .switch-thumb, .btn-icon, .btn-ghost, .tile, .chip, .carousel-btn, .avatar, .client-badge, .logo-tile, .social-btn, .alert, .kx-toast),
html[data-division] .hero-cover .btn-solid { border-color: transparent; }

/* ---------- public site: type and composition (headings, labels and buttons since replaced: see the teamhive block below) ----------
   Modelled on large product studios (x.ai, X Money, Giga, V7, Topaz, National Design Studio): one neutral grotesque
   (Geist) in sentence case at medium weight with tight tracking; big quiet headings whose second sentence drops to
   grey (.dim); small mono labels; numbers set large and plain, without boxes or icons; compact pills; a lot of air.
   Nothing shouts: no uppercase display type, no extra-bold, no coloured words in headings. */
html[data-division] body { font-weight: 400; letter-spacing: -.006em; }
html[data-division] .display { font-weight: 500; text-transform: none; letter-spacing: -.035em; line-height: 1.05; }
html[data-division] .h1 { font-size: clamp(2.5rem, 5.4vw, 4.25rem); letter-spacing: -.045em; line-height: 1; }
html[data-division] .h2 { font-size: clamp(1.7rem, 3vw, 2.5rem); letter-spacing: -.035em; line-height: 1.08; }
html[data-division] .h3 { font-weight: 500; text-transform: none; font-size: 1.05rem; letter-spacing: -.015em; line-height: 1.25; }
html[data-division] .display { text-wrap: balance; }
html[data-division] .h2 { max-width: 26em; }
html[data-division] .center .h2 { margin-inline: auto; }
/* a heading with a link beside it: the heading takes the room, the link stays on its line at the right */
html[data-division] .row-between > div:first-child { flex: 1 1 0; min-width: min(100%, 18rem); }
html[data-division] .row-between > .link { flex: none; }
html[data-division] .display .accent { color: inherit; } /* headings are one colour; emphasis comes from .dim on the rest */
html[data-division] .dim { color: rgb(var(--c-ink) / .42); }
html[data-division] .display .hot { color: rgb(var(--c-accent)); } /* the one coloured line a heading may have: the ask in the contact section */
html[data-division] .lead { font-weight: 400; color: rgb(var(--c-ink3)); line-height: 1.5; letter-spacing: -.012em; }
html[data-division] .hero-cover .lead { color: rgb(255 255 255 / .8); }
/* labels: small mono caps with a plain square in front (one neutral marker, not one colour per section) */
html[data-division] :where(.eyebrow) { font-family: var(--font-mono); font-size: .7rem; font-weight: 500; letter-spacing: .09em; color: rgb(var(--c-muted)); }
html[data-division] .div-card[class*="tone-"] .eyebrow { color: rgb(var(--c-muted)); }
html[data-division] .toned > .eyebrow::before, html[data-division] .toned > .row-between > div > .eyebrow::before, html[data-division] .toned > .wrap > .eyebrow::before { width: 5px; height: 5px; border-radius: 1px; rotate: 0deg; background: rgb(var(--c-ink3)); }
html[data-division] :is(.chip, .link, .label) { font-weight: 500; }
/* numbers: large, plain, medium weight */
html[data-division] :is(.stat-val, .kv dd, .live-strip-num, .case-wide-metrics dd, .compare-val, .compare-mini b) { font-weight: 500; letter-spacing: -.035em; }
html[data-division] .avatar-ini { font-weight: 500; }
/* headline numbers (partials/stats.php, case study metrics): a row of figures, no tiles */
html[data-division] .stats-row { gap: clamp(24px, 4vw, 56px); }
/* /privacy and /terms: one readable column of plain text, headings in the site's sentence case */
html[data-division] .legal .display { font-size: clamp(2rem, 4.5vw, 3.2rem); }
html[data-division] .legal-body { max-width: 44rem; }
html[data-division] .legal-body h2 { margin-top: 44px; font-size: 1.3rem; font-weight: 500; letter-spacing: -.02em; color: rgb(var(--c-ink)); }
html[data-division] .legal-body h3 { margin-top: 26px; font-size: 1rem; font-weight: 500; color: rgb(var(--c-ink)); }
html[data-division] .legal-body p, html[data-division] .legal-body li { margin-top: 12px; font-size: .97rem; line-height: 1.65; color: rgb(var(--c-ink3)); }
html[data-division] .legal-body ul { padding-left: 1.2em; list-style: disc; }
html[data-division] .legal-body li { margin-top: 8px; }
html[data-division] .legal-body b { font-weight: 500; color: rgb(var(--c-ink)); }
html[data-division] .legal-body code { font-family: var(--font-mono); font-size: .88em; }
html[data-division] .legal-body .lead { margin-top: 0; }
.footer-legal-links { display: inline-flex; gap: 14px; margin-left: 10px; }
.footer-legal-links a { color: rgb(var(--c-ink3)); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgb(var(--c-ink3) / .4); }
.footer-legal-links a:hover { opacity: .7; }
html[data-division] .metrics-asof { margin-top: 14px; } /* "Figures from <map> as of <day>" under a case study's figures taken from the map */
html[data-division] .stat { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; padding: 0; background: none; box-shadow: none; }
html[data-division] .stat-val { font-size: clamp(2.2rem, 4.4vw, 3.4rem); line-height: 1; }
html[data-division] .stat-label { font-size: .86rem; color: rgb(var(--c-ink3)); }
/* buttons: compact pills */
html[data-division] .btn { padding: 12px 20px; font-size: .9rem; letter-spacing: -.01em; }
html[data-division] .btn-sm { padding: 9px 15px; font-size: .84rem; }
@media (max-width: 820px) { html[data-division] .topbar-cta .btn { padding: 9px 14px; font-size: .84rem; } }
/* layout: a wider sheet, more room between sections and at the sides */
html[data-division] .sheet, html[data-division] .topbar-inner { max-width: 1200px; }
html[data-division] .section { padding-block: clamp(40px, 9vw, 120px); }
html[data-division] .wrap { padding-inline: clamp(16px, 4.6vw, 64px); }
@media (min-width: 721px) {
  html[data-division] .topbar .switch { background: transparent; } /* the nav is plain text with one raised pill for the open page */
}
html[data-division] .switch-item { font-size: .86rem; font-weight: 400; }
html[data-division] .switch-item.is-on { font-weight: 500; }
/* the two "why" cards: the picture ends above the copy instead of running under the label */
html[data-division] .div-card { min-height: 460px; }
html[data-division] .div-card .bg { height: 50%; }
html[data-division] .div-card .bg img { -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 40%, transparent 96%); mask-image: linear-gradient(to bottom, #000 0%, #000 40%, transparent 96%); }
@media (max-width: 600px) { html[data-division] .div-card { min-height: 400px; } html[data-division] .div-card .bg { height: 40%; } }
html[data-division] .avatar-ini { background: rgb(var(--c-dark2)); } /* initials on a grey disc, not on the accent */
html[data-division] .scope-list svg.ico { color: rgb(var(--c-ink3)); }

/* ---------- light theme: Social Media ----------
   html[data-theme="light"]: the layout sets it on division social (the Social Media page and its case studies; owner,
   2026-10-18: it used to be Game Development) and kx.router copies it on a swap. The colours are the :root tokens
   (neutral greys and the blue accent); the design is the one of the dark pages (fonts in :root, the "both themes" and "type and composition" blocks). Here:
   the light values those shared rules need, and the components that only had a dark rule. The same rules hold as on
   the dark pages: no glows, no outlines on modules, panels neutral, colours from the role tokens. */
/* dropdowns: a field like the others (white), its list a white menu on a soft shadow */
html[data-theme="light"] select.field { background-color: rgb(var(--paper)); }
@supports (appearance: base-select) {
  html[data-theme="light"] select.field::picker(select) { box-shadow: 0 18px 34px -14px rgb(var(--c-dark) / .28), 0 2px 6px rgb(var(--c-dark) / .1); }
}
/* the trophy on the neutral rank chip, in the light rarity colours */
html[data-theme="light"] .rank-legendary { --rank: 217 119 6; } html[data-theme="light"] .rank-epic { --rank: var(--h-violet); }
html[data-theme="light"] .rank-rare { --rank: var(--h-blue); } html[data-theme="light"] .rank-uncommon { --rank: var(--h-green); }
/* no glows: the plain drop shadow under the liquid-metal button, softer on white */
html[data-theme="light"] .btn-aura, html[data-theme="light"] .btn-aura:hover { box-shadow: 0 2px 6px rgb(var(--c-dark) / .22); }
/* the call to action: a plain white panel (--paper) like on the dark pages, where it is the lightest step, instead of
   the slate ink band of the old light theme (a tinted panel); its text takes the ink colours, the button the colour */
html[data-theme="light"] .cta-band { background: rgb(var(--paper)); color: rgb(var(--c-ink2)); box-shadow: var(--sh-card), 0 18px 34px -24px rgb(var(--c-dark) / .3); }
html[data-theme="light"] .cta-band .display { color: rgb(var(--c-ink)); }
html[data-theme="light"] .cta-band-points { color: rgb(var(--c-ink3)); }
html[data-theme="light"] .cta-band-points svg.ico { color: rgb(var(--c-accent)); }

/* ---------- public site: headings, labels and buttons (teamhive.org look, owner 2026-10-18) ----------
   Taken from teamhive.org: the type (Panchang, uppercase, for the page and section headlines only, in semibold:
   lighter than Hive's extra bold; Inter for everything else, card titles included), the small letter-spaced labels in the accent, squarer uppercase buttons with the accent as the
   primary fill, the nav in spaced caps (the open page in the text colour, only its icon in the accent), and the game card's full-width accent button. Not taken: its
   hexagon pattern and art, its gold, its full-bleed black stripes (the floating sheet and rounded panels stay).
   Replaces the sentence-case Geist rules of "public site: type and composition" above, which still hold where
   nothing here overrides them. The admin keeps its own look (html:not([data-division="admin"]) = everything but html[data-division="admin"]). */
html:not([data-division="admin"]) body { font-weight: 400; letter-spacing: 0; }
html:not([data-division="admin"]) .display { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: -.01em; line-height: 1.04; }
html:not([data-division="admin"]) .h1 { font-size: clamp(2rem, 4.6vw, 3.6rem); letter-spacing: -.01em; line-height: 1.02; }
html:not([data-division="admin"]) .h2 { font-size: clamp(1.4rem, 2.6vw, 2.2rem); letter-spacing: -.01em; line-height: 1.08; max-width: 22em; }
/* Panchang is for the page and section headlines only (.display: h1, h2; owner, 2026-10-18: "for better hierarchy").
   Everything below them, card and column titles (.h3), initials and the text logo, is set in Inter: plain case,
   semibold, easy to read at small sizes. */
html:not([data-division="admin"]) .h3 { font-family: var(--font-body); font-weight: 600; text-transform: none; font-size: 1.08rem; letter-spacing: -.012em; line-height: 1.3; }
html:not([data-division="admin"]) :is(.avatar-ini, .brand) { font-family: var(--font-body); }
html:not([data-division="admin"]) .chart-title { font-size: clamp(1.1rem, 2vw, 1.5rem); }
html:not([data-division="admin"]) .legal .display { font-size: clamp(1.7rem, 3.8vw, 2.6rem); }
html:not([data-division="admin"]) .lead { font-weight: 400; letter-spacing: 0; line-height: 1.6; }
/* labels: small spaced caps in the accent, no marker in front (a round dot only where the mock-up has one: .eyebrow-dot) */
html:not([data-division="admin"]) :where(.eyebrow) { font-family: var(--font-body); font-size: .72rem; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: rgb(var(--accent-text)); }
html:not([data-division="admin"]) .toned > .eyebrow::before, html:not([data-division="admin"]) .toned > .row-between > div > .eyebrow::before, html:not([data-division="admin"]) .toned > .wrap > .eyebrow::before { content: none; }
html:not([data-division="admin"]) .eyebrow-dot { display: flex; align-items: center; gap: 10px; }
html:not([data-division="admin"]) .eyebrow-dot::before { content: ''; flex: none; width: 7px; height: 7px; border-radius: 50%; background: rgb(var(--c-accent)); }
html:not([data-division="admin"]) .eyebrow-dot.is-bad { color: rgb(var(--c-ink3)); }
html:not([data-division="admin"]) .eyebrow-dot.is-bad::before { background: rgb(var(--c-err)); }
html:not([data-division="admin"]) .season .eyebrow { color: rgb(255 255 255 / .7); }
html:not([data-division="admin"]) :is(.season, .cinema) .dim { color: rgb(255 255 255 / .5); } /* the grey second line on the dark panels (season banner, video room), in either theme: the plain .dim is ink-based and vanishes there on the light page */
html:not([data-division="admin"]) .season .eyebrow-dot::before { background: rgb(77 107 255); } /* a step lighter than the accent: the panel is near black */
/* the accent as text: a lighter step on the dark pages (--accent-text), so small labels and links stay readable */
html:not([data-division="admin"]) :where(.accent, .link, .compare-side.is-after .compare-tag, .vslide-src a:hover, .vcard-src:hover) { color: rgb(var(--accent-text)); } /* :where keeps it light, so headings (.display .accent) and dark panels (.cinema .link) keep their own colour */
html:not([data-division="admin"]) .display .hot { color: rgb(var(--accent-text)); }
/* figures: Inter bold (Panchang is too wide for numbers in a row) */
html:not([data-division="admin"]) :is(.stat-val, .kv dd, .live-strip-num, .case-wide-metrics dd, .compare-val, .compare-mini b) { font-family: var(--font-body); font-weight: 700; letter-spacing: -.02em; }
html:not([data-division="admin"]) .pipeline-big { font-family: var(--font-body); font-weight: 700; } /* the workflow step number keeps its own tight tracking */
/* buttons: squarer, uppercase, spaced; the primary one is the accent, the secondary one near black */
html:not([data-division="admin"]) .btn { border-radius: var(--r-btn); padding: 15px 24px; font-size: .78rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
html:not([data-division="admin"]) .btn-sm { padding: 11px 16px; font-size: .72rem; }
@media (max-width: 820px) { html:not([data-division="admin"]) .topbar-cta .btn { padding: 10px 14px; font-size: .72rem; } }
/* the top bar's "Work with us": the page's primary button (flat accent, no liquid metal), as tall as the switch's
   raised item beside it and without a shadow of its own, so it sits in the bar instead of on it (owner, 2026-10-18) */
html:not([data-division="admin"]) .topbar-btn { min-height: 38px; padding-block: 0; box-shadow: none; }
html:not([data-division="admin"]) .topbar-btn:hover { box-shadow: none; transform: none; }
/* the bar's corners follow the button's (owner, 2026-10-18): the 38px button and the switch's raised item have the
   buttons' 12px, and the 58px bar has 12px plus the 10px it leaves around them, so the curves run parallel */
html:not([data-division="admin"]) .topbar-inner { border-radius: 22px; }
html:not([data-division="admin"]) .topbar :is(.switch, .switch-thumb, .switch-item) { border-radius: var(--r-btn); }
@media (max-width: 720px) {
  html:not([data-division="admin"]) .topbar .switch { border-radius: calc(var(--r-btn) + 4px); } /* the 12px items plus the track's 4px padding */
}
html:not([data-division="admin"]) .btn-primary { background: rgb(var(--c-accent)); color: #fff; border-color: transparent; box-shadow: 0 2px 6px rgb(0 0 0 / .22); }
html:not([data-division="admin"]) .btn-primary:hover { background: color-mix(in srgb, rgb(var(--c-accent)) 86%, #fff); box-shadow: 0 2px 6px rgb(0 0 0 / .22); }
html:not([data-division="admin"]) .btn-solid { background: rgb(var(--c-dark)); color: #fff; border-color: rgb(255 255 255 / .14); }
html:not([data-division="admin"]) .btn-solid:hover { opacity: 1; border-color: rgb(255 255 255 / .34); }
html:not([data-division="admin"]) .hero-cover .btn-solid { border-color: rgb(255 255 255 / .35); }
html:not([data-division="admin"]) .btn-aura::before { border-radius: inherit; }
/* nav: spaced caps; the open page keeps the text colour (white on dark, black on light), only its icon is blue */
html:not([data-division="admin"]) .switch-item { font-size: .74rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
html:not([data-division="admin"]) .switch-item.is-on { color: rgb(var(--c-ink)); font-weight: 600; }
html:not([data-division="admin"]) .switch-label::after { font-weight: 600; }
@media (max-width: 720px) { html:not([data-division="admin"]) .topbar .switch-item { font-size: .66rem; letter-spacing: .08em; } }
/* game card: "Play on Fortnite" as a full-width accent button across the foot of the card */
/* game card: "Play on Fortnite" keeps its dark grey fill and the round arrow button on the right (owner, 2026-10-18),
   in the spaced caps and squarer corners of the other buttons, with a pulsing green dot before the label */
html:not([data-division="admin"]) .game-card-link { padding: 6px 6px 6px 16px; border-radius: var(--r-btn); font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; }
html:not([data-division="admin"]) .game-card-link-ico { border-radius: 8px; }
.game-card-link-label { display: inline-flex; align-items: center; gap: 10px; }
.game-card-link .live-dot { margin: 0; }
/* the call to action band (partials/cta.php: "Own the next season.", "Want an island ..."): a near-black graphite panel
   with white text in both themes (owner, 2026-10-18), the flat accent button, and "Taking new clients" with the green
   live dot under it. A plain dark shadow, no glow. */
html:not([data-division="admin"]) .cta-band { background: #18181a; color: rgb(255 255 255 / .8); box-shadow: 0 18px 34px -24px rgb(0 0 0 / .55); }
html:not([data-division="admin"]) .cta-band .display { color: #fff; }
html:not([data-division="admin"]) .cta-band-points { color: rgb(255 255 255 / .78); }
html:not([data-division="admin"]) .cta-band-points svg.ico, html:not([data-division="admin"]) .cta-band-tag svg.ico { color: rgb(120 140 255); } /* the small icons on the dark band in the blue that reads on dark (gold until 2026-10-25, owner: no more yellow) */
html:not([data-division="admin"]) .cta-band-tag { background: rgb(255 255 255 / .1); color: #fff; }
html:not([data-division="admin"]) .cta-band-tag b { color: #fff; }
html:not([data-division="admin"]) .cta-band-open { display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-size: .8rem; font-weight: 500; color: rgb(255 255 255 / .78); }
html:not([data-division="admin"]) .cta-band-open .live-dot { margin: 0; }
html:not([data-theme="light"]) .cta-band { box-shadow: inset 0 0 0 1px rgb(255 255 255 / .06), 0 18px 34px -24px rgb(0 0 0 / .8); } /* on the dark page a faint edge keeps it apart from the sheet */
/* The call-to-action buttons are flat (owner, 2026-10-25: "without the shine (depth)"; from 2026-10-18 they had a light
   line along the top and a short shadow): the primary, solid and "Send message" buttons and the top bar's "Work with
   us" are plain fills, no top line, no shadow, no gloss; the "Send message" button is the flat accent (its liquid-metal
   fill and moving sheen are switched off) */
html:not([data-division="admin"]) :is(.btn-primary, .btn-solid, .btn-aura), html:not([data-division="admin"]) :is(.btn-primary, .btn-solid, .btn-aura):hover, html:not([data-division="admin"]) .topbar-btn, html:not([data-division="admin"]) .topbar-btn:hover { box-shadow: none; text-shadow: none; }
html:not([data-division="admin"]) .btn-aura { background: rgb(var(--c-accent)); border-color: transparent; }
html:not([data-division="admin"]) .btn-aura:hover { background: color-mix(in srgb, rgb(var(--c-accent)) 86%, #fff); }
html:not([data-division="admin"]) .btn-aura::before { display: none; }
.btn-aura:disabled:not(.is-loading) { box-shadow: none !important; }

/* case study page: the way back as a secondary button (light card fill, the arrow in its own round chip that slides
   on hover), and every figure on one results card with a clear order of weight (owner, 2026-10-18) */
html:not([data-division="admin"]) .btn-back { padding: 6px 18px 6px 6px; gap: 10px; background: rgb(var(--paper)); color: rgb(var(--c-ink)); box-shadow: inset 0 1px 0 rgb(255 255 255 / .5), 0 6px 12px -8px rgb(0 0 0 / .35); }
html:not([data-theme="light"]) .btn-back { box-shadow: inset 0 1px 0 rgb(255 255 255 / .08), 0 6px 12px -8px rgb(0 0 0 / .7); }
.btn-back-ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; background: rgb(var(--c-ink) / .08); transition: background-color var(--dur-base) var(--ease-in-out), color var(--dur-base); }
.btn-back-ico svg.ico { width: 14px; height: 14px; transition: transform var(--dur-base) var(--ease-in-out); }
.btn-back:hover .btn-back-ico { background: rgb(var(--c-accent)); color: #fff; }
.btn-back:hover .btn-back-ico svg.ico { transform: translateX(-2px); }
.btn-back + .eyebrow { margin-top: clamp(28px, 4vw, 44px); }
.case-results { margin-top: clamp(28px, 4vw, 44px); background: rgb(var(--paper)); overflow: hidden; }
.case-results-main { padding: clamp(22px, 3.4vw, 40px); }
.case-results-main .compare { margin-top: 0; align-items: end; }
.case-results-main .compare-val { font-size: clamp(2.4rem, 5vw, 3.8rem); } /* before and after the same size (owner, 2026-10-18): the arrow and the multiple carry the jump */
.case-results-main .compare-side.is-after .compare-val { color: rgb(var(--c-ink)); }
.case-results-main .compare-side:not(.is-after) { order: 0; }
.case-results-main .compare-flag { margin-top: 20px; }
.case-results-stats { display: grid; grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr)); border-top: 1px solid rgb(var(--c-line)); background: rgb(var(--c-ink) / .025); }
.case-results-main:empty + .case-results-stats, .case-results > .case-results-stats:first-child { border-top: 0; }
.case-results-stat { display: grid; align-content: start; gap: 6px; padding: clamp(18px, 2.6vw, 28px) clamp(18px, 3vw, 36px); }
.case-results-stat + .case-results-stat { border-left: 1px solid rgb(var(--c-line)); }
.case-results-stat b { font-family: var(--font-body); font-weight: 700; font-size: clamp(1.6rem, 3vw, 2.3rem); line-height: 1; letter-spacing: -.02em; color: rgb(var(--c-ink)); font-variant-numeric: tabular-nums; }
.case-results-stat span { font-size: .84rem; line-height: 1.35; color: rgb(var(--c-ink3)); }
@media (max-width: 479px) {
  /* phones: before over after, the hairline turns, so the large result keeps its size instead of being cut off */
  .case-results-main .compare { grid-template-columns: 1fr; }
  .case-results-main .compare-side { padding-right: 0; }
  .case-results-main .compare-side + .compare-side { padding: 18px 0 0; border-left: 0; border-top: 1px solid rgb(var(--c-line)); margin-top: 18px; }
  .case-results-main .compare-val { font-size: clamp(2.4rem, 12vw, 3.2rem); }
  .case-results-stat { padding: 16px 12px; }
  .case-results-stat b { font-size: clamp(1.3rem, 7vw, 1.7rem); }
  .case-results-stat span { font-size: .74rem; }
}

/* the secondary button on the dark pages ("Our islands"): a frosted light-glass button instead of black on near-black
   (owner, 2026-10-18: it looked muddy); on the light page .btn-solid stays the near-black fill */
html:not([data-theme="light"]):not([data-division="admin"]) .btn-solid { background: rgb(255 255 255 / .08); color: #fff; border-color: rgb(255 255 255 / .16); box-shadow: none; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
html:not([data-theme="light"]):not([data-division="admin"]) .btn-solid:hover { background: rgb(255 255 255 / .14); border-color: rgb(255 255 255 / .26); }

/* title tags (.eyebrow) on the dark pages in the blue that reads on dark, their dot too (gold from 2026-10-18 until the
   owner switched it to blue on 2026-10-25, the Game Development tab's controller with it); on the light page the accent blue. The video room keeps its grey tags, the
   "without" column its grey with the red dot. */
html:not([data-theme="light"]):not([data-division="admin"]) :where(.eyebrow) { color: rgb(var(--accent-text)); }
html:not([data-theme="light"]):not([data-division="admin"]) .eyebrow-dot:not(.is-bad)::before { background: rgb(var(--accent-text)); }
html:not([data-theme="light"]):not([data-division="admin"]) .eyebrow-dot.is-bad { color: rgb(var(--c-ink3)); }
html:not([data-theme="light"]):not([data-division="admin"]) .cinema .eyebrow { color: rgb(255 255 255 / .55); }

/* "We handle" / "You provide" (workflow.php): every line ticked by a white check in a green circle (owner, 2026-10-18) */
html:not([data-division="admin"]) .scope-list svg.ico { width: 20px; height: 20px; margin-top: 1px; padding: 4px; box-sizing: border-box; border-radius: 50%; background: #22c55e; color: #fff; box-shadow: inset 0 1px 0 rgb(255 255 255 / .3), 0 2px 4px -1px rgb(0 0 0 / .25); }

/* the "Easy & Simple" badge beside "You provide": an accent pill with a top shine and a light band that sweeps across
   it every few seconds (still under reduced motion) */
.scope-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; }
.badge-shine { position: relative; overflow: hidden; display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px; background: linear-gradient(180deg, color-mix(in srgb, rgb(var(--c-accent)) 82%, #fff), rgb(var(--c-accent))); color: #fff; font-family: var(--font-body); font-size: .66rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; box-shadow: inset 0 1px 0 rgb(255 255 255 / .35), 0 4px 10px -4px rgb(0 0 0 / .35); }
.badge-shine::after { content: ''; position: absolute; inset: -20% auto -20% -60%; width: 40%; background: linear-gradient(100deg, transparent, rgb(255 255 255 / .7), transparent); transform: skewX(-20deg); animation: badge-sheen 3.6s var(--ease-in-out) 1s infinite; }
@keyframes badge-sheen { 0% { transform: translateX(0) skewX(-20deg); } 45%, 100% { transform: translateX(475%) skewX(-20deg); } } /* transform, not left: no layout per frame */
@media (prefers-reduced-motion: reduce) { .badge-shine::after { display: none; } }

/* case study cards (case-card.php): the summary cut to two lines, and the card's button in the island cards' style:
   dark graphite, full width, top shine, the arrow in a chip that turns accent while the card is hovered */
.case-card-summary { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.case-card-btn { align-self: end; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 6px 6px 16px; border-radius: var(--r-btn); background: rgb(var(--c-graphite)); color: #fff; font-size: .74rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; line-height: 1; box-shadow: inset 0 1px 0 rgb(255 255 255 / .14), 0 6px 12px -8px rgb(0 0 0 / .7); transition: background-color var(--dur-base) var(--ease-in-out); }
.case-card-btn-ico { flex: none; width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; background: rgb(255 255 255 / .14); transition: background-color var(--dur-base) var(--ease-in-out); }
.case-card-btn-ico svg.ico { width: 13px; height: 13px; transition: transform var(--dur-base) var(--ease-in-out); }
.case-card:hover .case-card-btn { background: rgb(var(--c-graphite) / .9); }
.case-card:hover .case-card-btn-ico { background: rgb(var(--c-accent)); }
.case-card:hover .case-card-btn-ico svg.ico { transform: translateX(2px); }

/* case study page title: smaller than a page headline and two lines at most (owner, 2026-10-18) */
html:not([data-division="admin"]) .case-title { font-size: clamp(1.3rem, 2.6vw, 2rem); line-height: 1.08; max-width: none; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* before -> after: an arrow from the starting point to the result with the multiple above it (case-compare.php,
   case-card.php; owner, 2026-10-18). The arrow is a line that runs from grey to the accent with the arrowhead icon at
   its end; the multiple is a small accent pill. On a phone the two figures stack and the arrow points down. */
.compare { grid-template-columns: minmax(0, auto) minmax(72px, 1fr) minmax(0, auto); align-items: end; } /* the arrow takes the room between the two figures */
.compare .compare-side + .compare-jump + .compare-side { border-left: 0; padding-left: 0; }
.compare .compare-side:first-child { padding-right: 0; }
.compare-jump { display: grid; justify-items: center; gap: 8px; align-self: center; padding: 0 clamp(14px, 2.6vw, 28px); width: 100%; }
/* the case study page: the result on the right edge, the arrow running all the way to it, the multiple centred above */
.compare.has-jump { grid-template-columns: auto minmax(72px, 1fr) auto; }
.compare.has-jump .compare-side.is-after { justify-self: end; align-items: flex-end; text-align: right; }
/* the journey from the starting point to the result: a line across the whole gap, from grey into the accent, with a
   soft light gliding along it towards the result every few seconds (owner, 2026-10-18); still under reduced motion */
/* the arrow is one drawn shape (owner, 2026-10-18: no icon stuck on a line): a 3px line from grey into the accent that
   runs into an arrowhead of the same stroke and colour (::after, a turned corner), with the light gliding along it */
.compare-arrow { --aw: 3px; --ah: 11px; position: relative; display: block; width: 100%; height: calc(var(--ah) * 1.5); }
.compare-arrow::before { content: ''; position: absolute; left: 0; right: calc(var(--aw) / 2); top: 50%; height: var(--aw); translate: 0 -50%; border-radius: var(--aw);
  background-image: linear-gradient(90deg, transparent 40%, rgb(255 255 255 / .85) 50%, transparent 60%), linear-gradient(90deg, rgb(var(--c-muted) / .3), rgb(var(--c-accent)) 70%);
  background-size: 250% 100%, 100% 100%; background-repeat: no-repeat; background-position: 100% 0, 0 0; animation: journey 3.2s var(--ease-in-out) 1s infinite; }
.compare-arrow::after { content: ''; position: absolute; right: calc(var(--aw) * .7); top: 50%; width: var(--ah); height: var(--ah); border-top: var(--aw) solid rgb(var(--c-accent)); border-right: var(--aw) solid rgb(var(--c-accent)); border-radius: 0 calc(var(--aw) / 1.5) 0 0; translate: 0 -50%; rotate: 45deg; }
@keyframes journey { 0% { background-position: 100% 0, 0 0; } 55%, 100% { background-position: 0% 0, 0 0; } }
@media (prefers-reduced-motion: reduce) { .compare-arrow::before { animation: none; } }
.compare-x { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px; background: rgb(var(--c-accent)); color: #fff !important; /* white on the accent, also where a card's small grey text rule would reach */ font-family: var(--font-body); font-size: .8rem; font-weight: 700; letter-spacing: .02em; font-variant-numeric: tabular-nums; box-shadow: inset 0 1px 0 rgb(255 255 255 / .3), 0 4px 10px -4px rgb(0 0 0 / .35); }
/* on a case card: the two figures at the ends of one line, the journey between them taking all the room left, and
   what they count on the line underneath (left and right) */
.compare-mini.is-journey { display: grid; gap: 6px; }
.compare-mini.is-journey > .cm-nums { display: grid; grid-template-columns: auto minmax(40px, 1fr) auto; align-items: center; gap: 10px; min-width: 0; } /* outranks the old .compare-mini > span column */
.compare-mini.is-journey > .cm-labels { display: flex; flex-direction: row; justify-content: space-between; gap: 16px; min-width: 0; }
.cm-labels .is-after { text-align: right; }
.compare-mini.is-journey b.is-after { color: rgb(var(--c-ink)); }
.compare-mini-jump { display: grid; justify-items: center; gap: 6px; align-self: center; }
.compare-mini-jump .compare-x { padding: 3px 8px; font-size: .68rem; }
.compare-mini-jump svg.ico { width: 15px !important; height: 15px !important; margin-top: 0 !important; color: rgb(var(--c-accent)) !important; }
.compare-mini-jump .compare-arrow { --aw: 2px; --ah: 8px; }
@media (max-width: 479px) {
  .case-results-main .compare { grid-template-columns: 1fr; }
  .case-results-main .compare-side + .compare-jump + .compare-side { border-top: 0; margin-top: 0; padding-top: 0; }
  .case-results-main .compare-jump { grid-auto-flow: column; justify-content: start; align-items: center; gap: 12px; padding: 14px 0; }
  .case-results-main .compare-arrow { width: calc(var(--ah) * 1.5); height: 44px; }
  .case-results-main .compare-arrow::before { left: 50%; right: auto; top: 0; bottom: calc(var(--aw) / 2); width: var(--aw); height: auto; translate: -50% 0; background-image: linear-gradient(180deg, transparent 40%, rgb(255 255 255 / .85) 50%, transparent 60%), linear-gradient(180deg, rgb(var(--c-muted) / .3), rgb(var(--c-accent)) 70%); background-size: 100% 250%, 100% 100%; animation-name: journey-down; }
  .case-results-main .compare-arrow::after { right: auto; left: 50%; top: auto; bottom: calc(var(--aw) * .7); translate: -50% 0; rotate: 135deg; }
}
@keyframes journey-down { 0% { background-position: 0 100%, 0 0; } 55%, 100% { background-position: 0 0%, 0 0; } }

/* the rows of headline numbers (partials/stats.php): evenly spaced columns with a short hairline between them that
   fades out at both ends (owner, 2026-10-18). The first figure of each row has none; rows of three get a quiet line
   between the rows as well. */
html:not([data-division="admin"]) .stats-row { --rg: clamp(40px, 5vw, 60px); gap: var(--rg) 0; } /* the line between two rows sits in the middle of this gap */
html:not([data-division="admin"]) .stats-row .stat { position: relative; padding-inline: clamp(18px, 3vw, 40px); }
html:not([data-division="admin"]) .stats-row .stat::before { content: ''; position: absolute; left: 0; top: 6%; bottom: 6%; width: 1px; background: linear-gradient(180deg, transparent, rgb(var(--c-ink) / .16) 25%, rgb(var(--c-ink) / .16) 75%, transparent); }
.stats-row[data-cols="4"] .stat:nth-child(4n+1)::before, .stats-row[data-cols="3"] .stat:nth-child(3n+1)::before { content: none; }
.stats-row .stat:nth-child(1), .stats-row[data-cols="4"] .stat:nth-child(4n+1), .stats-row[data-cols="3"] .stat:nth-child(3n+1) { padding-left: 0; }
/* one size for every figure of a row, small enough that the longest (--len characters, stats.php) fits its column with
   room on both sides: .7em a character (Inter bold is about .6em; the rest is air), the column is the row (a size container) shared by the
   columns in view (--n), less the padding on both sides; never larger than the usual size (owner, 2026-10-20) */
html:not([data-division="admin"]) .stats-row { container-type: inline-size; --n: var(--cols, 4); }
html:not([data-division="admin"]) .stats-row[data-cols] .stat-val { font-size: min(clamp(2.2rem, 4.4vw, 3.4rem), calc((100cqw / var(--n) - 2 * clamp(18px, 3vw, 40px)) / (var(--len, 4) * .7))); }
@media (max-width: 1023px) { html:not([data-division="admin"]) .stats-row[data-cols="4"] { --n: 2; } }
/* the icon above each figure (owner, 2026-10-25: "more character, less texty"): a flat Nucleo icon in the accent on a
   small neutral round chip; since 2026-10-26 the headline numbers colour theirs, below */
.stat-ico { flex: none; display: grid; place-items: center; width: 38px; height: 38px; border-radius: 12px; background: rgb(var(--c-ink) / .06); color: rgb(var(--accent-text)); }
.stat-ico svg.ico { width: 18px; height: 18px; }
@media (max-width: 720px) { .stat-ico { width: 32px; height: 32px; border-radius: 10px; } .stat-ico svg.ico { width: 16px; height: 16px; } }
/* stat icon colours (owner, 2026-10-26: "colorful icons" in the headline numbers of both pages): every icon has its own
   colour (stats.php data-tone) on a chip faintly tinted with it; deeper values on the light page, lighter on the dark */
.stat-ico[data-tone] { color: rgb(var(--tone)); background: rgb(var(--tone) / .12); }
.stat-ico[data-tone="blue"] { --tone: 30 64 255; }
.stat-ico[data-tone="orange"] { --tone: 234 88 12; }
.stat-ico[data-tone="green"] { --tone: 22 163 74; }
.stat-ico[data-tone="violet"] { --tone: 124 58 237; }
.stat-ico[data-tone="pink"] { --tone: 219 39 119; }
.stat-ico[data-tone="teal"] { --tone: 13 148 136; }
.stat-ico[data-tone="gold"] { --tone: 202 138 4; }
html:not([data-theme="light"]) .stat-ico[data-tone] { background: rgb(var(--tone) / .16); }
html:not([data-theme="light"]) .stat-ico[data-tone="blue"] { --tone: 120 140 255; }
html:not([data-theme="light"]) .stat-ico[data-tone="orange"] { --tone: 251 133 60; }
html:not([data-theme="light"]) .stat-ico[data-tone="green"] { --tone: 74 222 128; }
html:not([data-theme="light"]) .stat-ico[data-tone="violet"] { --tone: 167 139 250; }
html:not([data-theme="light"]) .stat-ico[data-tone="pink"] { --tone: 244 114 182; }
html:not([data-theme="light"]) .stat-ico[data-tone="teal"] { --tone: 45 212 191; }
html:not([data-theme="light"]) .stat-ico[data-tone="gold"] { --tone: 250 204 21; }
/* "Live players" (stats.php is-live): the green dot before its label says it is live; while Epic is asked, a soft bar
   breathes where the figure will be (opacity only), the width of a four-digit figure so nothing moves when it lands */
.stat.is-live .stat-label { display: inline-flex; align-items: center; gap: 8px; } /* the dot after the words (2026-10-25) */
.stat.is-live .stat-label .live-dot { margin: 0; }
.stat-wait { display: block; width: 2.6em; height: .82em; margin-block: .09em; border-radius: 6px; background: rgb(var(--c-ink) / .1); animation: stat-wait 1.2s ease-in-out infinite alternate; }
@keyframes stat-wait { from { opacity: .45; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .stat-wait { animation: none; } }
/* the figures roll in like an odometer (odometer() in kx.js, owner 2026-10-20): a digit is a 1em window over a strip of
   0-9 (twice) that slides up a full round and settles on its digit (--d), with an expo ease; the marks between the
   digits rise in after them. Transform and opacity only; will-change goes once it has settled (.is-done). */
.odo { display: inline-flex; align-items: flex-end; }
.odo-d { display: inline-block; height: 1em; overflow: hidden; }
.odo-strip { display: flex; flex-direction: column; will-change: transform; transition: transform var(--odo-t, 1.6s) cubic-bezier(.16, 1, .3, 1) var(--odo-delay, 0s); }
.odo-strip > span { display: block; height: 1em; line-height: 1; }
.odo-c { display: inline-block; opacity: 0; transform: translateY(.4em); transition: opacity .45s var(--ease-out) var(--odo-delay, 0s), transform .7s cubic-bezier(.16, 1, .3, 1) var(--odo-delay, 0s); }
.odo.is-on .odo-strip { transform: translateY(calc(var(--d) * -1em)); }
.odo.is-on .odo-c { opacity: 1; transform: none; }
.odo.is-done .odo-strip { will-change: auto; }
.stats-row[data-cols="3"] .stat:nth-child(n+4)::after { content: ''; position: absolute; top: calc(var(--rg) / -2); left: 0; right: 0; height: 1px; background: linear-gradient(90deg, transparent, rgb(var(--c-ink) / .1) 20%, rgb(var(--c-ink) / .1) 80%, transparent); }
@media (max-width: 1023px) { /* four figures: two by two */
  .stats-row[data-cols="4"] .stat:nth-child(2n+1)::before { content: none; }
  .stats-row[data-cols="4"] .stat:nth-child(2n+1) { padding-left: 0; }
  .stats-row[data-cols="4"] .stat:nth-child(3)::before { content: none; }
}
@media (max-width: 767px) { /* three to a row stacks: the column lines go, the row lines stay */
  .stats-row[data-cols="3"] .stat::before { content: none; }
  .stats-row[data-cols="3"] .stat { padding-left: 0; }
  .stats-row[data-cols="3"] .stat:nth-child(n+2)::after { content: ''; position: absolute; top: calc(var(--rg) / -2); left: 0; right: 0; height: 1px; background: rgb(var(--c-ink) / .1); }
}

/* the countdown as split-flap cards (fomo() in site.js builds the halves): the card is the figure itself, cut at the
   seam into a lit top half and a darker bottom half, with its label underneath. The tile around it becomes a plain
   column. The fold and fall are animated in site.js; perspective here gives them depth. */
.season-clock span { padding: 0; gap: 7px; background: none; box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; min-width: 0; }
.season-clock span::after { content: none; }
.season-clock b.flap { --fh: calc(1em + 36px); position: relative; display: grid; place-items: center; min-width: var(--fh); height: var(--fh); padding: 0 10px; /* square: as wide as it is tall (wider only for a three-digit day count) */ border-radius: var(--r-card); perspective: 320px; text-shadow: none; box-shadow: 0 12px 20px -12px rgb(0 0 0 / .9), 0 1px 0 rgb(255 255 255 / .04); }
.flap .fl-size { visibility: hidden; font-style: normal; }
.flap .fl { position: absolute; left: 0; right: 0; height: 50%; overflow: hidden; display: block; backface-visibility: hidden; }
.flap .fl > i { position: absolute; left: 0; right: 0; height: 200%; /* twice its half = the whole flap, so the digit is centred on the seam and each half shows its half of it, also on phones where the flap's height comes from its width (2026-10-20: it showed the whole number twice) */ display: grid; place-items: center; font-style: normal; line-height: 1; text-shadow: 0 2px 3px rgb(0 0 0 / .45); }
.flap .fl-top { top: 0; border-radius: var(--r-card) var(--r-card) 0 0; background: linear-gradient(180deg, rgb(48 50 62), rgb(32 33 42)); box-shadow: inset 0 1px 0 rgb(255 255 255 / .16); transform-origin: 50% 100%; }
.flap .fl-top > i { top: 0; }
.flap .fl-bot { bottom: 0; border-radius: 0 0 var(--r-card) var(--r-card); background: linear-gradient(180deg, rgb(26 27 35), rgb(15 16 21)); box-shadow: inset 0 -2px 0 rgb(0 0 0 / .4); transform-origin: 50% 0; }
.flap .fl-bot > i { bottom: 0; }
.flap .fl-fold, .flap .fl-unfold { z-index: 2; }
.flap::after { content: ''; position: absolute; left: 0; right: 0; top: calc(50% - .5px); z-index: 3; height: 1px; background: rgb(0 0 0 / .6); box-shadow: 0 1px 0 rgb(255 255 255 / .05); } /* the seam */
.season-clock.is-compact b.flap { --fh: calc(1em + 26px); min-width: var(--fh); padding: 0 7px; border-radius: 10px; }
.season-clock.is-compact .flap .fl-top { border-radius: 10px 10px 0 0; } .season-clock.is-compact .flap .fl-bot { border-radius: 0 0 10px 10px; }
@media (max-width: 479px) { .season-clock b.flap { min-width: 0; width: 100%; height: auto; aspect-ratio: 1; } }

/* the call to action band's season clock (cta.php): the banner's flip tiles, smaller, above the title */
.cta-band-clock { display: grid; justify-items: start; gap: 8px; margin-bottom: 18px; }
.season-clock.is-compact { gap: 6px; }
.season-clock.is-compact span { gap: 5px; }
.season-clock.is-compact b { font-size: clamp(1.15rem, 1.8vw, 1.45rem); }
.season-clock.is-compact small { font-size: .56rem; }

/* "You provide": plain grey arrows, no circle (owner, 2026-10-18); "We handle" keeps the green-circled ticks */
html:not([data-division="admin"]) .scope.is-plain .scope-list svg.ico { width: 16px; height: 16px; margin-top: 3px; padding: 0; border-radius: 0; background: none; box-shadow: none; color: rgb(var(--c-ink3)); }
/* contact (contact.php): the other ways in stack right under the availability chip, card after card; the column may
   end above the form's foot (owner, 2026-10-20: not pushed to the foot, not spread out to it) */
@media (min-width: 900px) {
  .contact-intro { display: flex; flex-direction: column; }
  .contact-intro > .contact-open { align-self: flex-start; } /* the availability chip keeps its own width */
  .contact-intro .contact-ways { margin-top: 28px; }
}

/* islands (Game Development): a swipe row up to tablet width, the next card peeking in, instead of a column of tall
   cards (phones) or three squeezed ones (tablets) */
@media (max-width: 1000px) {
  .island-grid { display: grid; grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: 84%; grid-template-rows: repeat(var(--card-rows), auto); gap: 0 12px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none; margin: 26px calc(clamp(16px, 4.6vw, 64px) * -1) -24px; padding: 6px clamp(16px, 4.6vw, 64px) 24px; /* 26 + 6 = the 32px gap under the heading */ scroll-padding-inline: clamp(16px, 4.6vw, 64px); }
  .island-grid::-webkit-scrollbar { display: none; }
  .island-grid > * { grid-row: 1 / -1; margin-bottom: 0; scroll-snap-align: start; }
}
@media (min-width: 768px) and (max-width: 1000px) { .island-grid { grid-auto-columns: 44%; } }

/* ---------- phones (owner, 2026-10-18: every new section checked and tuned at 390 and 320 px) ---------- */
@media (max-width: 767px) {
  /* headline numbers: two to a row (three crushed the figures into each other), a hairline between the two and a
     quiet line between the rows */
  html:not([data-division="admin"]) .stats-row[data-cols] { grid-template-columns: repeat(2, minmax(0, 1fr)); --rg: 36px; --n: 2; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat { padding: 0 0 0 20px; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat::before { content: '' !important; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat:nth-child(2n+1) { padding-left: 0; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat:nth-child(2n+1)::before { content: none !important; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat::after { content: none !important; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat:nth-child(2n+1):nth-child(n+3)::after { content: '' !important; position: absolute; top: calc(var(--rg) / -2); left: 0; width: calc(200% + 20px); height: 1px; background: linear-gradient(90deg, transparent, rgb(var(--c-ink) / .1) 15%, rgb(var(--c-ink) / .1) 85%, transparent); }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat-val { font-size: clamp(1.55rem, 8.4vw, 2.4rem); }
  /* contact: the form first (it is what the section is for), the other ways in after it, as compact rows */
  .contact-grid { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
  .contact-form { margin-top: 24px; }
  .contact-intro > .contact-open { align-self: flex-start; }
  .contact-intro { display: contents; }
  .contact-intro > :not(.contact-ways) { order: 0; }
  .contact-form { order: 1; }
  .contact-intro > .contact-ways { order: 2; margin-top: 16px; padding-top: 0; max-width: none; gap: 8px; }
  .contact-way { padding: 9px 12px; gap: 12px; }
  .contact-way-ico { width: 36px; height: 36px; border-radius: 10px; }
  .contact-way-ico svg { width: 16px; height: 16px; }
  .contact-intro > .h2 + .lead { margin-top: 12px; }
  /* the case study page's title: up to three lines here, so the claim is not cut */
  html:not([data-division="admin"]) .case-title { -webkit-line-clamp: 3; font-size: clamp(1.25rem, 6.4vw, 1.6rem); }
}

/* Steady hover and press (owner, 2026-10-18: "it spazzes out on the very edge"). An element that moves on hover (lifts)
   or press (shrinks) can slide out from under a cursor that sits on its edge: hover drops, it moves back, hover
   comes back, and it flickers; a press on the edge even ends outside it, so the click is lost. While such an element
   is hovered or pressed, an invisible ::after reaches a little past its edges, under its content (z-index -1 in its
   own stacking context), so the cursor stays on it until it has really left. */
:is(.btn, .social-btn, .contact-way, .card-hover, a.vs-plat, .game-card-link, .case-card-btn) { isolation: isolate; }
:is(.social-btn, .contact-way, a.vs-plat, .game-card-link) { position: relative; }
.btn:not(.is-loading):is(:hover, :active)::after, .social-btn:hover::after, .contact-way:hover::after, a.vs-plat:hover::after, .game-card-link:is(:hover, :active)::after,
html[data-division] .card-hover:hover::after { content: ''; position: absolute; z-index: -1; pointer-events: auto; background: none; border: 0; box-shadow: none; animation: none; }
.btn:not(.is-loading):is(:hover, :active)::after { inset: -4px -8px -6px; border-radius: inherit; }
.social-btn:hover::after { inset: -3px -3px -5px; border-radius: 50%; }
.contact-way:hover::after { inset: -2px -2px -5px; }
a.vs-plat:hover::after { inset: -2px -2px -4px; }
.game-card-link:is(:hover, :active)::after { inset: -3px -6px; }
html[data-division] .card-hover:hover::after { inset: -2px -2px -10px; border-radius: inherit; } /* the card lifts 6px: the strip under it stays put */
.btn-aura { overflow: visible; } /* its fill (::before) is clipped by border-radius: inherit; overflow had clipped the steady edge too */

/* loading states (mediaStates() in site.js): a picture box shimmers until its picture is in; a video box shows its still
   and, while the video is asked to play but cannot yet, a small spinner over it that fades when it plays */
.media.is-pending::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(100deg, transparent 30%, rgb(255 255 255 / .12) 50%, transparent 70%) 0 0 / 250% 100% no-repeat; animation: media-shimmer 1.3s linear infinite; }
html[data-theme="light"] .media.is-pending::after { background-image: linear-gradient(100deg, transparent 30%, rgb(255 255 255 / .55) 50%, transparent 70%); }
@keyframes media-shimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }
.v-spin { position: absolute; left: 50%; top: 50%; z-index: 4; width: 34px; height: 34px; margin: -17px 0 0 -17px; border-radius: 50%; border: 3px solid rgb(255 255 255 / .28); border-top-color: #fff; pointer-events: none; opacity: 0; transition: opacity .25s var(--ease-in-out); filter: drop-shadow(0 2px 6px rgb(0 0 0 / .4)); animation: spin .8s linear infinite; animation-play-state: paused; }
.v-host.is-buffering > .v-spin { opacity: 1; animation-play-state: running; }
.hero-media > .v-spin, .season > .v-spin { display: none; } /* backdrops: their still is enough */
@media (prefers-reduced-motion: reduce) { .media.is-pending::after { animation: none; } }

/* ---------- performance (owner, 2026-10-18: fast on weak devices, little lost in quality) ----------
   Every device: a section below the first screen is not laid out or painted until it comes near (content-visibility),
   with a size estimate so the scrollbar stays steady; the hero and the first panel after it are always drawn (the
   hero's phone banner reaches into that panel). */
#main > section.section:not(:first-child):not(:nth-child(2)), #contact, .footer { content-visibility: auto; contain-intrinsic-size: auto 900px; }
html.kx-spotting #main > section.section, html.kx-spotting #contact { content-visibility: visible; } /* an admin "See it on the site" jump needs the real heights (site.js) */
/* html.lite (the head script, frameCheck() in site.js): weak devices keep the layout, the colours and the content and
   lose the costly effects: frosted glass (backdrop blur), blur and glow filters,
   most of the reaction stream, the light gliding along the arrows, the word-by-word heading animation (site.js) and
   the dot field (site.js); background videos show their still and only the header phone and the video stage play. */
html.lite *, html.lite *::before, html.lite *::after { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
html.lite .compare-arrow::before, html.lite .tt-sound-run span, html.lite .tt-disc, html.lite .badge-shine::after, html.lite .media.is-pending::after { animation: none !important; }
html.lite :is(.phone-frame, .phone-fx i, .tt-act svg.ico, .season-clock b, .flap .fl > i, .vs-act svg.ico) { filter: none !important; text-shadow: none !important; }
html.lite .phone-fx > :nth-child(n+7) { display: none; } /* three reactions instead of seven */
html.lite .topbar-inner { background: rgb(var(--c-bg) / .98); }
html.lite .topbar::before { display: none; }
/* (html.lite no longer hides the dot field: kx.field draws it as still frames there, 2026-10-25) */

/* a block scrolled out of view (.is-off, ambientZones() in site.js) pauses its own CSS animations */
.is-off, .is-off *, .is-off *::before, .is-off *::after { animation-play-state: paused !important; }

/* "Without / With" cards (waiting.php): from 768px the two cards share their rows (subgrid): label, title, then one row
   per line, so line n and its partner are equally tall and their highlights match */
@media (min-width: 768px) {
  .gap-grid { grid-template-rows: auto auto repeat(var(--rows, 4), auto); row-gap: 0; }
  .gap-grid > .gap-col { display: grid; grid-row: span calc(var(--rows, 4) + 2); grid-template-rows: subgrid; align-content: start; }
  .gap-grid .gap-list { display: contents; }
  .gap-grid .gap-list li { margin: 0 -10px 4px; }
  .gap-grid .gap-list li:first-child { margin-top: 16px; }
  .gap-grid .h3 { align-self: end; }
}

/* cards and inputs: a step rounder, like the reference */
html:not([data-division="admin"]) .field { border-radius: var(--r-input); }
html:not([data-division="admin"]) .chip-copy { font-family: var(--font-mono); }

/* ---------- admin ----------
   Same look as the public site: dark, monochrome panels a step apart in brightness, Geist, no outlines on modules
   (inputs keep theirs), no glows. The two frames (sidebar, main) are --c-bg on the darker page; cards and tables
   are --paper on top of that; wells (inputs, drop zones, code) are --c-surface2. */
html[data-division="admin"] body { background: rgb(var(--field)); }
.adm .card, .login.card { box-shadow: none; }
.adm .tile { border: 0; }
/* Moving between admin screens: every click loads a new page, and the site-wide page transition (old page slides
   out, new one rises in) made the whole frame jump, sidebar included. Here the sidebar is its own transition group
   that does not animate at all, and only the content area cross-fades. The scrollbar gutter is reserved so a short
   page and a long one sit at the same width. */
html[data-division="admin"] { scrollbar-gutter: stable; }
.adm-side { view-transition-name: adm-side; }
.adm-main { view-transition-name: adm-main; }
html[data-division="admin"]::view-transition-old(root), html[data-division="admin"]::view-transition-new(root) { animation: none; }
::view-transition-group(adm-side), ::view-transition-old(adm-side), ::view-transition-new(adm-side) { animation: none; }
::view-transition-old(adm-side) { display: none; }
::view-transition-group(adm-main) { animation: none; }
::view-transition-old(adm-main) { animation: adm-fade-out .12s var(--ease-in-out) both; }
::view-transition-new(adm-main) { animation: adm-fade-in .22s var(--ease-out) both; }
@keyframes adm-fade-out { to { opacity: 0; } }
@keyframes adm-fade-in { from { opacity: 0; } }
@media (max-width: 960px) { .adm-side, .adm-main { view-transition-name: none; } }
.vpick-find { display: block; max-width: 320px; }
.vpick-find .field { width: 100%; }
.vpick-cell[hidden] { display: none; }
.adm-side > .row:first-child { justify-content: center; padding: 14px 0 6px; }
.adm-side > .row:first-child .brand { margin-inline: auto; }
.adm .chip { border: 0; }
.adm-status { display: flex; flex-wrap: wrap; gap: 8px; margin: -6px 0 18px; }
.adm-status .chip { gap: 7px; padding: 7px 12px; background: rgb(var(--paper)); font-size: .78rem; transition: background-color var(--dur-quick); }
.adm-status .chip:hover { background: rgb(var(--c-dark2)); }
.adm-status .chip svg.ico { width: 13px; height: 13px; color: rgb(var(--c-ink3)); }
.adm-status .chip .muted { font-weight: 500; }
.adm-status .chip.is-off, .adm-status .chip.is-off svg.ico { color: rgb(var(--h-gold)); }
.adm-status span.chip:hover { background: rgb(var(--paper)); }
/* "Setup guide" next to a status that needs doing something: a chip-shaped button with an (i), and the guide itself,
   a popover in the middle of the screen (_cron-guide.php, the Connections guides): live checks first, then numbered steps with copyable lines */
.adm-status .chip-info { cursor: pointer; background: rgb(var(--c-accent) / .16); color: rgb(var(--c-accent)); }
.adm-status .chip-info:hover { background: rgb(var(--c-accent) / .26); color: rgb(var(--c-accent)); }
.chip-info i { width: 15px; height: 15px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-accent)); color: #fff; font: 600 .62rem/1 var(--font-mono); font-style: normal; }
.adm-guide { width: min(680px, calc(100vw - 24px)); max-height: min(86dvh, 860px); overflow: auto; margin: auto; padding: clamp(20px, 3vw, 32px); border: 0; border-radius: 22px; background: rgb(var(--paper)); color: rgb(var(--c-ink2)); box-shadow: 0 30px 60px -20px rgb(0 0 0 / .9); scrollbar-width: thin; }
.adm-guide::backdrop { background: rgb(0 0 0 / .6); }
.adm-guide:popover-open { animation: datepick-in .25s var(--ease-expo); }
.adm-guide-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.adm-guide-head .h2 { margin-top: 6px; }
.adm-guide-lead { margin-top: 14px; font-size: .95rem; line-height: 1.6; color: rgb(var(--c-ink3)); }
.adm-guide-lead b, .guide-steps b, .guide-checks b { color: rgb(var(--c-ink)); font-weight: 500; }
.adm-guide-sub { margin: 26px 0 12px; font-family: var(--font-mono); font-size: .68rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.adm-guide code { padding: 1px 6px; border-radius: 6px; background: rgb(var(--c-ink) / .08); font-family: var(--font-mono); font-size: .8em; color: rgb(var(--c-ink)); overflow-wrap: anywhere; }
.guide-checks { display: grid; gap: 10px; }
.guide-checks li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px; align-items: start; padding: 12px 14px; border-radius: 12px; background: rgb(var(--c-surface2)); font-size: .9rem; line-height: 1.5; }
.guide-state { margin-top: 1px; padding: 3px 8px; border-radius: 6px; font-family: var(--font-mono); font-size: .64rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
.guide-state.is-ok { background: rgb(var(--c-ok) / .16); color: rgb(var(--c-ok)); }
.guide-state.is-no { background: rgb(var(--h-gold) / .16); color: rgb(var(--h-gold)); }
.guide-steps { counter-reset: step; display: grid; gap: 16px; }
.guide-steps > li { counter-increment: step; position: relative; padding-left: 40px; font-size: .92rem; line-height: 1.6; }
.guide-steps > li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-ink) / .1); font-family: var(--font-mono); font-size: .72rem; font-weight: 600; color: rgb(var(--c-ink)); }
.guide-steps small { display: block; margin-top: 6px; font-size: .82rem; color: rgb(var(--c-muted)); }
.guide-code { margin-top: 8px; padding: 12px 14px; border-radius: 10px; background: rgb(var(--c-surface2)); font-family: var(--font-mono); font-size: .78rem; line-height: 1.5; color: rgb(var(--c-ink)); white-space: pre-wrap; overflow-wrap: anywhere; user-select: all; }
.table .cell-tags { white-space: normal; } .cell-tags .chip { margin: 2px 4px 2px 0; font-size: .68rem; }
.adm-settings > .is-wide { grid-column: 1 / -1; }
.adm-settings textarea.field { min-height: 0; line-height: 1.7; }
.adm-settings .field[data-digits] { max-width: 220px; } /* a whole number, also in a full-width card (Fortnite sync) */
.set-inline { display: flex; align-items: center; gap: 14px; } /* a link and its "Show whole link" switch on one row */
.set-inline > .field { flex: 1; min-width: 0; }
.set-inline > .toggle { flex: none; align-items: center; gap: 10px; }
.set-inline .toggle-text b { font-size: .84rem; white-space: nowrap; }
/* the tick in front of a link: its card is shown beside the contact form or not; an unticked row is dimmed */
.tick { position: relative; flex: none; display: inline-grid; cursor: pointer; }
.tick::after { content: ''; position: absolute; inset: -10px; } /* a bigger target than the box */
.tick input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.tick-box { width: 24px; height: 24px; border-radius: 7px; display: grid; place-items: center; background: rgb(var(--c-ink) / .12); color: transparent; transition: background-color var(--dur-quick), color var(--dur-quick); }
.tick-box svg.ico { width: 13px; height: 13px; }
.tick input:checked + .tick-box { background: rgb(var(--c-ok)); color: #04210c; }
.tick input:focus-visible + .tick-box { outline: 2px solid rgb(var(--c-accent)); outline-offset: 2px; }
.set-inline:has(> .tick input:not(:checked)) > :not(.tick) { opacity: .5; }
/* record forms, written for someone who does this twice a year: named blocks top to bottom, one plain sentence under
   a field, "required" spelled out, switches instead of bare checkboxes, and the save button always in view */
.adm-form-note { font-size: .88rem; color: rgb(var(--c-ink3)); }
.adm-form-note b, .req { font-family: var(--font-mono); font-size: .62rem; font-weight: 500; letter-spacing: .08em; text-transform: uppercase; padding: 2px 6px; border-radius: 5px; background: rgb(var(--c-accent) / .16); color: rgb(var(--c-accent)); }
.req { margin-left: 8px; vertical-align: 1px; }
.req.is-auto { background: rgb(var(--c-ink) / .08); color: rgb(var(--c-ink3)); }
.adm-form-sec { margin-top: 14px; padding-top: 22px; border-top: 1px solid rgb(var(--c-line)); font-size: 1.15rem; font-weight: 500; letter-spacing: -.02em; color: rgb(var(--c-ink)); }
.adm-form-note + .adm-form-sec, .adm-form > input[type=hidden] + .adm-form-sec { margin-top: 0; padding-top: 0; border-top: 0; }
.field-help { margin-top: 7px; font-size: .8rem; line-height: 1.45; color: rgb(var(--c-muted)); }
.field[readonly] { color: rgb(var(--c-ink3)); background: rgb(var(--c-ink) / .03); border-style: dashed; cursor: default; }
.toggle { display: flex; align-items: flex-start; gap: 14px; cursor: pointer; }
.toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.toggle-ui { flex: none; position: relative; width: 44px; height: 26px; margin-top: 1px; border-radius: 999px; background: rgb(var(--c-ink) / .14); transition: background-color var(--dur-base) var(--ease-in-out); }
.toggle-ui::after { content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgb(0 0 0 / .4); transition: translate .3s var(--ease-expo); }
.toggle input:checked + .toggle-ui { background: rgb(var(--c-ok)); }
.toggle input:checked + .toggle-ui::after { translate: 18px 0; }
.toggle input:focus-visible + .toggle-ui { outline: 2px solid rgb(var(--c-accent)); outline-offset: 2px; }
.toggle-text { display: grid; gap: 3px; }
.toggle-text b { font-size: .95rem; font-weight: 500; color: rgb(var(--c-ink)); }
.toggle-text small { font-size: .8rem; line-height: 1.45; color: rgb(var(--c-muted)); }
.adm-save { position: sticky; bottom: 12px; z-index: 3; display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 10px; padding: 10px; border-radius: 999px; background: rgb(var(--c-dark2) / .92); box-shadow: 0 14px 28px -14px rgb(0 0 0 / .9); justify-self: end; width: fit-content; }
.adm-save-note { padding: 0 6px 0 14px; font-size: .84rem; color: rgb(var(--c-ink3)); white-space: nowrap; }
.adm-save.is-dirty { background: rgb(60 44 12 / .96); }
.adm-save.is-dirty .adm-save-note { color: rgb(var(--h-gold)); font-weight: 500; }
.adm-save.is-dirty .adm-save-note::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%; background: rgb(var(--h-gold)); animation: pulse 1.6s ease-in-out infinite; }
.adm-save.is-dirty .btn-primary { background: rgb(var(--c-ok)); color: #04210c; box-shadow: none; animation: save-hop .6s var(--ease-overshoot) 1; }
@keyframes save-hop { 40% { translate: 0 -6px; scale: 1.05; } }
.adm-settings-save .adm-save { position: static; margin: 0; }
/* until something changes the bar is only the note "No changes yet"; the buttons appear with the first change (owner, 2026-10-18) */
form[data-shortcut-save] [data-savebar]:not(.is-dirty) .btn { display: none; }
form[data-shortcut-save] [data-savebar]:not(.is-dirty) .adm-save-note { padding-right: 14px; }
/* the one question the admin asks before leaving a changed form through its own links (admin.js): our dialog, not the browser's */
.adm-confirm { width: min(380px, calc(100vw - 24px)); margin: auto; padding: 26px; border: 0; border-radius: 22px; background: rgb(var(--paper)); color: rgb(var(--c-ink3)); box-shadow: 0 30px 60px -20px rgb(0 0 0 / .9); }
.adm-confirm::backdrop { background: rgb(0 0 0 / .55); }
.adm-confirm[open] { animation: datepick-in .2s var(--ease-expo); }
.adm-confirm .h2 { font-size: 1.3rem; }
.adm-confirm p { margin-top: 8px; font-size: .95rem; }
.adm-confirm-actions { display: grid; gap: 8px; margin-top: 20px; } /* stacked, one answer per line (owner, 2026-10-18) */
.adm-confirm-actions .btn { width: 100%; justify-content: center; }
/* ---------- admin search (owner, 2026-10-29): a quiet trigger under the logo and a Spotlight-style box ----------
   The trigger is a field-shaped button one step lighter than the sidebar (fill only, no outline), its shortcut as a small
   chip (html.mac shows ⌘K: Cmd+Space is Spotlight itself there); hover lifts the fill a step and grows the icon a little,
   a press scales it (transform and colour only, with the steady-hover strip). The box (dialog.adm-spot, admin.js) sits in
   the top layer over the dimmed page: the input on top, the results grouped under it, 44px rows, the matched letters bold. */
.adm-find { position: relative; display: flex; align-items: center; gap: 10px; width: 100%; height: 40px; margin-top: 14px; padding: 0 12px; border-radius: 10px; background: rgb(var(--paper)); color: rgb(var(--c-muted)); font-size: .88rem; font-weight: 500; text-align: left; transition: background-color var(--dur-quick), color var(--dur-quick), transform var(--dur-quick); }
.adm-find svg.ico { width: 15px; height: 15px; flex: none; transition: transform var(--dur-quick) var(--ease-out); }
.adm-find:hover { background: rgb(var(--c-dark2)); color: rgb(var(--c-ink3)); }
.adm-find:hover svg.ico { transform: scale(1.12); }
.adm-find:active { transform: scale(.985); }
.adm-find::after { content: ''; position: absolute; inset: -4px; } /* steady hover and press: a pointer on the very edge still counts */
.adm-find-text { flex: 1; min-width: 0; }
.adm-kbd { display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px; border: 0; border-radius: 6px; background: rgb(var(--c-ink) / .08); color: rgb(var(--c-muted)); font-family: var(--font-mono); font-size: .64rem; font-weight: 500; letter-spacing: .04em; white-space: nowrap; line-height: 1.5; }
html.mac .adm-kbd-pc, html:not(.mac) .adm-kbd-mac { display: none; }
.adm-find-mini { display: none; }
/* compact like Spotlight and always whole on screen: as much room below it as above, at most 520px, the list scrolls
   inside (owner, 2026-10-29: it ran past the bottom of a laptop screen, key hints and all) */
.adm-spot { width: min(640px, calc(100vw - 24px)); max-height: min(520px, calc(100dvh - 2 * min(12vh, 120px))); margin: min(12vh, 120px) auto auto; padding: 0; border: 0; border-radius: 20px; background: rgb(var(--paper)); color: rgb(var(--c-ink3)); box-shadow: 0 30px 60px -20px rgb(0 0 0 / .9); overflow: hidden; flex-direction: column; }
.adm-spot[open] { display: flex; animation: spot-in .22s var(--ease-expo); }
.adm-spot::backdrop { background: rgb(0 0 0 / .55); }
@keyframes spot-in { from { opacity: 0; transform: translateY(-10px) scale(.98); } }
.adm-spot-in { flex: none; display: flex; align-items: center; gap: 12px; padding: 14px 16px 14px 18px; border-bottom: 1px solid rgb(var(--c-line)); } /* a divider inside the one module: the input above, the results below */
.adm-spot-in > svg.ico { width: 18px; height: 18px; flex: none; color: rgb(var(--c-muted)); }
.adm-spot-input { flex: 1; min-width: 0; height: 36px; padding: 0; background: none; border: 0; outline: 0; color: rgb(var(--c-ink)); font: inherit; font-size: 1.1rem; font-weight: 500; letter-spacing: -.01em; } /* the box is the field: no border and no ring of its own */
.adm-spot-input::placeholder { color: rgb(var(--c-muted)); }
.adm-spot-input::-webkit-search-cancel-button, .adm-spot-input::-webkit-search-decoration { display: none; }
.adm-spot-esc { cursor: pointer; }
.adm-spot-list { flex: 1; min-height: 0; overflow: hidden auto; padding: 8px; scrollbar-width: thin; }
.adm-spot-list:empty { display: none; }
.adm-spot-group { margin: 10px 12px 4px; font-family: var(--font-mono); font-size: .64rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.adm-spot-group:first-child { margin-top: 4px; }
.adm-spot-row { display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 7px 12px; border-radius: 12px; color: rgb(var(--c-ink3)); }
.adm-spot-row.is-cur { background: rgb(var(--c-ink) / .08); color: rgb(var(--c-ink)); }
.adm-spot-ico { flex: none; width: 30px; height: 30px; border-radius: 9px; background: rgb(var(--c-ink) / .06); display: grid; place-items: center; color: rgb(var(--c-ink3)); }
.adm-spot-row.is-cur .adm-spot-ico { color: rgb(var(--c-accent)); }
.adm-spot-ico svg.ico { width: 15px; height: 15px; }
.adm-spot-text { flex: 1; min-width: 0; display: grid; gap: 1px; }
.adm-spot-title { font-size: .92rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-spot-title b { font-weight: 700; color: rgb(var(--c-ink)); }
.adm-spot-sub { font-size: .76rem; color: rgb(var(--c-muted)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-spot-ext { flex: none; color: rgb(var(--c-muted)); font-size: .85rem; }
.adm-spot-empty { margin: 0; padding: 18px 20px 22px; }
.adm-spot-foot { flex: none; display: flex; align-items: center; gap: 6px; margin: 0; padding: 10px 18px; border-top: 1px solid rgb(var(--c-line)); font-size: .72rem; }
.adm-spot-foot kbd { padding: 1px 6px; border-radius: 5px; background: rgb(var(--c-ink) / .08); font-family: var(--font-mono); font-size: .66rem; }
.adm-spot-foot kbd + kbd { margin-left: -3px; }
/* the settings field the search landed on (admin.js spotField(), #set-<key>): a flat accent wash around it that fades once */
.set-field { position: relative; scroll-margin-top: 28vh; } /* the browser's own jump to #set-<key> leaves the field well under the top edge too */
.set-field.is-spot::after { content: ''; position: absolute; inset: -8px -10px; border-radius: 12px; background: rgb(var(--c-accent) / .14); pointer-events: none; animation: set-spot 1.8s var(--ease-out) both; }
@keyframes set-spot { 0% { opacity: 0; } 15% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .adm-spot[open] { animation: none; } .adm-find:hover svg.ico, .adm-find:active { transform: none; } }
@media (max-width: 640px) { .adm-save { width: auto; justify-self: stretch; flex-wrap: wrap; border-radius: 22px; } .adm-save-note { flex: 1 0 100%; padding: 4px 8px 0; white-space: normal; } .adm-save .btn { flex: 1; } }
/* shortcuts from the admin to the public page (_head.php): link chips with an arrow */
.chip-go { gap: 6px; transition: background-color var(--dur-quick), color var(--dur-quick); }
.chip-go svg.ico { width: 11px; height: 11px; opacity: .7; }
.chip-go:hover { background: rgb(var(--paper)); color: rgb(var(--c-ink)); }
/* video field (_video-field.php): numbered ways to fill it, and a wall of the videos on the server to click on */
.vpick { display: grid; gap: 10px; }
.vpick-step { display: flex; align-items: center; gap: 10px; margin-top: 8px; font-size: .88rem; color: rgb(var(--c-ink2)); }
.vpick-step:first-child { margin-top: 0; }
.vpick-step b { flex: none; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-ink) / .1); font-family: var(--font-mono); font-size: .68rem; font-weight: 600; color: rgb(var(--c-ink)); }
.vpick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); grid-auto-rows: max-content; gap: 8px; max-height: 420px; overflow: auto; padding: 2px; scrollbar-width: thin; }
.vpick-item { position: relative; display: block; min-width: 0; border-radius: 12px; overflow: hidden; background: rgb(var(--c-surface2)); text-align: left; outline: 2px solid transparent; outline-offset: 0; transition: outline-color var(--dur-quick), opacity var(--dur-quick); }
.vpick-item video { display: block; width: 100%; aspect-ratio: 3 / 4; object-fit: cover; background: #000; pointer-events: none; }
/* a tile's picture: shimmer while its frame is fetched, a fade when it arrives, a plain note when there is none */
.vpick-item video { opacity: 0; transition: opacity .35s var(--ease-out); }
.vpick-item.is-ready video, html:not(.js) .vpick-item video { opacity: 1; }
.vpick-item.is-loading::before { content: ''; position: absolute; inset: 0 0 auto; aspect-ratio: 3 / 4; background: linear-gradient(100deg, transparent 30%, rgb(var(--c-ink) / .09) 50%, transparent 70%) 0 0 / 220% 100%; animation: vpick-shimmer 1.3s linear infinite; }
@keyframes vpick-shimmer { from { background-position: 120% 0; } to { background-position: -100% 0; } }
.vpick-none { position: absolute; inset: 0 0 auto; aspect-ratio: 3 / 4; display: none; place-items: center; font-family: var(--font-mono); font-size: .62rem; letter-spacing: .08em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.vpick-item.is-broken .vpick-none { display: grid; }
.vpick-grid:has(.is-on) .vpick-item:not(.is-on) { opacity: .5; }
.vpick-grid:has(.is-on) .vpick-item:not(.is-on):hover { opacity: 1; }
.vpick-item:hover { outline-color: rgb(var(--c-ink) / .35); }
.vpick-item.is-on { outline-color: rgb(var(--c-ok)); }
.vpick-check { position: absolute; top: 7px; right: 7px; width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; background: rgb(var(--c-ok)); color: #04210c; scale: 0; transition: scale .35s var(--ease-overshoot); }
.vpick-check svg.ico { width: 12px; height: 12px; }
.vpick-item.is-on .vpick-check { scale: 1; }
.vpick-name { display: grid; gap: 1px; padding: 7px 9px 8px; font-size: .74rem; font-weight: 500; line-height: 1.25; color: rgb(var(--c-ink2)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vpick-name small { font-family: var(--font-mono); font-size: .62rem; color: rgb(var(--c-muted)); }
/* delete an uploaded video (admin.js asks first): a round bin over the tile's top left, shown while the tile is hovered
   or focused, always on touch screens */
.vpick-cell { position: relative; min-width: 0; }
.vpick-cell > .vpick-item { width: 100%; }
.vpick-del { position: absolute; top: 7px; left: 7px; z-index: 2; width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; background: rgb(0 0 0 / .62); color: #fff; opacity: 0; transition: opacity var(--dur-quick), background-color var(--dur-quick); }
.vpick-del svg.ico { width: 13px; height: 13px; }
.vpick-cell:hover .vpick-del, .vpick-del:focus-visible, .vpick-cell:focus-within .vpick-del { opacity: 1; }
.vpick-del:hover { background: rgb(var(--c-err)); }
.vpick-del:disabled { opacity: .5; cursor: progress; }
@media (pointer: coarse) { .vpick-del { opacity: 1; } }
.adm-confirm-name { font-family: var(--font-mono); font-size: .8rem !important; color: rgb(var(--c-muted)); }
.adm-confirm-list { margin: 6px 0 0; padding-left: 18px; font-size: .9rem; line-height: 1.5; }
/* Numbers page (numbers.php): one line per number of the page, in the order of the site (owner, 2026-10-25): the handle,
   its name, the box (or the Live switch) with how the site shows it under it, and the arrows that move it */
.nums { grid-column: 1 / -1; display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; container-type: inline-size; } /* a size container: the lines stack by the list's own width (below) */
.num-field { display: grid; grid-template-columns: 22px minmax(120px, 210px) minmax(0, 1fr) auto auto; grid-template-areas: "handle label box plus move" "handle label note note move" "handle label err err move"; align-items: center; gap: 4px 16px; padding: 12px 14px; border-radius: 14px; background: rgb(var(--c-surface2)); }
.num-field > .num-label { align-self: center; } /* the name in the middle of its line, beside the box and the note under it (owner, 2026-10-25) */
.num-field:not(.has-error) { grid-template-areas: "handle label box plus move" "handle label note note move"; }
.num-field.is-dragging { opacity: .85; box-shadow: 0 14px 28px -16px rgb(var(--c-dark) / .6); position: relative; z-index: 2; }
.num-handle { grid-area: handle; align-self: stretch; display: grid; place-items: center; cursor: grab; color: rgb(var(--c-muted)); touch-action: none; }
.num-handle:active { cursor: grabbing; }
.num-handle svg.ico { width: 15px; height: 15px; }
.num-label { grid-area: label; font-weight: 600; font-size: .95rem; color: rgb(var(--c-ink)); }
.num-input, .num-toggle { grid-area: box; }
.num-plus { grid-area: plus; align-items: center; gap: 8px; white-space: nowrap; } /* the "+ after it" switch beside the box (owner, 2026-10-28) */
.num-plus .toggle-ui { margin-top: 0; }
.num-plus .toggle-text { font-size: .84rem; font-weight: 600; color: rgb(var(--c-ink2)); }
.num-input { font-size: 1.05rem; }
.num-note { grid-area: note; display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; color: rgb(var(--c-ink2)); }
.num-note b { color: rgb(var(--c-ink)); }
.num-field > .field-error { grid-area: err; }
.num-move { grid-area: move; display: flex; flex-direction: column; gap: 4px; }
.num-move .num-arrow { width: 30px; height: 26px; border-radius: 8px; }
.num-move svg.ico { width: 14px; height: 14px; rotate: 90deg; }
.num-field.has-error .num-input { border-color: rgb(var(--c-err)); }
/* a narrow list (a phone, or the admin beside its sidebar at about 1000-1060px): the "+ after it" switch on the name's
   line and the box the whole width under it (2026-10-28: beside the box, the switch squeezed it to two digits) */
@container (max-width: 680px) {
  .num-field { grid-template-columns: 22px minmax(0, 1fr) auto auto; grid-template-areas: "handle label plus move" "handle box box move" "handle note note move" "handle err err move"; }
  .num-field:not(.has-error) { grid-template-areas: "handle label plus move" "handle box box move" "handle note note move"; }
}
@container (max-width: 420px) { /* a phone: beside the name it ran into it, so the switch has its own line under the box */
  .num-field { grid-template-columns: 22px minmax(0, 1fr) auto; grid-template-areas: "handle label move" "handle box move" "handle plus move" "handle note move" "handle err move"; }
  .num-field:not(.has-error) { grid-template-areas: "handle label move" "handle box move" "handle plus move" "handle note move"; }
}
.btn.btn-danger { background: rgb(var(--c-err)); color: #fff; }
.btn.btn-danger:hover { filter: brightness(1.08); }
.dropzone.is-set { border-color: rgb(var(--c-ok)); color: rgb(var(--c-ink)); }
.dropzone.is-busy { border-color: rgb(var(--c-accent)); color: rgb(var(--c-ink)); pointer-events: none; }
/* date fields (admin.js builds them from input[type=date]): a field-shaped button with the date in words and a
   calendar icon at its right edge, and our own calendar as a floating menu, the same surface as the dropdown list */
.datepick { position: relative; }
.datepick-btn { display: flex; align-items: center; justify-content: space-between; gap: 12px; text-align: left; cursor: pointer; }
.datepick-btn.is-empty span { color: rgb(var(--c-muted)); }
.datepick-btn svg.ico { flex: none; width: 15px; height: 15px; color: rgb(var(--c-muted)); transition: color var(--dur-quick); }
.datepick-btn:hover svg.ico, .datepick-btn:focus-visible svg.ico { color: rgb(var(--c-ink)); }
.datepick-pop { position: fixed; inset: auto; margin: 0; width: 280px; padding: 12px; border: 0; border-radius: 16px; background: rgb(var(--pop)); color: rgb(var(--c-ink2)); box-shadow: 0 18px 34px -12px rgb(0 0 0 / .8), 0 2px 6px rgb(0 0 0 / .35); }
.datepick-pop:popover-open { animation: datepick-in .22s var(--ease-expo); }
@keyframes datepick-in { from { opacity: 0; translate: 0 -6px; } }
.datepick-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.datepick-head b { font-size: .92rem; font-weight: 500; letter-spacing: -.01em; color: rgb(var(--c-ink)); }
.datepick-head button { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; font-size: 1.15rem; line-height: 1; color: rgb(var(--c-ink2)); transition: background-color var(--dur-quick), color var(--dur-quick); }
.datepick-week, .datepick-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.datepick-week { margin-top: 10px; }
.datepick-week span { padding: 4px 0; text-align: center; font-family: var(--font-mono); font-size: .62rem; letter-spacing: .06em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.datepick-days button { aspect-ratio: 1; border-radius: 9px; font-size: .84rem; font-variant-numeric: tabular-nums; color: rgb(var(--c-ink2)); transition: background-color var(--dur-quick), color var(--dur-quick); }
.datepick-head button:hover, .datepick-days button:hover, .datepick-foot button:hover, .datepick-pop button:focus-visible { background: rgb(var(--c-ink) / .1); color: rgb(var(--c-ink)); outline: none; }
.datepick-days .is-today { box-shadow: inset 0 0 0 1px rgb(var(--c-ink) / .35); color: rgb(var(--c-ink)); }
.datepick-days .is-on, .datepick-days .is-on:hover { background: rgb(var(--c-accent)); color: #fff; font-weight: 600; box-shadow: none; }
.datepick-foot { display: flex; justify-content: space-between; margin-top: 10px; padding-top: 10px; border-top: 1px solid rgb(var(--c-ink) / .08); }
.datepick-foot button { padding: 6px 10px; border-radius: 8px; font-size: .8rem; font-weight: 500; color: rgb(var(--c-ink2)); transition: background-color var(--dur-quick), color var(--dur-quick); }
/* without JavaScript the native date box stays: at least keep its icon and popup on the dark side */
input[type=date].field { color-scheme: dark; }
/* full-window on desktop: sidebar + a main area that uses the whole width, so cards sit side by side */
.adm { display: grid; grid-template-columns: 270px 1fr; min-height: 100dvh; gap: 12px; padding: 12px; }
.adm-side { position: sticky; top: 12px; align-self: start; height: calc(100dvh - 24px); display: flex; flex-direction: column; padding: 20px; border-radius: 22px; background: rgb(var(--c-bg)); box-shadow: var(--sh-sheet); }
.adm-main { border-radius: 22px; background: rgb(var(--c-bg)); box-shadow: var(--sh-sheet); padding: clamp(18px, 3vw, 36px); min-width: 0; }
/* kree8 sidebar rows: 54px, icon + 16px medium label, hover opacity .7 */
/* "By task / By page" toggle above the menu; only the nav matching html[data-navmode] is shown */
.adm-mode { display: flex; margin-top: 16px; padding: 3px; }
.adm-mode .switch-item { flex: 1; padding: 6px 10px; font-size: .78rem; text-align: center; }
.adm-navs { flex: 1; min-height: 0; overflow: hidden auto; margin: 22px -8px 0; padding: 0 8px 8px; scrollbar-width: thin; }
html[data-navmode="page"] .adm-nav[data-navmode="task"], html:not([data-navmode="page"]) .adm-nav[data-navmode="page"] { display: none; }
.adm-nav { position: relative; width: 100%; display: flex; flex-direction: column; align-items: stretch; gap: 1px; background: none; border: 0; padding: 0; border-radius: 0; }
.adm-nav .switch-thumb { background: rgb(var(--paper)); border-radius: 10px; }
.adm-nav .switch-item { border-radius: 10px; padding: 0 12px; height: 38px; font-size: .9rem; display: flex; align-items: center; justify-content: flex-start; gap: 10px; white-space: nowrap; color: rgb(var(--c-ink3)); font-weight: 500; }
.adm-nav .switch-item .ico { display: block; width: 16px; height: 16px; margin: 0; }
.adm-nav .nav-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.adm-nav .switch-item.is-on { color: rgb(var(--c-ink)); font-weight: 600; }
.adm-nav .switch-item.is-on .ico { color: rgb(var(--c-accent)); }
.adm-nav-group { margin: 14px 12px 4px; font-family: var(--font-mono); font-size: .66rem; font-weight: 500; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.nav-badge { flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px; background: rgb(var(--c-accent)); color: #fff; font-size: .7rem; font-weight: 700; display: inline-grid; place-items: center; }
/* entity page header (views/admin/_head.php) */
.adm-crumb { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; color: rgb(var(--c-muted)); }
.adm-crumb a { color: rgb(var(--c-ink3)); font-weight: 600; transition: opacity var(--dur-quick); }
.adm-crumb a:hover { opacity: .7; }
.adm-crumb [aria-current] { color: rgb(var(--c-ink2)); }
/* Back, in front of the breadcrumb on every admin subpage: a small pill one level up */
.adm-crumb a.adm-back { display: inline-flex; align-items: center; gap: 6px; margin-right: 8px; padding: 5px 12px 5px 8px; border-radius: 999px; background: rgb(var(--c-ink) / .08); color: rgb(var(--c-ink)); }
.adm-crumb a.adm-back:hover { opacity: 1; background: rgb(var(--c-ink) / .14); }
.adm-back svg.ico { width: 14px; height: 14px; }
.adm-title { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
.adm-title-ico { width: 40px; height: 40px; flex: none; border-radius: 12px; display: grid; place-items: center; background: rgb(var(--paper)); color: rgb(var(--c-accent)); }
.adm-title-ico .ico { width: 19px; height: 19px; }
.adm-hint { margin-top: 10px; max-width: 62ch; font-size: .92rem; color: rgb(var(--c-ink3)); }
.adm-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 12px 0 22px; }
.adm-meta:empty { display: none; }
.chip-game { color: rgb(var(--h-violet)); }
.chip-social { color: rgb(var(--h-teal)); }
.chip-sync { background: rgb(var(--c-ok) / .1); color: rgb(var(--c-ok)); border-color: transparent; }
.chip-live.chip-sync::before { order: 1; margin-left: 2px; } /* "Synced ..." chip on the games page: icon, text, then the pulsing dot on the right */
.adm-search { position: relative; display: block; }
.adm-search .ico { position: absolute; left: 12px; top: 50%; translate: 0 -50%; color: rgb(var(--c-muted)); pointer-events: none; }
.adm-search .field { padding: 9px 12px 9px 34px; width: min(240px, 60vw); font-size: .88rem; }
.adm-record { display: flex; align-items: center; gap: 18px; }
.adm-record img { width: 160px; aspect-ratio: 16 / 9; height: auto; object-fit: cover; border-radius: 10px; background: rgb(var(--c-surface2)); flex: none; }
.adm-record-title { font-weight: 600; font-size: 1.1rem; letter-spacing: -.015em; color: rgb(var(--c-ink)); margin-top: 2px; }
/* settings: one form split into topic cards, save button stays in view while scrolling */
.adm-settings { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr)); gap: 16px; align-items: start; }
.adm-settings-save { grid-column: 1 / -1; }
/* record form: fields flow into columns on wide screens; long fields (textarea, image, checkbox) span the full row */
/* rows edited inside a form (form.php: a case study's figures, a client's maps): one line per row, a header line with
   the column names, Remove on the right; stacked on narrow screens */
.adm-kids { display: grid; gap: 8px; }
.adm-kid { display: grid; grid-template-columns: 20px repeat(var(--kid-cols, 3), minmax(0, 1fr)) auto 88px; gap: 10px; align-items: center; padding: 8px; border-radius: 12px; background: rgb(var(--c-surface2)); }
.adm-kid.is-dragging { opacity: .85; box-shadow: 0 14px 28px -16px rgb(var(--c-dark) / .6); position: relative; z-index: 2; }
.adm-kid-handle { display: grid; place-items: center; align-self: stretch; cursor: grab; color: rgb(var(--c-muted)); touch-action: none; }
.adm-kid-handle svg.ico { width: 14px; height: 14px; }
.adm-kid-move { display: flex; gap: 4px; }
.adm-kid-move .num-arrow { width: 28px; height: 28px; border-radius: 8px; }
.adm-kid-move svg.ico { width: 13px; height: 13px; rotate: 90deg; }
.adm-kid-head { padding-block: 0; background: none; font-size: .74rem; font-weight: 600; color: rgb(var(--c-muted)); }
.adm-kid-head span { padding-left: 4px; }
.adm-kid[data-kid-new] { background: rgb(var(--c-surface2) / .5); }
.adm-kid-rm { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; cursor: pointer; justify-self: end; }
.adm-kid-img { display: flex; align-items: center; gap: 8px; min-width: 0; }
.adm-kid-img img { flex: none; width: 64px; height: 36px; border-radius: 6px; object-fit: cover; }
.adm-kid-img input { min-width: 0; font-size: .78rem; }
.adm-kid-add { justify-self: start; }
@media (max-width: 760px) { .adm-kid { grid-template-columns: 1fr; } .adm-kid-head, .adm-kid-handle { display: none; } .adm-kid-rm { justify-self: start; } }
.adm-form { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); gap: 18px 22px; align-items: start; }
.adm-form > .is-wide { grid-column: 1 / -1; }
.field-err { margin-top: 6px; font-size: .78rem; color: rgb(var(--c-err)); }
.field:user-invalid, .field[aria-invalid="true"], .num:has(.field:user-invalid) { border-color: rgb(var(--c-err)); }
.adm-set-head { display: flex; align-items: center; gap: 14px; }
.adm-set-title { font-weight: 600; font-size: 1.05rem; letter-spacing: -.015em; color: rgb(var(--c-ink)); }
.adm-settings-save { position: sticky; bottom: 12px; z-index: 2; display: flex; justify-content: flex-end; }
.adm-settings + .adm-settings-save { margin-top: 16px; }
[data-savebar].is-nudge { animation: adm-nudge .45s var(--ease-in-out); }
@keyframes adm-nudge { 20%, 60% { translate: -6px 0; } 40%, 80% { translate: 6px 0; } }
/* settings tabs: one row of pills, the open one raised like the segmented switch; scrolls sideways on narrow screens */
.adm-tabs { display: flex; gap: 4px; width: fit-content; max-width: 100%; padding: 4px; overflow-x: auto; scrollbar-width: none; border-radius: 14px; background: rgb(var(--c-surface2)); }
.adm-tab { position: relative; display: inline-flex; align-items: center; gap: 8px; flex: none; padding: 8px 14px; border-radius: 10px; font-size: .86rem; color: rgb(var(--c-ink3)); text-decoration: none; transition: background-color var(--dur-quick), color var(--dur-quick); }
.adm-tab .ico { width: 16px; height: 16px; opacity: .6; }
.adm-tab:hover { color: rgb(var(--c-ink)); }
.adm-tab.is-on { color: rgb(var(--c-ink)); font-weight: 500; background: rgb(var(--paper)); box-shadow: 0 1.67px 4.18px rgb(var(--shade) / .31), 0 8px 10px -6px rgb(var(--shade) / .5); }
.adm-tab.is-on .ico { opacity: 1; }
.adm-tab:focus-visible { outline: 2px solid rgb(var(--c-accent)); outline-offset: -2px; }
.adm-tab-dot { width: 7px; height: 7px; border-radius: 50%; background: rgb(var(--c-err)); }
@media (pointer: coarse) { .adm-tab { min-height: 44px; } }
.adm-set-head .adm-set-guide { margin-left: auto; flex: none; align-self: flex-start; }
.adm-title-ico svg { width: 20px; height: 20px; }
/* settings sections that share one card (a 'group', Admin::SETTING_GROUPS: the video platforms): one part per platform,
   a hairline between them (two things inside one module); side by side once the card is wide enough, every part's
   heading on one row (subgrid) so the key boxes start on one line; stacked below that */
.adm-set-group { container-type: inline-size; }
.adm-set-group > .adm-set-parts { margin-top: 26px; }
.adm-set-parts { display: grid; }
.adm-set-part > .adm-set-head { align-items: flex-start; } /* the platforms' names on one line, whatever their text's length */
.adm-set-part > .adm-set-body { margin-top: 16px; }
.adm-set-part + .adm-set-part { margin-top: 22px; padding-top: 22px; border-top: 1px solid rgb(var(--c-line)); }
@container (min-width: 860px) {
  .adm-set-parts { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-rows: auto auto; row-gap: 16px; }
  .adm-set-part { display: grid; grid-row: span 2; grid-template-rows: subgrid; align-content: start; padding-inline: 24px; }
  .adm-set-part:first-child { padding-left: 0; }
  .adm-set-part:last-child { padding-right: 0; }
  .adm-set-part > .adm-set-body { margin-top: 0; }
  .adm-set-part + .adm-set-part { margin-top: 0; padding-top: 0; border-top: 0; border-left: 1px solid rgb(var(--c-line)); }
}
@container (max-width: 480px) { /* phones: Setup guide under the text, not squeezing it into a narrow column beside it */
  .adm-set-part > .adm-set-head { flex-wrap: wrap; row-gap: 10px; }
  .adm-set-part > .adm-set-head > div { flex: 1 1 calc(100% - 54px); min-width: 0; }
  .adm-set-part .adm-set-guide { margin-left: 54px; }
}
/* Social Stats Tracker: copyable addresses, saved-key state, connect buttons, invite links */
.copy-row { display: flex; align-items: center; gap: 8px; }
.copy-row .guide-code { flex: 1; min-width: 0; margin: 0; }
.secret-state code { padding: 1px 6px; border-radius: 6px; background: rgb(var(--c-ink) / .08); font-family: var(--font-mono); font-size: .9em; color: rgb(var(--c-ink)); }
.secret-state.is-saved { color: rgb(var(--c-ok)); }
.secret-state.is-unreadable { color: rgb(var(--c-err)); }
.conn-actions-row { display: flex; flex-wrap: wrap; gap: 8px; }
.btn-platform { display: inline-grid; place-items: center; width: 16px; height: 16px; }
.btn-platform svg { width: 16px; height: 16px; }
.invite-form { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) auto; gap: 12px; align-items: end; }
@media (max-width: 640px) { .invite-form { grid-template-columns: minmax(0, 1fr); } }
.invite-new { display: grid; gap: 8px; padding: 14px; border-radius: 14px; background: rgb(var(--c-surface2)); }
.invite-new .label { margin: 0; }
.invite-list { display: grid; gap: 8px; }
.invite-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 12px; border-radius: 12px; background: rgb(var(--c-surface2)); }
.invite-list .conn-ico { width: 32px; height: 32px; border-radius: 10px; }
.invite-list .conn-ico svg { width: 16px; height: 16px; }
.invite-who { flex: 1; min-width: 160px; }
/* public /connect/<token> pages (sign-in card look) */
.connect-ico { width: 48px; height: 48px; border-radius: 14px; display: grid; place-items: center; background: rgb(var(--c-surface2)); color: rgb(var(--c-ink)); }
.connect-ico svg { width: 24px; height: 24px; }
.connect-ico.is-ok { color: rgb(var(--c-ok)); }
.connect-list { display: grid; gap: 10px; }
.connect-list li { line-height: 1.5; color: rgb(var(--c-ink3)); }
.connect-list b { color: rgb(var(--c-ink)); font-weight: 500; }
/* dashboard: tables grouped like the sidebar */
.adm-groups { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 18px 14px; }
.adm-group-tiles { display: grid; gap: 6px; }
.adm-tile { display: flex; align-items: center; gap: 10px; padding: 10px 12px; transition: background var(--dur-quick); }
.adm-tile { border: 0; }
.adm-tile:hover { background: rgb(var(--paper)); }
.adm-tile-ico { color: rgb(var(--c-accent)); display: grid; }
.adm-tile-ico .ico { width: 16px; height: 16px; }
.adm-tile-label { flex: 1; min-width: 0; font-size: .875rem; font-weight: 600; color: rgb(var(--c-ink)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.adm-foot { margin-top: auto; padding-top: 14px; border-top: 1px solid rgb(var(--c-line)); display: flex; align-items: center; justify-content: space-between; font-size: .85rem; }
.adm-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 22px; }
.adm-burger { display: none; }
.table-wrap { --row: rgb(var(--paper)); --row-hover: color-mix(in srgb, rgb(var(--paper)), rgb(var(--c-ink)) 5%); overflow-x: auto; border-radius: var(--r-card); background: var(--row); }
.table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.table th { text-align: left; padding: 12px 14px; font-family: var(--font-mono); font-size: .68rem; letter-spacing: .09em; text-transform: uppercase; color: rgb(var(--c-muted)); font-weight: 500; border-bottom: 1px solid rgb(var(--c-line)); white-space: nowrap; }
.table td { padding: 12px 14px; border-top: 1px solid rgb(var(--c-line) / .7); vertical-align: middle; max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One highlight per row: the row and its sticky Edit / Delete cell (which needs its own opaque fill to cover what scrolls
   under it) change colour with the same transition, so they light up as one piece and not one after the other. */
.table tbody tr, .table tbody .actions { transition: background-color var(--dur-quick) var(--ease-in-out); }
.table tbody tr { background-color: var(--row); } /* the same resting colour as the actions cell, so both fade between the same two colours */
.table tbody tr:hover { background-color: var(--row-hover); }
/* actions column floats on the right edge of the scroll box, so Edit / Delete stay visible on wide tables */
.table .actions { text-align: right; white-space: nowrap; position: sticky; right: 0; background-color: var(--row); box-shadow: -14px 0 14px -12px rgb(var(--shade) / .55); }
.table th:last-child { position: sticky; right: 0; background: var(--row); }
.table tbody tr:hover .actions { background-color: var(--row-hover); }
.table .actions a, .table .actions button { display: inline-flex; align-items: center; font-weight: 600; font-size: .85rem; padding: 6px 10px; border-radius: 8px; transition: background var(--dur-quick), opacity var(--dur-quick); }
.table .actions a { color: rgb(var(--c-accent)); }
.table .actions a:hover { opacity: .7; }
.table .actions button { color: rgb(var(--c-err)); }
.table .actions button:hover { background: rgb(var(--c-err) / .08); }
.table tr[data-href] { cursor: pointer; }
.table .cell-title { font-weight: 600; color: rgb(var(--c-ink)); }
.table .thumb-cell { width: 78px; min-width: 78px; padding-right: 0; }
.thumb-cell img, .thumb-empty { width: 64px; height: 40px; max-width: none; border-radius: 8px; object-fit: cover; background: rgb(var(--c-surface2)); }
.thumb-empty { display: grid; place-items: center; color: rgb(var(--c-muted)); }
.thumb-empty .ico { width: 18px; height: 18px; opacity: .7; } /* no picture yet: the entry's icon, not letters (owner, 2026-10-18) */
.thumb-cell.is-round img, .thumb-cell.is-round .thumb-empty { width: 40px; height: 40px; border-radius: 50%; margin-inline: 12px; } /* a creator's profile picture */
.stat-mini { padding: 16px 18px; display: block; }
.stat-mini .stat-val { font-size: 1.9rem; }
.stat-mini canvas { height: 36px; width: 100%; margin-top: 10px; }
.dropzone { border: 1.5px dashed rgb(var(--c-line)); border-radius: var(--r-input); padding: 16px; text-align: center; font-size: .85rem; color: rgb(var(--c-muted)); transition: border-color var(--dur-base), background var(--dur-base); cursor: pointer; position: relative; background: rgb(var(--c-surface2)); }
.dropzone:hover, .dropzone.is-over { border-color: rgb(var(--c-accent)); background: rgb(var(--c-accent) / .04); }
.dropzone input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.dropzone img { max-height: 120px; margin: 0 auto 10px; border-radius: 8px; }
/* logo uploads (Settings): logos for the light Social Media page are previewed on a light backdrop, whatever the admin's theme */
.dropzone-logo img { max-width: 100%; object-fit: contain; border-radius: 6px; }
.dropzone-logo.is-onlight { background: #f4f4f5; color: #52525b; }
/* Settings pictures (kind 'picture'): the square ones previewed as a small square, the share picture in its 1200 x 630 shape */
.dropzone-pic img { width: 96px; height: 96px; object-fit: cover; border-radius: 14px; }
.dropzone-pic.is-wide img { width: min(100%, 360px); height: auto; aspect-ratio: 1200 / 630; border-radius: 10px; }
.adm-sitemap-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 10px; }
.dropzone-logo.is-onlight img { background: #f4f4f5; }
.login-wrap { min-height: 100dvh; display: grid; place-items: center; padding: 20px; }
.login { width: 100%; max-width: 400px; padding: 32px; border-radius: 24px; background: rgb(var(--c-bg)); box-shadow: var(--sh-sheet); }
.skeleton { position: relative; overflow: hidden; background: rgb(var(--c-surface2)); border-radius: 8px; color: transparent !important; }
.skeleton::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgb(var(--c-ink) / .08), transparent); transform: translateX(-100%); animation: shimmer 1.4s infinite; }
@keyframes shimmer { to { transform: translateX(100%); } }
.sync-log { font-family: var(--font-mono); font-size: .78rem; white-space: pre-wrap; background: rgb(var(--c-surface2)); border-radius: 12px; padding: 12px 14px; max-height: 200px; overflow: auto; }
@media (max-width: 960px) {
  .adm { grid-template-columns: 1fr; padding-top: 0; }
  .adm-side { position: static; height: auto; border-radius: 0 0 22px 22px; padding: 14px 16px; }
  .adm-navs, .adm-foot { display: none; }
  .adm-side.is-open .adm-foot { display: flex; }
  .adm-side.is-open .adm-navs { display: block; overflow: visible; }
  .adm-burger { display: inline-grid; }
  /* the search: the small button beside the menu button stands in for the trigger under the logo (admin.js opens the same box) */
  .adm-find { display: none; }
  .adm-find-mini { display: inline-grid; margin-right: 8px; }
  .adm-spot { width: min(640px, calc(100vw - 16px)); max-height: min(560px, calc(100dvh - 16px)); margin-top: 8px; border-radius: 18px; }
  .adm-spot-foot { display: none; }
}

/* ---------- admin: Data & sync panel, headline number form ---------- */
[data-show-when][hidden] { display: none; }
.calc-preview { margin-top: 10px; padding: 10px 14px; border-radius: 10px; background: rgb(var(--c-surface2)); font-size: .88rem; color: rgb(var(--c-ink3)); }
.calc-preview b { font-family: var(--font-mono); font-weight: 600; color: rgb(var(--c-ink)); }
.calc-preview span { color: rgb(var(--c-muted)); }
.calc-preview.is-empty { color: rgb(var(--c-muted)); }
/* Workflow steps → Own HTML: the code box and, under it, the screen at its size on a computer (400 × 250, as .sc-mock) */
.field-code { min-height: 220px; line-height: 1.5; tab-size: 2; white-space: pre; overflow-x: auto; }
.step-preview { display: grid; gap: 8px; justify-items: start; margin-top: 14px; }
.step-preview-screen { position: relative; display: block; width: min(100%, 400px); aspect-ratio: 16 / 10; border-radius: 16px; overflow: hidden; background: #0d0d0d; box-shadow: inset 0 3px 12px rgb(0 0 0 / .8); }
.step-preview-screen iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.dsync-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 14px; }
.dsync-head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.dsync .chip.is-warn { background: rgb(var(--h-gold) / .14); color: rgb(var(--h-gold)); border-color: transparent; }
.dsync .chip-info { cursor: pointer; background: rgb(var(--c-accent) / .16); color: rgb(var(--c-accent)); border-color: transparent; }
.dsync-st { gap: .5rem; flex: none; white-space: nowrap; }
.dsync-st.is-ok { background: rgb(var(--c-ok) / .12); color: rgb(var(--c-ok)); border-color: transparent; }
.dsync-st.is-warn { background: rgb(var(--h-gold) / .14); color: rgb(var(--h-gold)); border-color: transparent; }
.dsync-st.is-bad { background: rgb(var(--c-err) / .14); color: rgb(var(--c-err)); border-color: transparent; }
.dsync-st .muted { font-weight: 500; color: inherit; opacity: .7; }
.dsync-tasks { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 12px; }
.dsync-task { display: flex; flex-direction: column; gap: 8px; padding: 16px; border-radius: 16px; background: rgb(var(--c-surface2)); min-width: 0; }
.dsync-task-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.dsync-task h3 { font-size: 1rem; font-weight: 500; letter-spacing: -.01em; }
.dsync-task .field-help { margin-top: 0; }
.dsync-sum { font-size: .82rem; line-height: 1.45; color: rgb(var(--c-ink2)); overflow-wrap: anywhere; }
.dsync-task-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: auto; padding-top: 4px; }
.dsync-log summary { cursor: pointer; font-size: .8rem; color: rgb(var(--c-muted)); }
.dsync-log .sync-log { margin-top: 8px; background: rgb(var(--paper)); }
.dsync-auto-list { display: grid; gap: 10px; max-width: 560px; }
.dsync-auto-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.dsync-auto-row .field { width: 160px; flex: none; }
.dsync-auto .adm-save { margin-top: 14px; }
.dsync-nums { color: rgb(var(--c-muted)); }
.dsync-runs td { font-size: .84rem; }
@media (max-width: 520px) { .dsync-auto-row { flex-wrap: wrap; } .dsync-auto-row .field { width: 100%; } }

/* ---------- admin: live videos and accounts (post link, pulled numbers, connections) ---------- */
.postlink-in { position: relative; }
.postlink-ico { position: absolute; left: 14px; top: 50%; translate: 0 -50%; width: 18px; height: 18px; display: none; color: rgb(var(--c-ink2)); pointer-events: none; }
.postlink-ico.is-on { display: block; }
.postlink-ico svg { display: block; width: 100%; height: 100%; }
.postlink-in .field.has-ico { padding-left: 42px; }
.postlink-status { margin-top: 9px; font-size: .84rem; line-height: 1.45; color: rgb(var(--c-ink3)); }
.postlink-status.is-ok { color: rgb(var(--c-ok)); }
.postlink-status.is-warn { color: rgb(var(--h-gold)); }
.postlink-status.is-bad { color: rgb(var(--c-err)); }
.postlink-status a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.postlink-card { display: flex; align-items: center; gap: 14px; margin-top: 10px; padding: 10px 14px 10px 10px; border-radius: 14px; background: rgb(var(--c-surface2)); max-width: 520px; }
.postlink-card[hidden] { display: none; }
.postlink-card img { width: 54px; height: 96px; flex: none; border-radius: 8px; object-fit: cover; background: rgb(var(--paper)); }
.postlink-card img[hidden] { display: none; }
.postlink-card div { display: grid; gap: 3px; min-width: 0; }
.postlink-card b { font-size: .95rem; font-weight: 500; color: rgb(var(--c-ink)); overflow-wrap: anywhere; }
.postlink-card span { font-size: .8rem; color: rgb(var(--c-muted)); }
.seg-hint { display: inline-flex; align-items: center; gap: 6px; margin: 10px 0 0 12px; font-size: .8rem; color: rgb(var(--h-gold)); vertical-align: middle; }
.seg-hint[hidden] { display: none; }
.seg-hint svg.ico { width: 13px; height: 13px; }
.live-nums { padding: 16px 18px; border-radius: 14px; background: rgb(var(--c-surface2)); }
.live-nums .label { margin-bottom: 10px; }
.live-nums-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 12px 20px; }
.live-nums-grid dt { font-family: var(--font-mono); font-size: .64rem; letter-spacing: .08em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.live-nums-grid dd { margin: 4px 0 0; font-family: var(--font-mono); font-size: 1.35rem; font-weight: 500; font-variant-numeric: tabular-nums; color: rgb(var(--c-ink)); }
.live-nums-when { display: flex; align-items: center; gap: 7px; margin-top: 14px; font-size: .82rem; color: rgb(var(--c-ink3)); }
.live-nums-when svg.ico { width: 13px; height: 13px; opacity: .7; }
.live-nums-when.is-none { color: rgb(var(--c-muted)); }
.live-nums-err { margin-top: 8px; font-size: .84rem; line-height: 1.45; color: rgb(var(--c-err)); overflow-wrap: anywhere; }
.chip-err { background: rgb(var(--c-err) / .14); color: rgb(var(--c-err)); border-color: transparent; }
.chip-err::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.table .chip .muted { font-weight: 500; color: inherit; opacity: .7; }
.chip-studio { background: rgb(var(--c-accent) / .16); color: rgb(var(--c-accent)); border-color: transparent; }
.cell-plat { display: inline-flex; align-items: center; gap: 8px; }
.cell-plat svg, .cell-plat-ico svg { width: 16px; height: 16px; flex: none; }
.cell-plat-ico { display: inline-flex; vertical-align: -3px; margin-right: 8px; color: rgb(var(--c-ink3)); }
.adm-title-ico svg:not(.ico) { width: 19px; height: 19px; }
.conn-list { display: grid; gap: 10px; }
.conn-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 16px; align-items: center; padding: 16px; border-radius: 16px; background: rgb(var(--c-surface2)); }
.conn-row.is-later { opacity: .6; }
.conn-ico { width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center; background: rgb(var(--paper)); color: rgb(var(--c-ink)); }
.conn-ico svg { width: 20px; height: 20px; }
.conn-top { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; }
.conn-top h3 { font-size: 1rem; font-weight: 500; letter-spacing: -.01em; }
.conn-row .field-help { margin-top: 4px; }
.conn-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 6px; color: rgb(var(--c-ink3)); }
.conn-meta:empty { display: none; }
.conn-err { color: rgb(var(--c-err)); overflow-wrap: anywhere; }
.conn-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
@media (max-width: 640px) { .conn-row { grid-template-columns: auto minmax(0, 1fr); } .conn-actions { grid-column: 1 / -1; justify-content: flex-start; } }

/* ---------- motion safety ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
  .js .reveal { opacity: 1; translate: 0 0; }
  .marquee-track { animation: none; flex-wrap: wrap; width: auto; }
}

/* ---------- admin: followed creators and new maps to review ---------- */
.btn-quiet { background: transparent; color: rgb(var(--c-ink3)); }
.btn-quiet:hover { color: rgb(var(--c-ink)); background: rgb(var(--c-surface2)); }
.row-review .btn.btn-xs { padding: 4px 11px; font-size: .74rem; min-height: 0; line-height: 1.3; gap: 5px; }
.review-list { display: grid; gap: 10px; }
.review-item { display: grid; grid-template-columns: 208px minmax(0, 1fr) auto; gap: 18px; align-items: center; padding: 14px; border-radius: 16px; background: rgb(var(--c-surface2)); }
.review-thumb { aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: rgb(var(--paper)); display: grid; place-items: center; }
.review-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.review-nothumb { color: rgb(var(--c-muted)); }
.review-nothumb svg { width: 30px; height: 30px; }
.review-main { display: grid; gap: 6px; min-width: 0; }
.review-main h3 { font-size: 1.05rem; font-weight: 500; letter-spacing: -.01em; overflow-wrap: anywhere; }
.review-nums { display: flex; flex-wrap: wrap; gap: 6px 22px; margin: 2px 0 0; }
.review-nums dt { font-family: var(--font-mono); font-size: .62rem; letter-spacing: .08em; text-transform: uppercase; color: rgb(var(--c-muted)); }
.review-nums dd { margin: 2px 0 0; font-family: var(--font-mono); font-size: 1.1rem; font-weight: 500; font-variant-numeric: tabular-nums; color: rgb(var(--c-ink)); }
.review-links { display: flex; flex-wrap: wrap; gap: 4px 16px; }
.review-actions { display: grid; gap: 6px; justify-items: stretch; }
.review-actions .btn { width: 100%; justify-content: center; white-space: nowrap; }
@media (max-width: 900px) { .review-item { grid-template-columns: 140px minmax(0, 1fr); } .review-actions { grid-column: 1 / -1; grid-template-columns: repeat(3, auto); justify-content: start; } }
@media (max-width: 520px) { .review-item { grid-template-columns: 1fr; } .review-actions { grid-template-columns: 1fr 1fr; } .review-actions form:first-child { grid-column: 1 / -1; } }
.creators-list { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 8px; }
.creators-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 16px; padding: 12px 16px; border-radius: 14px; background: rgb(var(--c-surface2)); }
.creators-id { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; min-width: 0; }
.creators-state { margin: 0; overflow-wrap: anywhere; }
.creators-state.is-bad { color: rgb(var(--c-err)); }
.creators-foot { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-new { background: rgb(var(--c-accent) / .16); color: rgb(var(--c-accent)); border-color: transparent; }
.chip-dismissed { background: rgb(var(--c-surface2)); color: rgb(var(--c-muted)); border-color: transparent; }
.row-review { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 8px; }
.row-review .btn { white-space: nowrap; }
.row-review form { margin: 0; }
.table td:has(.row-review) { min-width: 230px; }

/* the headline's "Colour" tool (admin.js, settings.php data-two-tone): two small buttons under the box and the headline
   as the site will colour it */
.hl-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.hl-preview { flex-basis: 100%; margin: 4px 0 0; font-weight: 700; text-transform: uppercase; letter-spacing: .01em; color: rgb(var(--c-ink)); }
.hl-preview .hl { color: rgb(var(--accent-text)); }

/* the Story editor of a case study (form.php [data-richtext], admin.js): the toolbar on top, then the box written in as
   on the page. The blocks (pictures, videos, before / after) are single pieces with a remove button; a video and a
   before / after show what they hold from their data- values, so nothing is loaded while writing. */
.rte { border: 1px solid rgb(var(--c-line)); border-radius: var(--r-input); background: rgb(var(--c-surface2)); overflow: hidden; }
.rte.is-busy { opacity: .7; pointer-events: none; }
.rte-bar { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px; border-bottom: 1px solid rgb(var(--c-line)); background: rgb(var(--paper)); }
.rte-block { width: auto; min-width: 130px; padding-block: 7px; font-size: .84rem; }
.rte-btn { display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 0 11px; border: 0; border-radius: 9px; background: rgb(var(--c-ink) / .06); color: rgb(var(--c-ink)); font-size: .82rem; font-weight: 600; cursor: pointer; }
.rte-btn:hover { background: rgb(var(--c-ink) / .12); }
.rte-btn svg.ico { width: 14px; height: 14px; }
.rte-sep { width: 1px; height: 22px; margin-inline: 4px; background: rgb(var(--c-line)); }
.rte-area { min-height: 340px; max-height: 70vh; overflow-y: auto; padding: 18px 20px; outline: none; font-size: .98rem; line-height: 1.65; color: rgb(var(--c-ink)); }
.rte-area:focus-visible { box-shadow: inset 0 0 0 2px rgb(var(--c-accent) / .5); }
.rte-area:empty::before, .rte-area > p:only-child:has(> br:only-child)::before { content: attr(data-placeholder); color: rgb(var(--c-muted)); pointer-events: none; }
.rte-area > p:only-child:has(> br:only-child)::before { content: 'Tell the story: what the client needed, what you did, what came of it.'; }
.rte-area :is(p, ul, ol, blockquote, figure) { margin: 0 0 12px; }
.rte-area h2 { margin: 18px 0 8px; font-size: 1.45rem; font-weight: 700; }
.rte-area h3 { margin: 14px 0 6px; font-size: 1.12rem; font-weight: 700; }
.rte-area ul, .rte-area ol { padding-left: 22px; }
.rte-area ul { list-style: disc; } .rte-area ol { list-style: decimal; }
.rte-area blockquote { margin-inline: 0; padding-left: 10px; border-left: 3px solid rgb(var(--c-accent)); color: rgb(var(--c-ink2)); }
.rte-area a { color: rgb(var(--accent-text)); text-decoration: underline; }
.rte-area figure { position: relative; padding: 12px; border-radius: 12px; background: rgb(var(--c-ink) / .06); user-select: none; }
.rte-area figure img { display: block; max-width: 100%; max-height: 320px; border-radius: 8px; }
.rte-area figcaption { margin-top: 8px; font-size: .84rem; color: rgb(var(--c-ink2)); outline: none; user-select: text; }
.rte-area figcaption:empty::before { content: attr(data-placeholder); color: rgb(var(--c-muted)); }
.rte-area figure[data-kind="video"]::after { content: 'Video: ' attr(data-src); display: block; padding: 22px 6px; font-size: .86rem; font-weight: 600; word-break: break-all; }
/* a before / after block in the editor: a small dark card like the one on the page, the after value in blue */
.rte-area figure[data-kind="compare"] { background: #121214; color: #fff; }
.rte-area figure[data-kind="compare"]::before { content: 'BEFORE  ' attr(data-before) '  ' attr(data-before-label); display: block; padding: 14px 6px 4px; font-size: .86rem; font-weight: 600; color: rgb(255 255 255 / .55); white-space: pre-wrap; }
.rte-area figure[data-kind="compare"]::after { content: 'AFTER  ' attr(data-after) '  ' attr(data-after-label); display: block; padding: 2px 6px 12px; font-size: 1.15rem; font-weight: 800; color: rgb(140 160 255); white-space: pre-wrap; }
.rte-x { position: absolute; top: 8px; right: 8px; z-index: 1; width: 28px; height: 28px; border: 0; border-radius: 50%; background: rgb(var(--c-dark) / .75); color: #fff; font-size: 1rem; line-height: 1; cursor: pointer; }
.rte-x:hover { background: rgb(var(--c-err)); }
.rte-ask .rte-ask-fields { display: grid; gap: 12px; margin-top: 14px; }
.rte-ask .rte-ask-fields .label { display: grid; gap: 6px; }

/* the story on the case study page (case-study.php .case-body, RichText::render()): readable prose, the headings in the
   page's type, pictures and videos full width, a before / after block on its own card */
.case-body { margin-top: clamp(28px, 4vw, 48px); font-size: 1.04rem; line-height: 1.75; color: rgb(var(--c-ink2)); }
.case-body > * + * { margin-top: 1.1em; }
.case-body h2 { margin-top: 1.9em; font-size: clamp(1.35rem, 2.2vw, 1.75rem); font-weight: 700; line-height: 1.25; letter-spacing: -.01em; color: rgb(var(--c-ink)); }
.case-body h3 { margin-top: 1.5em; font-size: 1.15rem; font-weight: 700; line-height: 1.3; color: rgb(var(--c-ink)); }
.case-body > :first-child { margin-top: 0; }
.case-body strong { color: rgb(var(--c-ink)); font-weight: 700; }
.case-body a { color: rgb(var(--accent-text)); text-decoration: underline; text-underline-offset: 3px; }
.case-body ul, .case-body ol { padding-left: 1.3em; } .case-body ul { list-style: disc; } .case-body ol { list-style: decimal; }
.case-body li + li { margin-top: .35em; }
.case-body blockquote { margin-inline: 0; padding: 2px 0 2px 12px; border-left: 3px solid rgb(var(--c-accent)); font-size: 1.12rem; color: rgb(var(--c-ink)); } /* close to the text's edge (owner, 2026-10-25: "less indented") */
.rt-figure { margin-block: 1.8em; }
.rt-figure img { display: block; width: 100%; border-radius: var(--r-card); }
.rt-figure figcaption { margin-top: 10px; font-size: .86rem; color: rgb(var(--c-muted)); text-align: center; }
.rt-video .media { border-radius: var(--r-card); }
.rt-video iframe, .rt-video video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.rt-compare { margin-block: 1.8em; padding: clamp(20px, 3vw, 32px); }

/* ---------- full-width sections (owner, 2026-10-25: "unlock the main content bounds ... keep it centred, make the
   section bounding boxes full width") ----------
   The sheet is no longer a 1200px card: it spans the window, and its colour fades out towards the window's sides, so
   the dot field (kx.field) shows at the far left and right and runs into the page without ever lying under content
   (solid from the content column + 40px; on a window narrower than ~1280px there is no room and the sheet is solid).
   The content keeps its 1200px column: every section's .wrap pads itself to that column. The panels (.hero, .band,
   .cinema) run the window's width, 8px in from each side so their rounded corners still never meet a square edge; on
   phones they touch the sides as before. The topbar keeps its width. */
html:not([data-division="admin"]) { --col: 1200px; --gut: clamp(16px, 4.6vw, 64px); --fade: 640px; }
html:not([data-division="admin"]) .sheet {
  max-width: none; margin-inline: 0; border-radius: 0; box-shadow: none;
  /* solid to the window's edges (owner, 2026-10-25: the fades into the field banded and, on the light page, dark
     panels fading into white looked wrong): the dot field is drawn over the sides instead (kx.field, .bg-fx) */
  background: rgb(var(--c-bg));
}
html:not([data-division="admin"]) .wrap { padding-inline: max(var(--gut), calc((100% - var(--col)) / 2 + var(--gut))); }
/* the panels run to the window's very edges (owner, 2026-10-25: "all sections should extend to the very edge"), on
   phones too: no body padding, no inset; their content pads to the column like every .wrap */
html:not([data-division="admin"]) body { padding-inline: 0; }
/* the hero's pieces stay with the content column on a wide window: the backdrop video starts where it did in the
   1200px sheet (42% of it), the Social Media phone stands at the column's right edge */
@media (min-width: 901px) { html:not([data-division="admin"]) .hero-media:not(.is-cover) { left: max(42%, calc(50% - 96px)); right: max(0px, calc((100% - var(--col)) / 2)); border-radius: 0 var(--r-sheet) var(--r-sheet) 0; overflow: hidden; } } /* back to its width in the 1200px sheet (owner, 2026-10-25: "limit the hero video width, like in the beginning") */
html:not([data-division="admin"]) .hero-phone { right: calc(max(0px, (100% - var(--col)) / 2) + clamp(12px, 3.6vw, 70px)); }
@media (max-width: 760px) { html:not([data-division="admin"]) .hero-phone { right: auto; } }
/* both division heroes are the same height (owner, 2026-10-25), the Social Media one's, with the copy from the top, so
   switching pages moves neither the headline nor the section under it; phones keep their own stacked layouts */
html:not([data-division="admin"]) .hero { --hero-h: clamp(480px, 42vw, 600px); }
@media (min-width: 901px) { html:not([data-division="admin"]) .hero:not(.hero-cover) { min-height: var(--hero-h); justify-content: flex-start; } } /* shorter since 2026-10-25 (was up to 690px): the copy needs ~600 */
/* headline numbers in one row (stats.php, owner 2026-10-25): up to six figures side by side from 1024px, three to a row
   on tablets, two on phones (the phones block above). The padding beside each figure tightens with the count, so five
   or six still get a comfortable size; the size itself is still worked out from the longest figure (--len). */
html:not([data-division="admin"]) .stats-row[data-cols="5"], html:not([data-division="admin"]) .stats-row[data-cols="6"] { grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); }
html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat { padding-inline: clamp(12px, 1.8vw, 28px); }
html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:first-child { padding-left: 0; }
html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:first-child::before { content: none; }
html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat-val { font-size: min(clamp(2rem, 3.6vw, 3.1rem), calc((100cqw / var(--n) - 2 * clamp(12px, 1.8vw, 28px)) / (var(--len, 4) * .66))); }
html:not([data-division="admin"]) .stats-row:is([data-cols="2"]) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 721px) and (max-width: 1023px) {
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) { --n: 3; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(3n+1) { padding-left: 0; }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(3n+1)::before { content: none; }
}
/* a service card's background picture (services.php .has-bg; owner, 2026-10-25: "a background image fading into the
   details from the top right"): the picture sits in the card's top right corner and fades out towards the bottom left
   into the card's own fill (grey, or blue on the highlighted card), along an eased curve, so the text below and to the
   left stays on the plain card; the card keeps its own colours */
.service-card.has-bg { overflow: hidden; isolation: isolate; }
.service-card.has-bg .service-badge { background: rgb(var(--c-accent)); color: #fff; } /* the tag sits on the picture: a solid blue chip keeps it readable (owner, 2026-10-25) */
/* the tag on the icon's line: its middle level with the 46px icon tile's middle, the card's padding from the top and the side */
.service-badge { top: calc(clamp(22px, 2.4vw, 32px) + 23px); right: clamp(22px, 2.4vw, 32px); translate: 0 -50%; }
.service-bg { position: absolute; top: 0; right: 0; z-index: -1; width: 85%; height: 62%; object-fit: cover; object-position: right top; pointer-events: none;
  -webkit-mask-image: radial-gradient(120% 100% at 100% 0%, #000 0%, rgb(0 0 0 / .88) 22%, rgb(0 0 0 / .6) 40%, rgb(0 0 0 / .3) 56%, rgb(0 0 0 / .1) 68%, transparent 80%); mask-image: radial-gradient(120% 100% at 100% 0%, #000 0%, rgb(0 0 0 / .88) 22%, rgb(0 0 0 / .6) 40%, rgb(0 0 0 / .3) 56%, rgb(0 0 0 / .1) 68%, transparent 80%); }

/* admin lists with their own order (list.php, Admin::sortable(), 2026-10-25): a handle and two small arrows in front of
   each row; the order is saved as soon as a row is dropped */
.table .drag-cell { width: 1%; white-space: nowrap; padding-right: 0; }
.drag-handle { display: inline-grid; place-items: center; width: 22px; height: 30px; vertical-align: middle; cursor: grab; color: rgb(var(--c-muted)); touch-action: none; }
.drag-handle:active { cursor: grabbing; }
.drag-handle svg.ico { width: 14px; height: 14px; }
.drag-arrows { display: inline-flex; gap: 2px; vertical-align: middle; opacity: 0; transition: opacity var(--dur-quick); }
tr:hover .drag-arrows, .drag-arrows:focus-within { opacity: 1; }
@media (pointer: coarse) { .drag-arrows { opacity: 1; } .drag-handle { display: none; } }
.drag-arrows .num-arrow { width: 26px; height: 26px; border-radius: 7px; }
.drag-arrows svg.ico { width: 12px; height: 12px; rotate: 90deg; }
tr.is-dragging { opacity: .85; background: rgb(var(--c-surface2)); box-shadow: 0 14px 28px -16px rgb(var(--c-dark) / .6); position: relative; z-index: 2; }
/* the row being dragged (admin.js sortable, 2026-10-25): lifted above the others, opaque, with a soft shadow, and the
   grab cursor on the whole page while it is held */
:is(.num-field, .adm-kid, tr).is-dragging { position: relative; z-index: 5; box-shadow: 0 18px 36px -14px rgb(0 0 0 / .65), 0 0 0 1px rgb(var(--c-accent) / .35); cursor: grabbing; }
tr.is-dragging > td { background: rgb(var(--c-surface2)); }
body:has(.is-dragging) { cursor: grabbing; user-select: none; }
/* a block's own toolbar in the Story editor (admin.js arm()): drag handle, up, down, Edit, remove, top right */
.rte-tools { position: absolute; top: 8px; right: 8px; z-index: 1; display: flex; gap: 4px; padding: 3px; border-radius: 10px; background: rgb(var(--c-dark) / .78); }
.rte-tool { display: grid; place-items: center; min-width: 28px; height: 28px; padding: 0 6px; border: 0; border-radius: 7px; background: transparent; color: #fff; font-size: .9rem; font-weight: 700; line-height: 1; cursor: pointer; }
.rte-tool:hover { background: rgb(255 255 255 / .16); }
.rte-tool-text { font-size: .74rem; letter-spacing: .02em; }
.rte-tool-x:hover { background: rgb(var(--c-err)); }
.rte-grip { cursor: grab; touch-action: none; font-size: 1rem; }
.rte-grip:active { cursor: grabbing; }
.rte-area figure.is-moving { opacity: .45; }
.rte { position: relative; }
.rte-dropline { position: absolute; left: 14px; right: 14px; top: 0; z-index: 3; height: 3px; border-radius: 3px; background: rgb(var(--c-accent)); pointer-events: none; }
.rte-dropline::before { content: ''; position: absolute; left: -4px; top: -3px; width: 9px; height: 9px; border-radius: 50%; background: rgb(var(--c-accent)); }
/* story videos (RichText::render()): a wide player (Vimeo, YouTube) 16:9 across the column; an upright one (a Short,
   TikTok, an Instagram Reel) 9:16, centred and no wider than a phone; Instagram's embed page carries its own header
   and caption, so its box is taller */
.rt-video.is-wide .media { aspect-ratio: 16 / 9; }
.rt-video.is-tall .media { aspect-ratio: 9 / 16; width: min(100%, 360px); margin-inline: auto; }
.rt-video.is-instagram .media { aspect-ratio: auto; height: min(760px, 160vw); width: min(100%, 400px); background: #fff; }
/* the before / after card in a case study's story (partials/story-compare.php, owner 2026-10-25: "sleeker and more
   compact"): one row on a dark card in both themes, the starting point small and grey, the result large and filled
   white into the accent (the gradient rule below), and between them the multiple as a small accent pill over one arrow
   drawn as a single stroke that draws itself once in view (stroke-dashoffset, a one-off). */
.scmp { position: relative; display: grid; grid-template-columns: minmax(0, auto) minmax(56px, 1fr) minmax(0, auto); align-items: end; gap: clamp(16px, 2.4vw, 26px); margin-block: 1.8em; padding: clamp(18px, 2.6vw, 26px) clamp(20px, 3vw, 30px); border-radius: var(--r-card); background: #0f1012; color: rgb(255 255 255 / .7); box-shadow: 0 20px 40px -30px rgb(0 0 0 / .7); }
.scmp-side { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.scmp-side.is-after { align-items: flex-end; text-align: right; }
.scmp-tag { font-size: .64rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: rgb(255 255 255 / .45); }
.scmp-side.is-after .scmp-tag { color: rgb(150 170 255); }
.scmp-val { font-weight: 800; line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.scmp-side.is-before .scmp-val { font-size: clamp(1.5rem, 3vw, 2.1rem); color: rgb(255 255 255 / .75); } /* lighter than the 50% it was (owner, 2026-10-25), still a step under the white-to-blue result */
.scmp-side.is-after .scmp-val { font-size: clamp(2.1rem, 4.8vw, 3.2rem); color: #fff; }
.scmp-label { font-size: .8rem; line-height: 1.35; color: rgb(255 255 255 / .55); max-width: 15em; }
.scmp-mid { display: grid; justify-items: center; gap: 6px; align-self: center; width: 100%; min-width: 0; padding-bottom: 14px; }
.scmp-x { padding: 4px 10px; border-radius: 999px; background: rgb(var(--c-accent)); color: #fff; font-size: .82rem; font-weight: 800; line-height: 1.2; letter-spacing: -.01em; }
/* the arrow fills the whole gap between the two figures (the line stretches, its head is a marker that does not) */
.scmp-mid { justify-items: stretch; }
.scmp-mid .scmp-x { justify-self: center; }
.scmp-arrow { display: block; width: 100%; height: 14px; color: rgb(140 160 255); padding-right: 2px; box-sizing: border-box; }
.scmp-arrow line { stroke-dasharray: 1; stroke-dashoffset: 0; }
.scmp-arrow marker path { transition: opacity .3s; }
.js .scmp.reveal .scmp-arrow line { stroke-dashoffset: 1; transition: stroke-dashoffset 1s var(--ease-snappy) .25s; }
.js .scmp.reveal .scmp-arrow marker path { opacity: 0; transition-delay: 1s; }
.js .scmp.reveal.in .scmp-arrow line { stroke-dashoffset: 0; }
.js .scmp.reveal.in .scmp-arrow marker path { opacity: 1; }
.js .scmp.reveal .scmp-x { opacity: 0; transform: scale(.7); transition: opacity .3s, transform .5s var(--ease-overshoot); transition-delay: .8s; }
.js .scmp.reveal.in .scmp-x { opacity: 1; transform: none; }
@media (max-width: 520px) {
  .scmp { grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 10px; }
  .scmp-arrow { max-width: 44px; }
  .scmp-label { font-size: .74rem; }
}
@media (prefers-reduced-motion: reduce) { .js .scmp.reveal .scmp-arrow line { stroke-dashoffset: 0; transition: none; } .js .scmp.reveal .scmp-arrow marker path { opacity: 1; transition: none; } .js .scmp.reveal .scmp-x { opacity: 1; transform: none; transition: none; } }

/* the sparkle highlight in a story (<mark>, owner 2026-10-25: "it makes the selected text shine blue"): the words in the
   accent blue with a lighter band of light that sweeps across them twice when they come into view and again on hover,
   and a small four-point star after them that twinkles (scale and opacity). The sweep is a moving background, so it
   runs a limited number of times, never in a loop. */
/* two layers: the moving band of light over a solid blue that always covers every letter (the band alone left parts of
   the word without paint while it moved) */
.case-body mark, .rte-area mark { position: relative; padding: 0 .08em; border-radius: 3px; color: rgb(var(--accent-text)); font-weight: 700; background: linear-gradient(100deg, transparent 38%, color-mix(in srgb, rgb(var(--accent-text)) 30%, #fff) 50%, transparent 62%) 120% 0 / 250% 100% no-repeat, rgb(var(--accent-text)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.case-body mark.is-lit, .case-body mark:hover { animation: spark-sweep 1.4s var(--ease-in-out) .2s 2; } /* .is-lit: sparkles() in site.js, once in view */
.case-body mark:hover { animation-iteration-count: 1; }
@keyframes spark-sweep { from { background-position: 120% 0, 0 0; } to { background-position: -20% 0, 0 0; } }
.case-body mark::after, .rte-area mark::after { content: ''; display: inline-block; width: .62em; height: .62em; margin-left: .14em; vertical-align: .55em; background: rgb(var(--accent-text)); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 0l1.6 5.4L16 8l-6.4 2.6L8 16l-1.6-5.4L0 8l6.4-2.6z'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 0l1.6 5.4L16 8l-6.4 2.6L8 16l-1.6-5.4L0 8l6.4-2.6z'/%3E%3C/svg%3E") center / contain no-repeat; animation: spark-twinkle 2.6s var(--ease-in-out) infinite; }
@keyframes spark-twinkle { 0%, 100% { transform: scale(.7) rotate(0); opacity: .55; } 50% { transform: scale(1.1) rotate(45deg); opacity: 1; } }
html.lite .case-body mark, html.lite .case-body mark::after { animation: none; }
@media (prefers-reduced-motion: reduce) { .case-body mark, .case-body mark::after { animation: none !important; } }
.rte-spark-btn svg.ico { color: rgb(var(--accent-text)); }
.rte-btn.rte-ico { width: 34px; padding: 0; justify-content: center; }
.rte-btn.rte-ico svg.ico { width: 16px; height: 16px; }
/* the result of a story's before / after card: filled with a gradient from white into the accent's light step (owner,
   2026-10-25, an explicit exception to "no colour gradients") */
.scmp-side.is-after .scmp-val { background: linear-gradient(100deg, #fff 15%, rgb(150 170 255) 70%, rgb(110 135 255) 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; padding-bottom: .06em; }
/* the Story toolbar shows the styles the cursor is in (admin.js syncButtons()): quote, bold, italic, sparkle lit */
.rte-btn.is-on { background: rgb(var(--c-accent)); color: #fff; }
.rte-btn.is-on svg.ico { color: #fff; }
/* the sound control on a localization card (video-card.php; owner 2026-10-25: "copy the control colours from the
   theatre", no blue): the theater's own pill (.vstage-sound: graphite, frosted, a white speaker, the white volume slider
   once the sound is on), in the card's top right corner, shown while the card is hovered or focused, always on touch */
.vcard .vcard-sound { top: 10px; right: 10px; left: auto; translate: none; z-index: 3; }
.vcard:hover .vcard-sound, .vcard .vcard-sound:focus-within, .vcard.has-sound .vcard-sound { opacity: 1; scale: 1; pointer-events: auto; }
.vcard .vcard-sound .vstage-sound-range { width: 64px; }
@media (hover: none) { .vcard .vcard-sound { opacity: 1; scale: 1; pointer-events: auto; } }
html.lite .vcard .vcard-sound { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgb(var(--c-graphite) / .92); }
/* carousel arrows on a dark panel (the video stage in .cinema): a plain dark drop shadow; the light page's long grey
   shadow (--sh-pill with a light --shade) showed as a pale glow under them on the dark panel (owner, 2026-10-25) */
.cinema .carousel-btn, html[data-theme="light"] .cinema .carousel-btn { box-shadow: 0 2px 6px rgb(0 0 0 / .45), 0 10px 18px -10px rgb(0 0 0 / .6); }
.cinema .carousel-btn:hover, html[data-theme="light"] .cinema .carousel-btn:hover { box-shadow: 0 4px 10px rgb(0 0 0 / .5), 0 14px 24px -12px rgb(0 0 0 / .65); }
/* headline numbers: one space from each divider to its figure on every row and page (owner, 2026-10-25: "more unified,
   larger"): --gap-l after the line, a small --gap-r before the next one; the first figure of a row has no line and no
   space. The size of the figures is worked out with the same two spaces. Phones keep their own two-column rules. */
@media (min-width: 721px) {
  html:not([data-division="admin"]) .stats-row { --gap-l: clamp(24px, 2.8vw, 42px); --gap-r: clamp(6px, .8vw, 12px); }
  html:not([data-division="admin"]) .stats-row .stat, html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat { padding-inline: var(--gap-l) var(--gap-r); }
  html:not([data-division="admin"]) .stats-row .stat:first-child,
  html:not([data-division="admin"]) .stats-row[data-cols="4"] .stat:nth-child(4n+1),
  html:not([data-division="admin"]) .stats-row[data-cols="3"] .stat:nth-child(3n+1) { padding-left: 0; }
  html:not([data-division="admin"]) .stats-row[data-cols] .stat-val { font-size: min(clamp(2.2rem, 4.4vw, 3.4rem), calc((100cqw / var(--n) - var(--gap-l) - var(--gap-r)) / (var(--len, 4) * .7))); }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat-val { font-size: min(clamp(2rem, 3.6vw, 3.1rem), calc((100cqw / var(--n) - var(--gap-l) - var(--gap-r)) / (var(--len, 4) * .66))); }
}
@media (min-width: 721px) and (max-width: 1023px) {
  html:not([data-division="admin"]) .stats-row[data-cols="4"] .stat:nth-child(2n+1),
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(3n+1) { padding-left: 0; }
}
/* the "Theater light" box of a video (form.php ambient, admin.js [data-ambient]): one swatch per second of the video */
.amb-field { display: grid; gap: 10px; }
.amb-strip { display: flex; height: 18px; border-radius: 6px; overflow: hidden; background: rgb(var(--c-surface2)); }
.amb-strip:empty { display: none; }
.amb-strip i { flex: 1; min-width: 1px; }
.amb-state { color: rgb(var(--c-muted)); }
.amb-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.amb-pick { cursor: pointer; }
/* ---------- the Social Media hero banner (partials/hero-phone.php, owner 2026-10-25) ----------
   The Kubx Studio banner (two phones, 3002 x 2899) in its exact shape, standing on the hero's foot at the content
   column's right edge and running on a little under the dark section below (which stacks above it). The video box sits
   behind the banner over the right phone's see-through screen, a few pixels larger than the opening (measured from the
   PNG: x 1109-2902 of 3002, from y 82 of 2899 to the bottom, top corners curving over ~315 px), so the phone's frame
   and camera pill mask it; the video fills it cropped. --banner-w sets the size; the copy keeps clear of it. */
/* the banner stays inside the content column (owner, 2026-10-25: "it's going outside the content flow bounds"): its right
   edge on the column's right edge, as wide as the room beside the copy (36rem + 32px) allows, at most 600px, never under
   340px; it stands on the hero's foot and runs on 24px under the dark section below, which closes the 8px gap between
   the panels (owner, 2026-10-25: "there's a gap between the second section and the phone graphic"); only the phones'
   lower bodies go under, the screens stay in view */
/* bigger since 2026-10-26 (owner: "make the phone graphic bigger"): the banner's right edge sits 16px in from the
   column's edge (in the gutter) instead of a whole gutter, and it may be as tall as the hero itself */
.hero.has-phone { --copy-w: 31rem; --banner-r: max(16px, calc((100% - var(--col)) / 2 + 16px)); --banner-w: clamp(340px, min(calc(min(100%, var(--col)) - var(--gut) - 16px - var(--copy-w) - 32px), calc(var(--hero-h) * 1.036)), 720px); }
@media (min-width: 1101px) { .hero.has-phone .hero-copy { max-width: var(--copy-w); } } /* a little narrower than 36rem, so the banner beside it can be larger (2026-10-25) */
html:not([data-division="admin"]) .hero-banner { top: auto; bottom: -24px; right: var(--banner-r); width: var(--banner-w); aspect-ratio: 3002 / 2899; }
.hb-frame { position: relative; z-index: 1; display: block; width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 30px 40px rgb(0 0 0 / .28)); }
/* the opening's corner is the phone's soft "squircle", flatter than a circle of the same reach: a circle of ~200 px (of
   the 3002 px image) keeps the video under the frame all along the curve, so no gap shows in the corners */
.hb-screen { position: absolute; z-index: 0; left: 36.74%; top: 2.62%; width: 60.13%; bottom: 0; overflow: hidden; border-radius: 11.1% 11.1% 0 0 / 7.1% 7.1% 0 0; background: #000; container-type: size; }
.hb-screen > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hb-screen .vembed[data-embed="vimeo"] { position: absolute; border: 0; pointer-events: none; inset: auto; left: 50%; top: 50%; translate: -50% -50%; aspect-ratio: auto; max-width: none; width: max(100cqw * var(--ar, .5625) / var(--car, var(--ar, .5625)), 100cqh * var(--ar, .5625)); height: max(100cqh, 100cqw / var(--car, var(--ar, .5625))); }
.hero-banner .phone-fx { left: 52%; top: 34%; width: 30%; height: 24%; } /* over the middle of the right phone's screen */
/* the TikTok screen over the video (.tt-* above, in cqw of this box): the box is cut by the banner's foot, so the rail
   and the caption sit a little higher than on a whole phone and stay clear of the dark section that runs over the foot */
.hb-screen { color: #fff; font-family: var(--font-body); } /* TikTok's white type, also on the light page */
.hb-screen > :is(.tt-shade, .tt-status, .tt-tabs, .tt-views, .tt-rail, .tt-caption) { z-index: 1; }
.hb-screen .tt-status { top: 3cqw; inset-inline: 9cqw; }
.hb-screen .tt-tabs { top: 15cqw; }
.hb-screen .tt-views { top: 25cqw; }
.hb-screen .tt-rail { bottom: 30cqw; }
.hb-screen .tt-caption { bottom: 12cqw; }
/* tablets and small laptops: banner and copy share the width in viewport units, which both read the same way (a % in
   --banner-w meant one thing for the banner and another for the copy, and the gap closed) */
@media (min-width: 901px) and (max-width: 1100px) {
  .hero.has-phone { --banner-w: min(45vw, calc(var(--hero-h) * 1.036)); }
  .hero.has-phone .hero-copy { max-width: min(36rem, calc(100vw - var(--gut) - 16px - 45vw - 32px)); }
}
/* up to 900px the banner stands under the copy, centred and whole (beside it the copy got too narrow) */
@media (max-width: 900px) {
  html:not([data-division="admin"]) .hero-banner { position: relative; order: 2; right: auto; bottom: auto; width: min(640px, 100%); margin: 28px auto -24px; }
  .hero.has-phone .hero-copy { max-width: none; }
  html:not([data-division="admin"]) .hero.has-phone { padding-bottom: 0; } /* the banner stands on the foot and runs on under the next panel here too: no gap (2026-10-25) */
}
html.lite .hb-frame { filter: none; }
/* "Then we make it better": the tools it is edited in, under the bars (owner, 2026-10-25); the two icons pop in after the
   blue bar has grown */
.sw-tools { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
.sw-tools em { font-style: normal; font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: rgb(255 255 255 / .55); margin-right: 2px; }
.sw-tools img { width: 26px; height: auto; border-radius: 6px; transform: scale(.5); opacity: 0; transition: transform .5s cubic-bezier(.3, 1.6, .5, 1), opacity .3s; transition-delay: calc(var(--d) + 1.9s); }
.sw-tools img + img { transition-delay: calc(var(--d) + 2.05s); }
.switching-step.is-in .sw-tools img { transform: none; opacity: 1; }
@media (prefers-reduced-motion: reduce) { .switching .sw-tools img { transition: none; transform: none; opacity: 1; } }
/* Results (workflow.php 'chart', owner 2026-10-25, after fortnite.gg's player count): the client's map, three figures
   in green / violet / cyan, and its daily player peaks as a smooth green area chart that draws itself when the step is
   on the stage (stroke-dashoffset, a one-off per visit of the step) */
.mk-growth { grid-template-rows: auto auto minmax(0, 1fr) auto; gap: .45em; padding: .85em 1em .7em; } /* not .mk-results: the Research screen uses that for its row of results */
.mk-rhead { display: flex; align-items: center; gap: .6em; min-width: 0; }
.mk-rmap { flex: none; width: 3.4em; aspect-ratio: 16 / 10; border-radius: .35em; overflow: hidden; background: #1e1e1e; }
.mk-rmap img { width: 100%; height: 100%; object-fit: cover; }
.mk-rtitle { display: grid; min-width: 0; line-height: 1.15; }
.mk-rtitle b { font-size: .95em; font-weight: 800; letter-spacing: .02em; text-transform: uppercase; color: #fff; }
.mk-rtitle small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .7em; color: rgb(255 255 255 / .55); }
.mk-figs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4em; text-align: center; }
.mk-fig { display: grid; line-height: 1.1; }
.mk-fig b { font-size: 1.15em; font-weight: 800; font-variant-numeric: tabular-nums; }
.mk-fig small { font-size: .56em; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: rgb(255 255 255 / .6); }
.mk-fig.is-green b { color: #4ade80; } .mk-fig.is-violet b { color: #a78bfa; } .mk-fig.is-cyan b { color: #22d3ee; }
.mk-chart { position: relative; min-height: 0; padding-bottom: 1em; }
.mk-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
.mk-grid line { stroke: rgb(255 255 255 / .08); stroke-width: 1; vector-effect: non-scaling-stroke; }
.mk-trend { fill: none; stroke: rgb(255 255 255 / .5); stroke-width: 1.2; stroke-dasharray: 4 4; vector-effect: non-scaling-stroke; } /* the week's trend, growthCharts() */
.is-on .mk-trend { animation: mk-area-in .8s ease 1.4s both; }
.mk-line { fill: none; stroke: #4ade80; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; stroke-dasharray: 1; stroke-dashoffset: 0; }
.mk-ticks { position: absolute; left: 0; right: 0; bottom: 0; height: 1em; }
.mk-ticks span { position: absolute; transform: translateX(-50%); font-size: .55em; color: rgb(255 255 255 / .45); white-space: nowrap; }
.mk-ticks span:first-child { transform: none; } .mk-ticks span:last-child { transform: translateX(-100%); }
.mk-rfoot { display: inline-flex; align-items: center; gap: .4em; font-size: .6em; color: rgb(255 255 255 / .55); }
.mk-rfoot i { width: .6em; height: .6em; border-radius: 50%; background: #4ade80; }
.is-on .mk-line { animation: mk-draw 1.6s cubic-bezier(.16, 1, .3, 1) .25s both; }
.is-on .mk-area { animation: mk-area-in 1s ease .9s both; }
.is-on .mk-fig { animation: mk-pop .45s var(--ease-overshoot) calc(.15s + var(--i, 0) * 120ms) both; }
.mk-fig:nth-child(2) { --i: 1; } .mk-fig:nth-child(3) { --i: 2; }
@keyframes mk-draw { from { stroke-dashoffset: 1; } }
@keyframes mk-area-in { from { opacity: 0; } }
/* no hard line under the top bar (owner, 2026-10-25): the sheet starts at the very top, behind the bar, instead of 84px
   down where its colour began in a hard edge over the darker field */
html:not([data-division="admin"]) .sheet { margin-top: 0; padding-top: 84px; }

/* a darker panel (game.php "Companies & creators"), so two panels of the same colour never follow each other (the
   contact panel comes next); a pure grey, like every dark surface */
html:not([data-theme="light"]):not([data-division="admin"]) .band.band-deep { background: #060606; }
html[data-theme="light"] .band.band-deep { background: color-mix(in srgb, rgb(var(--c-ink)) 7%, rgb(var(--c-bg))); }
/* the deep panel is close to the page's own black, so its foot padding and the 8px between the panels read as an empty
   stretch before the contact panel (owner, 2026-10-25: "a strange gap between Let's build something and Companies &
   creators", then "remove the gap entirely"): since 2026-10-26 every panel is square and flush (below), so the contact
   panel simply follows it; its top padding is short (its full 120px read as more of the gap) */
html[data-division] #main > .band.band-deep:last-child { padding-bottom: clamp(40px, 9vw, 120px); } /* the same space under the cards as above the heading (a section's padding; owner: "unify the bottom padding with the top") */
html[data-division] #main:has(> .band.band-deep:last-child) + #contact { padding-top: clamp(32px, 3.4vw, 48px); }


/* banding in the dark gradients (owner, 2026-10-25: "fix the black gradient banding across the website", then a
   screenshot of the panels' side fade still in stripes): a screen has 256 steps per colour, and a long fade between two
   near-blacks (a panel fading into the dot field at the sides, the hero's fade into the video, the theater's light and
   vignette, the season banner) spans only a dozen or two of them, so each step shows as a stripe tens of pixels wide.
   One faint layer of per-pixel noise over the whole page dithers those steps away: assets/img/grain.png, 128 x 128
   white pixels of random opacity (made once with a script, 23 KB, cached for long), drawn one image pixel per screen
   pixel (the size follows the pixel ratio, pixelated, so it is never smoothed into blotches, which the SVG noise before
   was) at 2.2% opacity: 0-5 of 255 levels, about 2.5 on average, more than one step, too fine to see as grain. Normal
   blending (soft-light barely touches near-black). One static fixed layer: nothing repaints on scroll, so it stays on in
   lite too, where the banding was worst; not in print. */
html:not([data-division="admin"])::after { content: ''; position: fixed; inset: 0; z-index: 2147483000; pointer-events: none;
  background: url("../img/grain.png") 0 0 / 128px 128px repeat; image-rendering: pixelated; opacity: .022; }
@media (min-resolution: 1.25dppx) { html:not([data-division="admin"])::after { background-size: 102.4px 102.4px; } }
@media (min-resolution: 1.5dppx) { html:not([data-division="admin"])::after { background-size: 85.333px 85.333px; } }
@media (min-resolution: 2dppx) { html:not([data-division="admin"])::after { background-size: 64px 64px; } }
@media (min-resolution: 3dppx) { html:not([data-division="admin"])::after { background-size: 42.667px 42.667px; } }
@media print { html::after { display: none; } }
/* phones and tablets (up to 900px, where the hero's visual no longer stands beside the copy): the hero is one centred
   composition (owner, 2026-10-25: "mobile view should have a centered composition hero"): label, headline, line and
   buttons on the middle axis, the banner centred under them */
@media (max-width: 900px) {
  html:not([data-division="admin"]) .hero:not(.hero-cover) .hero-copy { width: 100%; margin-inline: auto; text-align: center; } /* full width: as a size container (.hlines) it would shrink to nothing with auto margins */
  html:not([data-division="admin"]) .hero:not(.hero-cover) .hero-copy > * { margin-inline: auto; }
  html:not([data-division="admin"]) .hero:not(.hero-cover) .hero-actions { justify-content: center; }
}

/* headline numbers on phones (owner, 2026-10-25: "improve the stat distribution on mobile, it could be in two rows"):
   five or six figures stand in two rows of three (five: three over two, the two centred under the gaps), each figure
   centred under its icon, a hairline between neighbours; they shrink together to fit a third of the width */
@media (max-width: 767px) {
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) { --n: 3; --rg: 28px; grid-template-columns: repeat(6, minmax(0, 1fr)); }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat { grid-column: span 2; padding-inline: 6px; align-items: center; text-align: center; }
  html:not([data-division="admin"]) .stats-row[data-cols="5"] .stat:nth-child(4) { grid-column: 2 / span 2; }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat::before { content: '' !important; }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(3n+1)::before { content: none !important; }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat::after,
  html:not([data-division="admin"]) .stats-row[data-cols]:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(n):nth-child(n):nth-child(n)::after { content: none !important; } /* no line between the two rows (owner: "the two horizontal dividers are disgusting"); the phones' two-column rule draws one per row start, which outranked the plain selector */
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat-body { display: grid; justify-items: center; }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat-val { font-size: min(clamp(1.35rem, 7vw, 2.1rem), calc((100cqw / 3 - 12px) / (var(--len, 4) * .62))); }
  html:not([data-division="admin"]) .stats-row:is([data-cols="5"], [data-cols="6"]) .stat-label { font-size: .76rem; line-height: 1.3; }
}
/* the sheet runs to the very bottom of the page (it stopped 24px short, which cut the footer's side fade off; owner
   2026-10-25): the space under the footer is padding inside the sheet now */
html:not([data-division="admin"]) .sheet { margin-bottom: 0; padding-bottom: 24px; }
/* with the sheet solid to the edges, the page and the top bar's fade use the sheet's own colour (the field colour, a
   step darker, is only the WebGL field's old backdrop now; the fade from it into the sheet drew a band under the bar) */
html:not([data-division="admin"]) body { background: rgb(var(--c-bg)); }
html:not([data-division="admin"]) .topbar::before { background: linear-gradient(rgb(var(--c-bg)) 0%, rgb(var(--c-bg) / .85) 45%, rgb(var(--c-bg) / 0) 100%); }
/* five or six headline figures below 1024px stand three to a row: a hairline before every figure except the first of
   each row, and none between the rows (set here with weight, the older two-column phone rules took some away) */
@media (max-width: 1023px) {
  html:not([data-division="admin"]) .stats-row[data-cols]:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(n):nth-child(n):nth-child(n)::before { content: '' !important; }
  html:not([data-division="admin"]) .stats-row[data-cols]:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(3n+1):nth-child(n):nth-child(n)::before { content: none !important; }
  html:not([data-division="admin"]) .stats-row[data-cols]:is([data-cols="5"], [data-cols="6"]) .stat:nth-child(n):nth-child(n):nth-child(n)::after { content: none !important; }
}
/* a sideways row being dragged with a mouse (kx.dragScroll): it follows the pointer exactly, nothing is selected */
:is(.island-grid, .carousel-track, .vcard-row, .vcard-grid, .switching-steps).is-dragging { scroll-snap-type: none !important; scroll-behavior: auto !important; cursor: grabbing; -webkit-user-select: none; user-select: none; }
:is(.island-grid, .carousel-track, .vcard-row, .vcard-grid, .switching-steps).is-dragging * { pointer-events: none; }
:is(.island-grid, .carousel-track, .vcard-row, .vcard-grid, .switching-steps) img { -webkit-user-drag: none; }
/* the localization videos on phones and tablets (owner, 2026-10-25: "horizontal stacking for the translation videos as
   well"): one sideways swipe row like the maps, the next card peeking in at the edge, snapping card by card, and a
   mouse drag scrolls it (kx.dragScroll) */
@media (max-width: 767px) {
  html:not([data-division="admin"]) .vcard-grid { display: grid; grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: 67%; gap: 12px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none;
    margin-inline: calc(var(--gut) * -1); padding: 4px var(--gut) 8px; scroll-padding-inline: var(--gut); }
  html:not([data-division="admin"]) .vcard-grid::-webkit-scrollbar { display: none; }
  html:not([data-division="admin"]) .vcard-grid > * { scroll-snap-align: start; }
}
/* 67%: the card and half of the next one fill the screen (owner, 2026-10-25: "slightly wider, so only half of the second
   video is visible") */
@media (min-width: 480px) and (max-width: 767px) { html:not([data-division="admin"]) .vcard-grid { grid-auto-columns: 46%; } }
/* "Switching to Kubx" on phones: one card at a time, a bar per step that fills and moves on, arrows (switching.php,
   switching() in site.js; owner, 2026-10-25). The fill is a scale of a pseudo-element, paused while the row is off
   screen or a card is held; it runs in lite too (one cheap transform), stands full and still under reduced motion. */
.sw-nav { display: none; }
@media (max-width: 767px) {
  .switching-steps { grid-auto-flow: column; grid-template-columns: none; grid-auto-columns: 100%; gap: 12px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scrollbar-width: none; }
  .switching-steps::-webkit-scrollbar { display: none; }
  .switching-step { scroll-snap-align: start; }
  .sw-nav { display: flex; align-items: center; gap: 10px; margin-top: 16px; }
  .sw-bars { flex: 1; display: flex; gap: 6px; }
  .sw-bar { flex: 1; display: grid; align-items: center; height: 32px; padding: 0; border: 0; background: none; cursor: pointer; }
  .sw-bar i { position: relative; display: block; height: 3px; border-radius: 2px; overflow: hidden; background: rgb(255 255 255 / .18); }
  .sw-bar i::after { content: ''; position: absolute; inset: 0; background: #fff; transform: scaleX(0); transform-origin: left; }
  .sw-bar.is-done i::after { transform: none; }
  .sw-bar.is-active i::after { animation: sw-fill 6s linear forwards; }
  .sw-nav.is-paused .sw-bar.is-active i::after { animation-play-state: paused; }
  .sw-arrow { flex: none; display: grid; place-items: center; width: 38px; height: 38px; border: 0; border-radius: 50%; background: rgb(255 255 255 / .1); color: #fff; cursor: pointer; transition: background-color var(--dur-quick) var(--ease-in-out), transform var(--dur-quick) var(--ease-knob); }
  .sw-arrow:active { transform: scale(.92); }
  .sw-arrow.is-prev { margin-left: 6px; } .sw-arrow.is-prev + .sw-arrow { margin-left: -2px; }
  .sw-arrow svg.ico { width: 16px; height: 16px; }
}
@keyframes sw-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) { .sw-bar.is-active i::after { animation: none; transform: none; } }
/* headline lines set with "|" (two_tone(); owner, 2026-10-25: the Social Media title reads "Content that / fills
   Fortnite / maps" at every width): each line whole on its own row; the lines take the headline's size unless the
   longest would not fit the copy's width, then they shrink together. CSS assumes a full em per letter (safe, never too
   wide); fitLines() in site.js measures the real lines and sets the exact size. */
.hero-copy { container-type: inline-size; }
.hlines { display: block; font-size: min(1em, calc(100cqi / var(--hl-chars, 12))); }
.hline { display: block; white-space: nowrap; }
.hero.has-phone { --copy-w: 32rem; } /* three lines need a little more width than the four did (31rem); 32rem since 2026-10-26, so the bigger banner fits beside it */
/* the contact panel on the light page (owner, 2026-10-25: "a bit too grey"): half the usual band step, about #F0F0F0
   on the #F6F6F6 sheet, so it still reads as its own panel under the white form and cards */
html[data-theme="light"] #contact.band { background: color-mix(in srgb, rgb(var(--c-ink)) 2%, rgb(var(--c-bg))); }
/* the link box above a picture field's drop box (_image-field.php, 2026-10-25); the drop box under it is the second choice */
.img-link + .field-help + .dropzone { margin-top: 10px; padding-block: 18px; }
.adm-kid-img { display: flex; align-items: center; gap: 8px; min-width: 0; }
.adm-kid-img .field { min-width: 0; flex: 1 1 140px; }
.adm-kid-img input[type=file] { max-width: 180px; font-size: .75rem; }
