/* ===========================================================
   79 Event Organizer — Full Site
   8-BIT MONOCHROME concept: cool black & white, pixel motifs, CRT scanlines,
   cinematic logo-assembly intro, clean sleek pixel-grid hero.
   =========================================================== */

/* Skip-to-content link — invisible until a keyboard user Tabs to it (a11y). */
.skip-link{position:fixed;left:14px;top:-80px;z-index:100001;background:#fff;color:#000;
  padding:11px 18px;border-radius:9px;font-weight:700;font-size:14px;text-decoration:none;
  box-shadow:0 6px 24px rgba(0,0,0,.4);transition:top .16s ease}
.skip-link:focus{top:14px;outline:3px solid var(--acc,#e01a24);outline-offset:2px}
.skip-anchor{position:absolute;width:1px;height:1px}

/* PDPA cookie consent banner (bottom-left card; clears the contact FAB on the right). */
.cookie-consent{position:fixed;left:16px;bottom:16px;max-width:440px;z-index:95;
  background:rgba(14,16,20,.94);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,.14);border-radius:14px;padding:16px 18px;color:#e7ebf0;
  box-shadow:0 18px 50px rgba(0,0,0,.55);transform:translateY(150%);opacity:0;
  transition:transform .35s cubic-bezier(.2,.8,.2,1),opacity .35s}
.cookie-consent.on{transform:translateY(0);opacity:1}
.cc__text{font-size:13px;line-height:1.55;color:rgba(231,235,240,.86)}
.cc__text strong{color:#fff}
.cc__text a{color:var(--acc,#e01a24);text-decoration:underline}
.cc__en{display:block;margin-top:5px;font-size:11.5px;color:rgba(231,235,240,.5)}
.cc__btns{display:flex;gap:8px;margin-top:13px;flex-wrap:wrap}
.cc__btn{flex:1;min-width:130px;border:0;border-radius:9px;padding:9px 12px;font-size:12.5px;font-weight:700;cursor:pointer;font-family:inherit;transition:transform .12s,filter .15s}
.cc__btn:hover{filter:brightness(1.08)}
.cc__btn:active{transform:translateY(1px)}
.cc__btn--ghost{background:rgba(255,255,255,.08);color:#e7ebf0;border:1px solid rgba(255,255,255,.16)}
.cc__btn--ok{background:#fff;color:#000}
@media(max-width:560px){.cookie-consent{left:10px;right:10px;bottom:10px;max-width:none}}

/* PDPA consent checkbox on the contact form */
.consent-field{display:flex;gap:10px;align-items:flex-start;margin:2px 0 14px;cursor:pointer;
  font-size:12px;line-height:1.5;color:hsl(var(--foreground)/0.72)}
.consent-field input{margin-top:2px;width:16px;height:16px;flex:none;accent-color:var(--acc,#e01a24)}
.consent-field a{color:var(--acc,#e01a24);text-decoration:underline}
.consent-erasure{font-size:11.5px;line-height:1.5;color:hsl(var(--foreground)/0.55);margin:-6px 0 12px}
.consent-erasure a{color:var(--acc,#e01a24);text-decoration:underline}
.consent-field.is-missing{color:var(--accent-1,#FF3B3B)}
.consent-field.is-missing input{outline:2px solid var(--accent-1,#FF3B3B);outline-offset:2px}
.consent-en{display:block;margin-top:3px;font-size:11px;color:hsl(var(--foreground)/0.45)}
/* footer legal links */
.contact__legal a{color:hsl(var(--foreground)/0.62);text-decoration:none}
.contact__legal a:hover{color:hsl(var(--foreground)/0.95)}   /* no hover underline (site rule) — brighten only */

/* Data-deletion request form (used on /data-deletion AND inside the AJAX modal) */
.dd__what{background:hsl(var(--foreground)/0.04);border:1px solid hsl(var(--line)/0.18);border-radius:12px;padding:14px 16px;margin:14px 0 18px;font-size:13px;line-height:1.6}
.dd__what ul{margin:8px 0 6px 1.1em}
.dd__what li{margin-bottom:4px;color:hsl(var(--foreground)/0.82)}
.dd__what em{color:hsl(var(--foreground)/0.5);font-style:italic}
.dd__note{margin:8px 0 0;font-size:12px;color:hsl(var(--foreground)/0.6)}
.dd__form{display:flex;flex-direction:column;gap:12px;margin:6px 0 14px}
.dd__field{display:flex;flex-direction:column;gap:5px;font-size:12px;color:hsl(var(--foreground)/0.7)}
.dd__field i{color:var(--accent-1,#FF3B3B);font-style:normal}
.dd__opt{color:hsl(var(--foreground)/0.4)}
.dd__field input,.dd__field textarea{width:100%;background:hsl(var(--foreground)/0.04);border:1px solid hsl(var(--line)/0.3);border-radius:9px;padding:10px 12px;color:hsl(var(--foreground)/0.95);font:inherit;font-size:14px}
.dd__field input:focus,.dd__field textarea:focus{outline:none;border-color:var(--acc,#e01a24)}
.dd__consent{display:flex;gap:10px;align-items:flex-start;font-size:12px;line-height:1.5;color:hsl(var(--foreground)/0.72);cursor:pointer}
.dd__consent input{margin-top:2px;width:16px;height:16px;flex:none;accent-color:var(--acc,#e01a24)}
.dd__consent.is-missing{color:var(--accent-1,#FF3B3B)}
.dd__consent.is-missing input{outline:2px solid var(--accent-1,#FF3B3B);outline-offset:2px}
.dd__btn{margin-top:2px;border:0;border-radius:10px;padding:12px 16px;font-weight:700;font-size:13px;cursor:pointer;background:#fff;color:#000;font-family:inherit}
.dd__btn:hover{filter:brightness(.94)}
.dd__btn:disabled{opacity:.55;cursor:default}
.dd__msg{margin:4px 0 0;font-size:13px;line-height:1.5;padding:10px 12px;border-radius:9px}
.dd__msg--ok{background:rgba(20,160,90,.14);color:#37d08a;border:1px solid rgba(40,200,120,.28)}
.dd__contact{font-size:12.5px;color:hsl(var(--foreground)/0.6)}
.dd__contact a{color:var(--acc,#e01a24)}
/* in the modal, give the form content comfortable padding */
#modalContent .dd{padding:4px 2px}
#modalContent .dd__h1{font-family:var(--font-display);font-weight:700;font-size:1.5rem;margin:0 0 10px}
#modalContent .dd__eyebrow{font-family:var(--font-pixel);font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--acc,#e01a24);margin-bottom:8px}
#modalContent .dd__p{font-size:13.5px;line-height:1.65;color:hsl(var(--foreground)/0.85);margin:0 0 .8em}
#modalContent .dd__p--en{color:hsl(var(--foreground)/0.5);font-size:12px}
#modalContent .dd__p a,#modalContent .dd__what a{color:var(--acc,#e01a24);text-decoration:underline}

@font-face {
  font-family: "Seventy Nine Display";
  src: url("assets/fonts/seventy-nine-display.otf") format("opentype");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
/* Headings — chunky arcade pixel display font */
@font-face {
  font-family: "Bolds Pixels";
  src: url("assets/fonts/BoldsPixels.ttf") format("truetype");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
/* Body / reading text — RD Chulajaruek (Latin + Thai) */
@font-face {
  font-family: "RD Chulajaruek";
  src: url("assets/fonts/RDChulajaruek.ttf") format("truetype");
  font-weight: 400 700; font-style: normal; font-display: swap;
}

:root {
  /* cool black & white */
  --background: 0 0% 0%;            /* solid black (stripe texture removed) */
  --surface: 0 0% 9%;
  --foreground: 0 0% 96%;       /* cool white */
  --hero-sub: 0 0% 74%;

  /* monochrome ramp (token names kept; values now grayscale/cool-white) */
  --gold-300: 0 0% 86%;
  --gold-400: 0 0% 78%;
  --gold-500: 0 0% 70%;
  --gold-600: 0 0% 52%;
  --gold-700: 0 0% 40%;
  --cream: 0 0% 95%;
  --ice: 0 0% 90%;

  /* ink scale */
  --ink-1: 0 0% 96%;
  --ink-2: 0 0% 66%;
  --ink-3: 0 0% 42%;
  --line: 0 0% 100%;            /* used with low alpha for hairlines */

  /* flat monochrome “gradients” (kept as vars for existing refs) */
  --jewel: #f5f5f5;                /* solid white button */
  --silver: linear-gradient(180deg, #ffffff 0%, #a2a2a2 100%);

  /* dynamic seamless accent gradient (signature colour over the B&W) */
  --accent-grad: linear-gradient(100deg, #C2161F 0%, #E11D2A 26%, #FF5252 52%, #A00D16 76%, #C2161F 100%);
  --accent-1: #C2161F;
  --accent-2: #E11D2A;
  --accent-3: #A00D16;

  /* RD Chulajaruek reads cleanly for Latin + Thai; Tahoma etc. as fallbacks */
  --font-body: "RD Chulajaruek", "Tahoma", "Leelawadee UI", "Noto Sans Thai", Archivo, system-ui, sans-serif;
  --font-display: "Bolds Pixels", "Seventy Nine Display", "Archivo", system-ui, sans-serif;
  --font-pixel: "Press Start 2P", "Tahoma", monospace;
  /* arcade 3D extrude for headings (scales with font-size via em).
     --arcade-depth is set by the CMS Appearance theme (0 = effect off). */
  --arcade-ink: #2a2a2a;
  --arcade-ink-deep: #161616;
  --arcade-depth: 1;
  /* font-size scale multipliers (CMS Appearance sliders override these) */
  --fs-display: 1;
  --fs-body: 1;
  --fs-pixel: 1;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
html, body, #root { background: hsl(var(--background)); }
body {
  font-family: var(--font-body);
  color: hsl(var(--foreground));
  font-size: calc(16px * var(--fs-body));
  /* RD Chulajaruek reads best antialiased; the 8-bit feel now lives in the
     pixel display font + arcade headings + Press Start 2P labels. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}
/* keep the tiny pixel labels crisp (they're bitmap-style) + scalable */
.badge, .nav-center a, .mm-search, .mobile-menu a, [class*="pixel"],
.proj-meta .cat, .contact__eyebrow, .subpage__eyebrow, .hm-label {
  -webkit-font-smoothing: none;
}
/* top-nav menu size — CMS Nav appearance sets --nav-fs (rem, root-relative). #nav specificity
   beats the Labels/pixel role CSS (which lists .nav-center a with !important). */
#nav .nav-center a { font-size: calc((var(--nav-fs, calc(13px * var(--fs-pixel) * var(--nav-scale, 1)))) * 1.2) !important; } /* nav menu 20% bigger */
.badge { font-size: calc(9px * var(--fs-pixel) * var(--fs-chip, 1)); }
.mobile-menu a, .mm-search { font-size: calc(15px * var(--fs-pixel)); }
.contact__eyebrow, .subpage__eyebrow, .pb-eyebrow, .site-faq__eyebrow { font-size: calc(10px * var(--fs-pixel) * var(--fs-eyebrow, 1)); }

/* ===== Arcade-machine 3D extrude on headings (Bolds Pixels) =====
   Stepped dark drop-shadows give the chunky extruded depth from the
   reference. Uses em so it scales with each heading's size, and works on
   gradient-clipped text (drop-shadow follows the glyph alpha). */
.headline, .h-section, .subpage__head h1, .article__h1,
.contact__title, .pb-hero__title, .pb-hero h1 {
  filter:
    drop-shadow(calc(0.018em * var(--arcade-depth)) calc(0.018em * var(--arcade-depth)) 0 var(--arcade-ink))
    drop-shadow(calc(0.036em * var(--arcade-depth)) calc(0.036em * var(--arcade-depth)) 0 var(--arcade-ink))
    drop-shadow(calc(0.052em * var(--arcade-depth)) calc(0.052em * var(--arcade-depth)) 0 var(--arcade-ink-deep));
}
/* a touch more depth for the giant hero title */
.headline { filter:
    drop-shadow(calc(0.014em * var(--arcade-depth)) calc(0.014em * var(--arcade-depth)) 0 var(--arcade-ink))
    drop-shadow(calc(0.028em * var(--arcade-depth)) calc(0.028em * var(--arcade-depth)) 0 var(--arcade-ink))
    drop-shadow(calc(0.044em * var(--arcade-depth)) calc(0.044em * var(--arcade-depth)) 0 var(--arcade-ink))
    drop-shadow(calc(0.06em * var(--arcade-depth)) calc(0.06em * var(--arcade-depth)) 0 var(--arcade-ink-deep)); }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
/* deter image downloading: block native drag + selection (bg-image art is already unsaveable) */
img { -webkit-user-drag: none; -khtml-user-drag: none; -moz-user-drag: none; user-drag: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
img { display: block; max-width: 100%; }
::selection { background: var(--accent-2); color: #fff; }

/* dynamic seamless accent: animated gradient text */
.accent-text {
  background-image: var(--accent-grad); background-size: 280% 100%;
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent;
  animation: accentPan 9s linear infinite;
}
@keyframes accentPan { from { background-position: 0% 50%; } to { background-position: 280% 50%; } }
@media (prefers-reduced-motion: reduce) { .accent-text { animation: none; } }

/* global CRT scanline + pixel grain texture (very subtle, unifies the 8-bit concept) */
.fx-scan {
  position: fixed; inset: 0; z-index: 90; pointer-events: none; opacity: 0.5;
  background-image: repeating-linear-gradient(to bottom, rgba(255,255,255,0.035) 0 1px, transparent 1px 3px);
  mix-blend-mode: overlay;   /* kept: the user prizes this scanline look (removing the blend was
                                reverted before). It IS a GPU cost; only remove with explicit OK. */
}

/* ---- animation power-saving (driven by site.js) ----
   The site has many perpetual CSS animations (gradient-text "accentPan" on every
   section heading + service card, blinking dots, the FAB pulse…). Each one keeps
   the GPU busy forever, even off-screen or in a background tab. site.js adds
   `.anim-paused` to elements scrolled out of view, and `html.tab-hidden` when the
   tab is hidden — these rules then freeze those animations so the laptop stays cool. */
.anim-paused,
.anim-paused *,
.anim-paused::before,
.anim-paused::after { animation-play-state: paused !important; }
html.tab-hidden *,
html.tab-hidden *::before,
html.tab-hidden *::after { animation-play-state: paused !important; }

/* ---------- liquid glass ---------- */
.liquid-glass {
  background: rgba(255, 255, 255, 0.01);
  background-blend-mode: luminosity;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: none; box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12);
  position: relative; overflow: hidden;
}
.liquid-glass::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1.4px;
  background: linear-gradient(180deg, rgba(255,255,255,0.45) 0%, rgba(255,255,255,0.14) 20%, rgba(255,255,255,0) 40%, rgba(255,255,255,0) 60%, rgba(255,255,255,0.14) 80%, rgba(255,255,255,0.45) 100%);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}

/* ---------- buttons (8-bit B&W) — solid jewel + ghost, NO liquid glass ----------
   Hover physics (shared site-wide, see --lift vars): the button shifts smoothly UP a
   little on hover (no scaling); on release it drops and bounces once before settling.
   The bounce comes from the BASE transition (overshoot bezier on the way back to 0);
   the smooth lift comes from a faster ease-out transition applied only while :hover. */
:root { --lift-up: transform .22s cubic-bezier(.22,1,.36,1); --lift-down: transform .5s cubic-bezier(.34,1.7,.5,1); }
.btn-jewel {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  border-radius: 999px; padding: 16px 28px; font-family: var(--font-pixel); font-weight: 400; font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--background));
  background: var(--jewel);
  box-shadow: 4px 4px 0 0 hsl(var(--foreground)/0.32);
  transition: var(--lift-down), box-shadow 0.18s, background 0.15s, color 0.15s;
}
.btn-jewel:hover { transform: translateY(-6px); box-shadow: 4px 10px 0 0 hsl(var(--foreground)/0.45); transition: var(--lift-up), box-shadow 0.18s, background 0.15s, color 0.15s; }
.btn-jewel:active { transform: translateY(-2px); }
.btn-jewel .arrow { transition: transform 0.2s steps(3); }
.btn-jewel:hover .arrow { transform: translateX(4px); }

/* Ghost button: clear fill with a thick outline. Hover lifts (no scale) + a touch more
   inner fill; release drops + bounces once. */
.btn-ghost {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  border-radius: 999px; padding: 14px 24px; font-family: var(--font-pixel); font-weight: 400; font-size: 10px;
  letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--foreground));
  border: 4px solid hsl(var(--foreground)/0.8);
  background: hsl(var(--foreground)/0.07);
  transition: var(--lift-down), background 0.15s, color 0.15s, border-color 0.15s;
}
.btn-ghost:hover { transform: translateY(-6px); background: hsl(var(--foreground)/0.16); border-color: hsl(var(--foreground)); transition: var(--lift-up), background 0.15s, color 0.15s, border-color 0.15s; }
.btn-ghost:active { transform: translateY(-2px); }

/* ---------- sticky navbar ---------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: 18px clamp(20px, 4.5vw, 40px); gap: 20px;
  transition: background 0.3s ease, border-color 0.3s ease, padding 0.3s ease, backdrop-filter 0.3s ease;
  border-bottom: 1px solid transparent;
}
.nav .brand { justify-self: start; }
.nav .nav-right { justify-self: end; display: flex; align-items: center; gap: 12px; }
.nav.scrolled { padding-top: 12px; padding-bottom: 12px; }
/* the full-width "top bar" (background + divider) only shows when enabled in Appearance —
   and only once the hero is about to leave the scene (.past-hero, set by site.js), so it
   never covers the hero. Off by default → just the floating glass nav pill. */
/* Top bar reveals on scroll as a flat DARK rgba(0,0,0,.85) bar, no blur — painted as a full-width
   layer BEHIND the nav content (::before), with a PILL-SHAPED hole clipped out around the glass pill.
   Through that stadium hole the pill's own backdrop-filter samples the LIVE page (clear background
   under the pill), so the pill stands proud of the bar. The stadium clip path is computed from the
   live pill rect by site.js and handed in as --nav-clip. */
.nav.nav--bar.past-hero { background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; }
.nav.nav--bar.past-hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: rgba(0,0,0,0.85);
  clip-path: var(--nav-clip, none); -webkit-clip-path: var(--nav-clip, none);
}
.nav.nav--bar.past-hero > * { position: relative; z-index: 1; }   /* logo, pill, search sit ABOVE the dark bar layer */
.brand img { height: 64px; width: auto; transition: height 0.3s ease; }
.nav.scrolled .brand img { height: 48px; }
/* See-through liquid-glass pill: low tint + strong blur so ALL page content scrolling
   behind the bar reads through it (the "glass" is backdrop-filter, which samples the live
   page behind a fixed element — WebGL refraction can't capture a fixed overlay). */
.nav-center { display: flex; align-items: center; gap: 5px; padding: 7px; border-radius: var(--nav-radius, 999px); justify-self: center;
  position: relative;
  background: var(--nav-bg, rgba(10,12,16,0.22)); border: 1px solid hsl(var(--foreground)/0.14);
  -webkit-backdrop-filter: blur(18px) saturate(1.7) brightness(1.05); backdrop-filter: blur(18px) saturate(1.7) brightness(1.05);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 10px 30px -14px rgba(0,0,0,0.7);
  transition: background 0.3s ease, border-color 0.3s ease; }
.nav.scrolled .nav-center { background: var(--nav-bg-scrolled, rgba(10,12,16,0.30)); border-color: hsl(var(--foreground)/0.16); }
/* glass OFF (CMS Nav appearance) → a solid dark pill, no backdrop blur */
.nav--noglass .nav-center { background: var(--nav-bg-solid, rgba(12,14,18,0.92)) !important; -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
/* traveling "magic" highlight that follows the hovered item (positioned by JS): fades IN on
   first hover, TRAVELS between items, and fades OUT 1s after the pointer leaves the bar. */
/* GPU-composited: position via translate3d (not left), so travel between items stays buttery.
   JS snaps it to the first hovered item before fading in (no sweep from the left edge). */
.nav-magic { position: absolute; top: 50%; left: 0; width: 0; height: calc(100% - 12px);
  transform: translate3d(0, -50%, 0); will-change: transform, width;
  border-radius: var(--nav-radius, 999px); background: var(--nav-hl-bg, #ffffff); border: 1px solid rgba(0,0,0,0.06);
  box-shadow: 0 6px 18px -8px rgba(0,0,0,0.55);
  opacity: 0; pointer-events: none; z-index: 0;
  transition: transform .5s cubic-bezier(.22,1,.36,1), width .5s cubic-bezier(.22,1,.36,1), opacity .3s ease; }
@media (prefers-reduced-motion: reduce) { .nav-magic { transition: opacity .2s ease; } }
.nav-center a {
  display: inline-flex; align-items: center; gap: 6px; position: relative; z-index: 1;
  color: var(--nav-text, hsl(var(--ice) / 0.86)); font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; padding: 11px 20px; border-radius: var(--nav-radius, 999px);
  transition: color 0.3s ease;   /* matches the .nav-magic chip's opacity fade so text + chip change together */
}
/* hovered item sits on the white travelling chip → its text turns black */
.nav-center a:hover { color: #0a0a0a; }
/* JS keeps .nav-lit on the item the chip is under, and REMOVES it on the same delayed timeout
   that fades the chip out — so the text stays black for as long as the chip is lit and then
   fades back in lock-step with the chip (instead of snapping light the instant you un-hover). */
.nav-center a.nav-lit { color: #0a0a0a; }
.nav-center a.is-active { color: #fff; }
.nav-center a.is-active.nav-lit { color: #0a0a0a; }
.nav-cta { padding: 11px 22px; font-size: 12px; }
.nav-search-btn { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; border: 1px solid hsl(var(--line)/0.18); color: hsl(var(--foreground)/0.85); background: hsl(var(--foreground)/0.04); transition: background 0.18s, color 0.18s; }
.nav-search-btn svg { width: 17px; height: 17px; }
.nav-search-btn:hover { background: hsl(var(--foreground)/0.12); color: hsl(var(--foreground)); }
/* search overlay */
.site-search { position: fixed; inset: 0; z-index: 120; }
.site-search[hidden] { display: none; }
.site-search__scrim { position: absolute; inset: 0; background: hsl(var(--background)/0.78); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.site-search__panel { position: relative; max-width: 680px; margin: clamp(80px, 13vh, 150px) auto 0; padding: 0 20px; }
.site-search__bar { display: flex; align-items: center; gap: 12px; background: hsl(var(--surface)); border: 1px solid hsl(var(--line)/0.22); border-radius: 14px; padding: 6px 14px; }
.site-search__bar svg { width: 20px; height: 20px; color: hsl(var(--foreground)/0.5); flex: none; }
.site-search__bar input { flex: 1; background: none; border: none; color: hsl(var(--foreground)); font-size: 1.1rem; padding: 16px 4px; font-family: inherit; }
.site-search__bar input:focus { outline: none; }
/* kill the browser's native search-field decorations (the stray glyph on the right) */
.site-search__bar input { -webkit-appearance: none; appearance: none; }
.site-search__bar input::-webkit-search-decoration,
.site-search__bar input::-webkit-search-cancel-button,
.site-search__bar input::-webkit-search-results-button,
.site-search__bar input::-webkit-search-results-decoration { -webkit-appearance: none; appearance: none; display: none; }
.site-search__bar button { font-size: 26px; color: hsl(var(--foreground)/0.6); width: 36px; }
.site-search__results { margin-top: 10px; background: hsl(var(--surface)); border: 1px solid hsl(var(--line)/0.18); border-radius: 14px; overflow: hidden; display: none; }
.site-search__results.show { display: block; }
.ss-row { display: block; padding: 13px 18px; border-bottom: 1px solid hsl(var(--line)/0.1); }
.ss-row:last-child { border-bottom: none; }
.ss-row:hover, .ss-row.on { background: hsl(var(--foreground)/0.06); }
.ss-row .t { font-weight: 600; font-size: 0.98rem; color: hsl(var(--foreground)); }
.ss-row .m { font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0.05em; text-transform: uppercase; color: hsl(var(--accent-2)); }
.ss-all { display: block; text-align: center; padding: 12px; font-size: 0.85rem; color: hsl(var(--foreground)/0.7); background: hsl(var(--foreground)/0.03); }
.ss-empty { padding: 16px 18px; color: hsl(var(--foreground)/0.55); font-size: 0.9rem; }
.mm-search { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-pixel); font-size: 15px; letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--foreground)/0.8); padding: 16px 20px; }
/* pagination */
.pager { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; align-items: center; margin-top: clamp(40px, 6vh, 70px); }
.pager a { display: inline-grid; place-items: center; min-width: 42px; height: 42px; padding: 0 14px; border-radius: 10px; border: 1px solid hsl(var(--line)/0.18); color: hsl(var(--foreground)/0.85); font-size: 0.95rem; font-weight: 600; transition: background 0.15s, border-color 0.15s; }
.pager a:hover { background: hsl(var(--foreground)/0.06); border-color: hsl(var(--line)/0.4); }
.pager a.on { background: var(--accent-grad); background-size: 280% 100%; color: #fff; border: none; animation: accentPan 9s linear infinite; }
@media (max-width: 900px) { .nav-search-btn { display: none; } }
/* hamburger (mobile) */
/* Floating random-SVG menu button (mobile) — always pinned top-right, ABOVE the drawer (z>99),
   so it stays visible/tappable when the menu is open. A gentle idle bob + hover wiggle. */
.fab-menu { display: none; position: fixed; top: clamp(10px, 1.6vh, 16px); right: clamp(10px, 3vw, 18px); z-index: 120; flex-direction: row; align-items: center; gap: 6px; padding: 4px; background: none; border: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; } /* MENU caption + icon on one row */
.fab-menu__icon { width: clamp(44px, 12vw, 54px); height: auto; display: block; filter: drop-shadow(0 6px 14px rgba(0,0,0,0.5)); transform-origin: 50% 60%; will-change: transform; animation: fabBob 3.4s ease-in-out infinite; }
/* "MENU" caption stacked one letter per line, sitting ABOVE the icon (moved from below). */
.fab-menu__cap { display: flex; flex-direction: column; align-items: center; gap: 1px; font-family: var(--font-pixel); font-size: 7px; line-height: 1; letter-spacing: 0.02em; text-transform: uppercase; color: hsl(var(--foreground)/0.85); text-shadow: 0 1px 6px rgba(0,0,0,0.7); }
.fab-menu__cap span { display: block; }
.fab-menu__cursor { width: 10px; height: 10px; margin-bottom: 2px; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6)); }
.fab-menu:hover .fab-menu__icon, .fab-menu:focus-visible .fab-menu__icon { animation: fabWiggle 0.55s ease-in-out; }
.fab-menu:active .fab-menu__icon { transform: scale(0.88); animation: none; }
/* no heavy accent box on this icon button — keyboard focus gets a subtle ring on the icon only */
.fab-menu, .fab-menu:focus, .fab-menu:focus-visible { background: none !important; border: 0 !important; box-shadow: none !important; outline: none !important; }
.fab-menu:focus-visible .fab-menu__icon { outline: 2px solid hsl(var(--foreground)/0.55); outline-offset: 3px; border-radius: 8px; }
@media (prefers-reduced-motion: reduce) { .fab-menu__icon { animation: none; } }
@keyframes fabBob { 0%, 100% { transform: translateY(0) rotate(-4deg); } 50% { transform: translateY(-5px) rotate(4deg); } }
@keyframes fabWiggle { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(-14deg); } 60% { transform: rotate(12deg); } }
/* mobile menu overlay */
/* closed = visibility:hidden + NO backdrop-filter, so the (mobile-only) full-screen blur backing
   isn't held while it's invisible; the blur + paint are applied only while the menu is open. */
.mobile-menu { position: fixed; inset: 0; z-index: 99; background: hsl(var(--background)/0.97); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, visibility 0s linear 0.3s; }
body.menu-open .mobile-menu { opacity: 1; visibility: visible; pointer-events: auto; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); transition: opacity 0.3s ease, visibility 0s; }
.mobile-menu a { font-family: var(--font-pixel); font-size: 15px; letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--foreground)/0.8); padding: 16px 20px; transition: color 0.18s ease; }
.mobile-menu a:hover { color: hsl(var(--foreground)); }
.mobile-menu .mm-cta { margin-top: 16px; }
/* Search sits pinned at the bottom of the mobile menu (links stay centred above it). */
.mobile-menu .mm-search { position: absolute; left: 50%; bottom: clamp(28px, 7vh, 60px); transform: translateX(-50%); border: 1px solid hsl(var(--foreground)/0.22); border-radius: 999px; }
@media (max-width: 900px) {
  .nav { grid-template-columns: 1fr auto; }
  .nav-center { display: none; }
  .nav .nav-right .nav-cta { display: none; }
  .nav .nav-search-btn { display: none; }   /* search now lives in the drawer (bottom) + the floating menu owns the corner */
  .fab-menu { display: flex; }
  /* Dark rgba(0,0,0,.85) top bar on mobile too, so the brand + menu read clearly over any section. */
  .nav.nav--bar.past-hero { background: rgba(0,0,0,0.85); border-bottom: 0; }   /* solid: mobile has no pill */
  .nav.nav--bar.past-hero::before { display: none; }   /* no pill → no clipped bar layer, the solid nav bg is enough */
}

/* ===== MOBILE LANDSCAPE (landscape orientation + short screen — i.e. a phone turned sideways;
   desktop/tablets are taller than 500px so they never match). The TOP bar becomes a LEFT bar:
   LOGO pinned at the top, the floating MENU button at the bottom, logo / MENU / icon all the same
   width so they line up down the strip. The vertical "M E N U" flips to a single line
   (cursor + "MENU") sitting above the SVG icon. ===== */
@media (orientation: landscape) and (max-height: 500px) {
  .nav-center { display: none; }                     /* also hide on wide landscape phones (>900px) */
  .nav .nav-search-btn, .nav .nav-cta { display: none; }
  .fab-menu { display: flex; }

  /* Shift the whole page to the RIGHT of the bar (the bar does not float over content). */
  #root { padding-left: 66px; }

  /* nav → STICKY vertical LEFT bar (always visible, solid — not tied to hero scroll), logo top */
  .nav {
    display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
    grid-template-columns: none; gap: 0;
    top: 0; left: 0; right: auto; bottom: 0;
    width: 66px; height: 100vh; height: 100svh; padding: 14px 6px;
    background: rgba(10,11,14,0.94); border-bottom: 0; border-right: 1px solid hsl(var(--foreground)/0.18);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .nav .brand { justify-self: auto; }
  .nav .brand img, .nav.scrolled .brand img { width: 46px; height: auto; }   /* logo — the shared size */
  /* keep the bar solid whether or not the hero has scrolled away */
  .nav.nav--bar.past-hero { background: rgba(10,11,14,0.94); border-bottom: 0; border-right: 1px solid hsl(var(--foreground)/0.18); }
  .nav.nav--bar.past-hero::before { display: none; }   /* landscape left bar is solid, no pill hole */

  /* floating MENU → bottom of the left bar; cursor + "MENU" on ONE line, above the icon */
  .fab-menu {
    top: auto; right: auto; left: 0; bottom: clamp(10px, 6vh, 22px);
    width: 66px; flex-direction: column; gap: 4px;
  }
  .fab-menu__cap { flex-direction: row; gap: 0; letter-spacing: 0.05em; font-size: 8px; }   /* one-line MENU */
  .fab-menu__cursor { width: 11px; height: 11px; margin: 0 3px 0 0; }                        /* cursor on the LEFT */
  .fab-menu__icon { width: 46px; }                                                           /* same size as the logo */
}

/* ===========================================================
   HERO
   =========================================================== */
.hero-wrap { position: relative; min-height: 100vh; min-height: 100svh; overflow: hidden; display: flex; flex-direction: column; }
/* Bottom fade — EVERY hero background (animation or image) melts into the page.
   Sits above the scene (z0) but below the content (z10), so it only dims the
   art. Colour is the CMS-chosen --hero-fade (default = page background ≈ black). */
.hero-wrap::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: clamp(120px, 20vh, 240px); z-index: 1; pointer-events: none; background: linear-gradient(to bottom, transparent, var(--hero-fade, #000)); }
/* Bright animated GPU scenes (esp. Flow Field) have light streaks that run to the very
   bottom edge — give them a taller fade that reaches solid black before the edge, so the
   lower ~20% clearly melts to black. */
.hero-wrap[data-hero-scene="flowfield"]::after,
.hero-wrap[data-hero-scene="flame"]::after,
.hero-wrap[data-hero-scene="swirl"]::after,
.hero-wrap[data-hero-scene="aurora"]::after {
  height: clamp(220px, 34vh, 460px);
  background: linear-gradient(to bottom, transparent 0%, var(--hero-fade, #000) 80%, var(--hero-fade, #000) 100%);
}
/* Bottom REVEAL for STILL-IMAGE heroes — fade the hero photo to TRANSPARENT so the real page
   background (incl. a "Space & Fading Colour" pattern) shows through and continues seamlessly
   into the section below. Only heroes that actually render a static <img> qualify; live GPU
   scenes own their own background and keep the colour fade above. */
.hero-wrap:has(.hero-bg > img)::after { content: none; }
.hero-wrap:has(.hero-bg > img) .hero-bg { -webkit-mask-image: linear-gradient(to bottom, #000 80%, transparent 100%); mask-image: linear-gradient(to bottom, #000 80%, transparent 100%); }
#bomb { display: none; }
/* static hero background image (replaces the old laggy animated canvas) */
.hero-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
/* the lazy-image skeleton (lazyimg.css .lz-skel/.lz-skel>img) sets position:relative,
   which would drop the hero background + its image into flow and shove the hero content
   below it — keep the hero background absolutely filling its container regardless. */
.hero-wrap .hero-bg, .hero-wrap .hero-bg.lz-skel { position: absolute; }
.hero-wrap .hero-bg > img, .hero-wrap .hero-bg.lz-skel > img { position: absolute; inset: 0; }
.hero-bg img { width: 100%; height: 100%; object-fit: cover; object-position: center;
  display: block; image-rendering: auto; image-rendering: -webkit-optimize-contrast; }
.hero-bg::after { content: ""; position: absolute; inset: 0;
  background: hsl(var(--background) / var(--hero-dark, 0.45)); }
/* YouTube video hero background (CMS → Hero → Background video). Full-bleed cover at z-index 0,
   so the hero's black bottom fade (.hero-wrap::after, z1) + vignette (z1) + content (z10) all sit
   on top — same look as the image/animation heroes. Decorative: pointer-events + tabindex off.
   The 100vw/56.25vw pair covers wide viewports; the 177.78vh/100vh mins cover tall/portrait ones. */
/* container-type:size lets the children measure themselves against THIS box (the hero), so the video
   cover math is exact regardless of the viewport — the fix for the old overflow. */
.hero-video { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none;
  background: hsl(var(--background)); container-type: size; }
/* SELF-HOSTED <video> — the clean path: object-fit:cover fills the hero exactly (crops the excess,
   never overflows), just like a background-size:cover image. No ads, no chrome, seamless loop. */
.hero-video__v { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: center; border: 0; z-index: 1; pointer-events: none;
  /* Hidden until a REAL decoded frame exists (hero-video-fps.js adds .is-playing), then it fades in
     over the poster. Prevents the <video> painting its black decode box before the first frame — the
     poster image shows continuously, the video just appears on top once it's actually playing. */
  opacity: 0; transition: opacity .45s ease; }
.hero-video.is-playing .hero-video__v { opacity: 1; }
/* YOUTUBE iframe — exact 16:9 COVER using container units so it fills the hero in both axes and is
   clipped by overflow:hidden (no overflow). width = max(hero width, hero height × 16/9); aspect-ratio
   sets the height. The tiny scale(1.02) hides a sub-pixel seam. YouTube's own title/controls are off
   (controls=0, modestbranding), and pointer-events:none blocks any hover UI. Container units fall back
   safely: if unsupported, min-width/height still cover. */
.hero-video__f { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(1.02);
  aspect-ratio: 16 / 9; width: max(100cqw, 177.78cqh); height: auto;
  min-width: 100%; min-height: 100%; border: 0; z-index: 1; pointer-events: none; }
/* IMAGE FALLBACK: the still-image poster sits UNDER the iframe (z0) — visible while the video loads,
   and it is what shows when the video is hidden (mobile / reduced-motion, below). */
.hero-video__poster { position: absolute; inset: 0; z-index: 0; background-size: cover; background-position: center; background-repeat: no-repeat; }
.hero-video::after { content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: hsl(var(--background) / var(--hero-dark, 0.45)); }
/* FORCED-FPS canvas — the video re-sampled to data-fps by hero-video-fps.js. Same cover box as the
   <video>; revealed only once it has painted a real frame (.is-fps) so there is no blank flash, and
   the raw <video> is then hidden (still decoding for source frames). */
.hero-video__c { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  object-position: center; z-index: 1; pointer-events: none; display: none; }
.hero-video.is-fps .hero-video__c { display: block; }
.hero-video.is-fps .hero-video__v { opacity: 0; }
/* On touch / small screens / reduced-motion, HIDE the video and show the poster image instead —
   a reliable image fallback (mobile autoplay is unreliable + video is data-heavy). */
@media (max-width: 680px), (hover: none) and (pointer: coarse), (prefers-reduced-motion: reduce) {
  .hero-video__f, .hero-video__v, .hero-video__c { display: none; }
}
/* glowing fluid particle hero background (hero-particles.js) */
.hero-particles-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* classic 8-bit dot-matrix hero background (hero-grid.js) */
.hero-grid-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* interactive mouse-extrusion particle grid (hero-extrude.js) */
.hero-extrude-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* foggy flame background (hero-flame.js) */
.hero-flame-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* golden swirl vortex background (hero-swirl.js) */
.hero-swirl-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* northern-lights GPU shader background (hero-aurora.js) */
.hero-aurora-canvas { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
.hero-aurora-dark { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
/* the "no image yet" placeholder fills its container (cover) wherever it appears
   — !important so it wins over any same-specificity container rule (cards, etc.) */
img[src$="placeholder-image.svg"] { object-fit: cover !important; object-position: center !important; }
.hero-vignette {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(58% 48% at 50% 46%, transparent 28%, hsl(var(--background)/0.55) 100%),
    radial-gradient(42% 30% at 50% 50%, hsl(var(--background)/0.5), transparent 72%);
}
/* scroll cue */
.hero-scroll { position: absolute; left: 50%; bottom: clamp(92px, 13vh, 128px); transform: translateX(-50%); width: 24px; height: 38px; border: 2px solid hsl(var(--foreground)/0.28); border-radius: 14px; z-index: 6; pointer-events: none; }
.hero-scroll span { position: absolute; left: 50%; top: 7px; width: 4px; height: 8px; border-radius: 1px; background: hsl(var(--foreground)); transform: translateX(-50%); animation: scrollDot 1.6s steps(6) infinite; }
@keyframes scrollDot { 0%, 100% { opacity: 0; transform: translate(-50%, 0); } 40% { opacity: 1; } 70% { opacity: 1; transform: translate(-50%, 12px); } }
/* detonate button */
.hero-detonate { display: inline-flex; align-items: center; gap: 9px; padding: 16px 26px; border-radius: 999px; font-weight: 600; font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--foreground)); border: 1.5px solid hsl(var(--foreground)/0.22); background: hsl(var(--foreground)/0.04); transition: var(--lift-down), border-color .2s, box-shadow .2s, color .2s; }
.hero-detonate:hover { color: hsl(var(--background)); background: hsl(var(--foreground)); border-color: transparent; transform: translateY(-6px); transition: var(--lift-up), border-color .2s, box-shadow .2s, color .2s; }
.hero-detonate:active { transform: translateY(-2px); }
/* letter-blast entrance — visible by default; animate FROM hidden so the
   headline is never stuck invisible if the animation/JS doesn't run */
.blast-letter { display: inline-block; white-space: pre; will-change: transform, opacity, filter; }
@media (prefers-reduced-motion: no-preference) {
  .headline.blast.lit .blast-letter { animation: blastIn 0.72s cubic-bezier(.2,.85,.25,1) both; animation-delay: var(--d, 0s); }
}
@keyframes blastIn { from { opacity: 0; transform: translateY(42px) scale(0.3); filter: blur(14px); } to { opacity: 1; transform: none; filter: blur(0); } }

.hero { position: relative; z-index: 10; flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 96px 24px 0; }
.badge { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 28px; padding: 9px 16px; border-radius: 3px; border: 1px solid hsl(var(--line)/0.16); font-family: var(--font-pixel); font-size: 9px; font-weight: 400; letter-spacing: 0.04em; text-transform: uppercase; color: hsl(var(--foreground)/0.86); background: rgba(0,0,0,0.8); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.badge .dot { width: 7px; height: 7px; border-radius: 1px; background: hsl(var(--foreground)); animation: blink 1.1s steps(2) infinite; }
@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.2; } }

/* Eyebrow/tag over titles → leading bullet dot, like the hero badge. */
.subpage__eyebrow, .pb-eyebrow, .contact__eyebrow, .site-faq__eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
}
.site-faq__eyebrow { justify-content: center; }
.subpage__eyebrow::before, .pb-eyebrow::before, .contact__eyebrow::before, .site-faq__eyebrow::before {
  content: ""; width: 7px; height: 7px; border-radius: 1px; flex: none;
  background: hsl(var(--gold-400)); animation: blink 1.1s steps(2) infinite;
}
.headline { font-family: var(--font-display); font-weight: 700; font-size: calc(clamp(60px, 13vw, 184px) * var(--fs-display) * var(--h-mobile, 1)); line-height: 1.02; letter-spacing: -0.024em; padding-top: 0.14em; }
.headline .l1 { display: block; color: hsl(var(--cream)); }
.headline .l2 { display: block; color: hsl(var(--foreground)); }
/* Phones: shrink the big H1/H2 display headings 20% (multiplied into every heading font-size via --h-mobile). */
@media (max-width: 640px) { :root { --h-mobile: 0.8; } }

/* ===== Stair headings — decorative square "stair" dots between the heading lines.
   The dot elements are aria-hidden and carry NO text, so the <h1> source stays one clean
   line for SEO/GEO. Line offsets use position:left (not transform) so they don't fight the
   hero blast animation. Sizes are em-based so they scale with the rendered font; tweak
   --stair-dot / --stair-step (a future Appearance panel can expose these). ===== */
:root { --stair-dot: 0.1em; --stair-step: 0.6em; }
/* base — applies to ANY stair heading (.stair is added by heading-stair.js, so this
   covers the hero .headline AND opted-in two-line section headings .stair-head) */
.stair .l1, .stair .l2, .stair .l3 { position: relative; display: block; }
.stair .stair-dots { display: block; position: relative; width: calc(var(--stair-n, 4) * var(--stair-dot)); height: calc(var(--stair-n, 4) * var(--stair-dot)); margin: 0.06em auto; pointer-events: none; }
.stair .stair-dots i { position: absolute; width: var(--stair-dot); height: var(--stair-dot); background: currentColor; left: calc(var(--n) * var(--stair-dot)); top: calc(var(--n) * var(--stair-dot)); }
/* After the opening animation settles, heading-stair.js picks ONE dot per stair and gives it a
   single random subtle idle effect (rotate / tilt-on-corner / drift left-right / drift up-down).
   transform only (composited); paused off-screen by the global anim-pauser. */
.stair-dots i.sd-live { animation-iteration-count: infinite; animation-timing-function: ease-in-out; will-change: transform; }
.stair-dots i.sd-rot  { animation-name: sdRot;  animation-duration: 5.5s; animation-timing-function: linear; }   /* rotate */
.stair-dots i.sd-tilt { animation-name: sdTilt; animation-duration: 3.6s; transform-origin: 100% 100%; }         /* tilt hinged on a connected corner */
.stair-dots i.sd-lr   { animation-name: sdLR;   animation-duration: 3.2s; }                                     /* drift left/right */
.stair-dots i.sd-ud   { animation-name: sdUD;   animation-duration: 3.2s; }                                     /* drift up/down */
@keyframes sdRot  { to { transform: rotate(360deg); } }
@keyframes sdTilt { 0%,100% { transform: rotate(0deg); } 50% { transform: rotate(-18deg); } }
@keyframes sdLR   { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-50%); } 75% { transform: translateX(50%); } }
@keyframes sdUD   { 0%,100% { transform: translateY(0); } 25% { transform: translateY(-50%); } 75% { transform: translateY(50%); } }
@media (prefers-reduced-motion: reduce) { .stair-dots i.sd-live { animation: none !important; } }
/* after — ASCENDING diagonal (climbs bottom-left → top-right) */
.stair.stair-pos--after .stair-dots i { top: auto; bottom: calc(var(--n) * var(--stair-dot)); }
/* baseline + above — a FLAT horizontal row of squares (all on one line) */
.stair.stair-pos--baseline .stair-dots, .stair.stair-pos--above .stair-dots { width: calc(var(--stair-n, 4) * var(--stair-dot)); height: var(--stair-dot); }
.stair.stair-pos--baseline .stair-dots i, .stair.stair-pos--above .stair-dots i { top: 0; bottom: auto; }
/* column — a VERTICAL column of squares */
.stair.stair-pos--column .stair-dots { width: var(--stair-dot); height: calc(var(--stair-n, 4) * var(--stair-dot)); }
.stair.stair-pos--column .stair-dots i { left: 0; top: calc(var(--n) * var(--stair-dot)); bottom: auto; }
/* between (H1) — the two lines stagger around the descending stair between them */
.stair.stair-pos--between .l1 { left: calc(-3 * var(--stair-step)); }
.stair.stair-pos--between .l2 { left: calc(3 * var(--stair-step)); }
/* 3-line staircase (e.g. the contact "Let's Create / Something / Extraordinary" heading):
   each line steps further right, with a dot block between every pair. */
.stair--3l.stair-pos--between .l1 { left: calc(-4 * var(--stair-step)); }
.stair--3l.stair-pos--between .l2 { left: 0; }
.stair--3l.stair-pos--between .l3 { left: calc(4 * var(--stair-step)); }
.stair--3l.stair-pos--after  .l1 { left: calc(-4 * var(--stair-step)); }
.stair--3l.stair-pos--after  .l2 { left: calc(-1 * var(--stair-step)); }
.stair--3l.stair-pos--after  .l3 { left: calc(2 * var(--stair-step)); }
.stair--3l.stair-pos--front  .l1 { left: calc(2 * var(--stair-step)); }
.stair--3l.stair-pos--front  .l2 { left: calc(3 * var(--stair-step)); }
.stair--3l.stair-pos--front  .l3 { left: calc(4 * var(--stair-step)); }
/* frontstep — a stair of dots IN FRONT of lines 2 & 3 (line 1 has none), stepping down. Each
   line lays its dot block + text in a row; the lines indent progressively so it descends. */
.stair--3l.stair-pos--frontstep { display: block; }
.stair--3l.stair-pos--frontstep .l1 { left: 0; }
.stair--3l.stair-pos--frontstep .l2 { left: calc(1.5 * var(--stair-step)); }
.stair--3l.stair-pos--frontstep .l3 { left: calc(3 * var(--stair-step)); }
.stair-pos--frontstep .l2, .stair-pos--frontstep .l3 { display: flex; align-items: flex-end; justify-content: flex-start; }
.stair-pos--frontstep .l2 .stair-dots, .stair-pos--frontstep .l3 .stair-dots {
  display: inline-block; position: relative; flex: none; vertical-align: bottom;
  width: calc(var(--stair-n, 2) * var(--stair-dot)); height: calc(var(--stair-n, 2) * var(--stair-dot));
  margin: 0 .4em .14em 0;
}
/* after — line 1 leads, line 2 nudged right */
.stair.stair-pos--after .l1 { left: calc(-3 * var(--stair-step)); }
.stair.stair-pos--after .l2 { left: calc(1 * var(--stair-step)); }
/* front/above/column put the block ABOVE the two centred lines; baseline puts it BELOW */
.stair.stair-pos--front, .stair.stair-pos--above, .stair.stair-pos--column, .stair.stair-pos--baseline { display: flex; flex-direction: column; align-items: center; }
.stair.stair-pos--front .l1 { left: calc(2 * var(--stair-step)); }
.stair.stair-pos--front .l2 { left: calc(3 * var(--stair-step)); }
.stair.stair-pos--front .stair-dots, .stair.stair-pos--above .stair-dots, .stair.stair-pos--column .stair-dots { margin: 0 auto .12em; }
.stair.stair-pos--baseline .stair-dots { margin: .12em auto 0; }
/* page-builder section headings opted into stair dots ([data-stair-headings]): keep each
   title's own alignment — don't centre a left-aligned heading. Dots sit at the start edge;
   explicitly-centred headings (.pb-center) keep their centred dots. */
[data-stair-headings] .pb-h2.stair, [data-stair-headings] .pb-h3.stair { align-items: flex-start; }
[data-stair-headings] .pb-h2.stair .stair-dots, [data-stair-headings] .pb-h3.stair .stair-dots { margin-left: 0; margin-right: auto; }
[data-stair-headings] .pb-center.stair { align-items: center; }
[data-stair-headings] .pb-center.stair .stair-dots { margin-left: auto; margin-right: auto; }
/* two-line section heading WITHOUT the stair — just stack the lines */
.h-section.h-2l .l1, .h-section.h-2l .l2 { display: block; }

/* ===== MOBILE "UNITY" PATTERN for stair headings =====
   On phones the per-pattern left/right line offsets (`left: calc(±N * --stair-step)`) pushed the
   .l1/.l2/.l3 lines past the screen edge and widened the viewport (the reported bug). Below the
   phone breakpoint EVERY stair heading collapses to one consistent, centred, NON-shifted stack
   that fits the viewport — no left/right jog. Width hierarchy by level (of the mobile viewport):
   H1 = full width, H2 = 85%, H3 and smaller = 60%. Desktop keeps the full staircase design. */
@media (max-width: 640px) {
  .stair {
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    text-align: center !important;
    max-width: 100%;
    margin-left: auto; margin-right: auto;
  }
  .stair .l1, .stair .l2, .stair .l3 {
    left: 0 !important;                 /* neutralise every per-pattern horizontal shift */
    max-width: 100%;
    text-align: center;
    overflow-wrap: break-word;
  }
  .stair .stair-dots { margin-left: auto !important; margin-right: auto !important; }
  .stair-pos--frontstep .l2, .stair-pos--frontstep .l3 { justify-content: center !important; }
  [data-stair-headings] .pb-h2.stair, [data-stair-headings] .pb-h3.stair { align-items: center !important; }
  [data-stair-headings] .pb-h2.stair .stair-dots, [data-stair-headings] .pb-h3.stair .stair-dots { margin-left: auto; margin-right: auto; }

  /* width hierarchy — the heading occupies at most this share of the viewport, centred */
  h1.stair, .headline.stair                          { width: 100%; max-width: 100vw; }
  h2.stair, .stair.pb-h2                             { max-width: 85vw; }
  h3.stair, h4.stair, h5.stair, h6.stair, .stair.pb-h3 { max-width: 60vw; }

  /* contact + FAQ titles are left-aligned by design on desktop — unify them to centred on mobile too */
  .contact__title.stair, .site-faq__title.stair {
    align-items: center !important; text-align: center !important;
    margin-left: auto !important; margin-right: auto !important;
  }
  .contact__title.stair .stair-dots, .site-faq__title.stair .stair-dots { margin-left: auto !important; margin-right: auto !important; }

  /* scroll-reveal: slide in VERTICALLY (not sideways) on phones, so an x-slide can't jog a heading
     or its column off the screen edge mid-animation. Resolves to transform:none via [data-fade].in. */
  [data-fade="x-"], [data-fade="x+"] { transform: translateY(28px); }
}
@media (prefers-reduced-motion: no-preference) {
  .headline.blast.lit .l2 { animation: accentPan 9s linear infinite, l2In 0.72s cubic-bezier(.2,.85,.25,1) 0.5s both; }
}
@keyframes l2In { from { opacity: 0; transform: translateY(34px) scale(0.96); } to { opacity: 1; transform: none; } }
@keyframes shimmer { from { background-position: 0% 50%; } to { background-position: 100% 50%; } }
/* display headings: a touch of top room so tall Agency caps aren't clipped by background-clip */
.h-section { padding-top: 0.12em; line-height: 0.98; }
.subtitle { max-width: 33rem; margin: 26px auto 0; font-size: calc(18px * var(--fs-body)); line-height: 1.7; color: hsl(var(--hero-sub)); opacity: 0.9; }
/* empty placeholder that holds the original subtitle slot so the CTA buttons don't shift up (subtitle now sits BELOW the buttons) */
.subtitle-spacer { margin: 26px auto 0; height: calc(18px * var(--fs-body) * 1.7 * 2); max-width: 33rem; }
.hero .cta-row { margin-top: 32px; display: flex; gap: 14px; flex-wrap: wrap; align-items: center; justify-content: center; }
/* Hero logo marquee removed (it was laggy). Brand logos live in the static
   ".clients-band" logo wall lower on the page instead. */

/* ===========================================================
   GIF MARQUEE (scroll-driven)
   =========================================================== */
/* ===========================================================
   BLOB GALLERY — CMS carousel photos as a BRICK/honeycomb of morphing clip-path
   tiles. Full rows fill the width; offset rows shift half a tile to nest into the
   gaps; rows overlap vertically (negative margin) so the blobs interlock with NO
   gap. site.js sizes the tile to the viewport (fills the width, ~10px gutters on
   every device) and injects the blob clip-path scaled to that size, so the smooth
   hover morph (blob → rounded square) is preserved. Hover pops in colour + dims
   the rest. --gx-s = tile size, --gx-ov = row overlap (both set by site.js).
   =========================================================== */
.gx-sec { background: hsl(var(--background)); padding: clamp(48px, 7vh, 100px) 10px; }
.gx-gallery { width: 100%; max-width: 1280px; margin: 0 auto; }
.gx-row { display: flex; flex-wrap: nowrap; gap: var(--gx-gap, 0); }   /* horizontal gap between tiles */
.gx-row + .gx-row { margin-top: var(--gx-ov, -14px); }       /* −50% + gap/2 → woven, equal H/V gaps */
.gx-row--off { padding-left: var(--gx-off, calc(var(--gx-s, 140px) / 2)); } /* brick: offset ½ a column pitch */
.gx-row--center { justify-content: center; }                 /* a partial last row stays centred */
.gx-item {
  flex: none;
  width: var(--gx-s, 140px);
  aspect-ratio: 1;
  background: #161616 center / cover no-repeat;
  text-decoration: none;
  transition: filter 450ms ease, clip-path 450ms ease;       /* clip-path injected by site.js */
}
.gx-item.is-link { cursor: pointer; }
.gx-gallery:hover .gx-item { filter: brightness(0.5) saturate(0.5); }                      /* dim the rest… */
.gx-gallery .gx-item:hover { filter: brightness(1) saturate(1.5); position: relative; z-index: 5; }  /* …pop the hovered */

/* legacy marquee styles (unused — kept harmless) */
.mq { background: hsl(var(--background)); padding: clamp(72px, 11vh, 150px) 0 36px; overflow: hidden; }
.mq-row { display: flex; gap: 12px; width: max-content; }
.mq-row + .mq-row { margin-top: 12px; }
.mq-tile { width: clamp(240px, 30vw, 420px); height: clamp(155px, 19.3vw, 270px); border-radius: 18px; flex: none; background-size: cover, cover; background-position: center; background-repeat: no-repeat; box-shadow: 0 10px 30px rgba(0,0,0,0.4); }

/* ===========================================================
   shared section headings
   =========================================================== */
.h-section { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; line-height: 0.98; letter-spacing: -0.02em; font-size: calc(clamp(3rem, 12vw, 160px) * var(--fs-display) * var(--hl-scale, 1) * var(--h-mobile, 1)); text-align: center; padding-top: 0.1em; }
/* heading-LEVEL visual scale: a Homepage section heading set to H3–H6 (CMS → Hero & Page
   Structure → Homepage section headings) now shrinks its VISUAL size to match the level too,
   not just the semantic <hN> tag. H2 = unchanged (your existing sizes are untouched). */
h2.h-section, h2.contact__title, h2.site-faq__title { --hl-scale: 1; }
h3.h-section, h3.contact__title, h3.site-faq__title { --hl-scale: .78; }
h4.h-section, h4.contact__title, h4.site-faq__title { --hl-scale: .62; }
h5.h-section, h5.contact__title, h5.site-faq__title { --hl-scale: .5; }
h6.h-section, h6.contact__title, h6.site-faq__title { --hl-scale: .4; }
.h-silver { color: hsl(var(--foreground)); -webkit-text-fill-color: hsl(var(--foreground)); background: none; animation: none; }
/* headings are plain WHITE (the animated red stroke that used to travel around each
   heading's box has been removed). Keep the centred fit-content box for layout. */
.h-section { position: relative; width: -moz-fit-content; width: fit-content; max-width: 100%; margin-left: auto; margin-right: auto; isolation: isolate; }

/* Liquid metallic flow reveal (assets/heading-flow.js): a transparent WebGL
   canvas paints the red+silver flow inside the letter shapes. When active the
   real glyphs go transparent (kept in the DOM for SEO/a11y) and the traveling
   stroke is hidden — the flow IS the heading now. */
.hf-canvas { position: absolute; pointer-events: none; z-index: 1; }
.hf-flow { position: relative; }                                 /* positioning context only — no layout/format change */
/* When the flow is active, fully blank the real glyphs (incl. any background-clip
   gradient like the contact line's red) so only the metallic flow shows. */
.hf-on { color: transparent !important; -webkit-text-fill-color: transparent !important;
         background-image: none !important; -webkit-background-clip: border-box !important; background-clip: border-box !important; }
.hf-on::before { display: none !important; }                     /* hides the .h-section traveling stroke when active */
/* Pre-paint guard (set by the inline head script only when WebGL is present):
   blank the un-flowed heading look so the old white text / red stroke / red
   gradient never flash before the metallic flow paints. */
.hf-arm .h-section, .hf-arm .hf-flow { color: transparent !important; -webkit-text-fill-color: transparent !important;
         background-image: none !important; -webkit-background-clip: border-box !important; background-clip: border-box !important; }
.hf-arm .h-section::before { display: none !important; }

/* ===========================================================
   ABOUT
   =========================================================== */
.about { position: relative; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: clamp(40px, 6vh, 64px); padding: calc(clamp(96px, 12vh, 140px) * var(--sec-space, 1)) 20px; text-align: center; overflow: hidden; }
.about .deco { position: absolute; z-index: 0; aspect-ratio: 1; filter: drop-shadow(0 24px 40px rgba(0,0,0,0.55)); }
.about .deco.m3d-int { background: transparent; cursor: grab; pointer-events: auto; }
.about .deco.m3d-int:active { cursor: grabbing; }
.deco.tl { width: clamp(120px, 17vw, 210px); top: 3%; left: clamp(0px, 4vw, 64px); }
.deco.tr { width: clamp(120px, 17vw, 210px); top: 3%; right: clamp(0px, 4vw, 64px); }
.deco.bl { width: clamp(100px, 14vw, 180px); bottom: 6%; left: clamp(8px, 8vw, 120px); }
.deco.br { width: clamp(130px, 17vw, 220px); bottom: 6%; right: clamp(8px, 8vw, 120px); }
.about-inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: clamp(40px, 6vh, 64px); }
.about-text { color: hsl(var(--foreground)); font-weight: 500; text-align: center; line-height: 1.7; max-width: 620px; font-size: calc(clamp(1.05rem, 2vw, 1.45rem) * var(--fs-body)); }
.about-text .ch { transition: opacity 0.1s linear; }
@media (max-width: 600px) { .deco.bl, .deco.br { opacity: 0.55; } }

/* ===========================================================
   SERVICES (light)
   =========================================================== */
/* softer than pure white so it isn't glaring (dark card text stays readable) */
.services { position: relative; z-index: 2; background: hsl(var(--background)); color: hsl(var(--foreground)); border-top: 1px solid hsl(var(--foreground)/0.08); border-radius: clamp(40px, 5vw, 60px) clamp(40px, 5vw, 60px) 0 0; padding: calc(clamp(80px, 10vh, 128px) * var(--sec-space, 1)) clamp(20px, 4vw, 40px) calc(clamp(96px, 12vh, 160px) * var(--sec-space, 1)); }
.services .h-section { color: hsl(var(--foreground)); margin-bottom: clamp(48px, 7vh, 100px);
  /* on the white services panel, the arcade extrude uses ACCENT colours (not dark)
     so a black header still gets a visible, on-brand 3D pop instead of black-on-black */
  filter:
    drop-shadow(calc(0.022em * var(--arcade-depth)) calc(0.022em * var(--arcade-depth)) 0 var(--accent-2))
    drop-shadow(calc(0.044em * var(--arcade-depth)) calc(0.044em * var(--arcade-depth)) 0 var(--accent-3)); }
.svc-list { max-width: 64rem; margin: 0 auto; }
.svc-item { display: flex; align-items: flex-start; gap: clamp(18px, 4vw, 56px); padding: clamp(26px, 4vh, 48px) 4px; border-top: 1px solid rgba(12,12,12,0.15); }
.svc-item:last-child { border-bottom: 1px solid rgba(12,12,12,0.15); }
.svc-num { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.6rem, 9vw, 130px); line-height: 0.82; color: #0C0C0C; flex: none; }
.svc-body { padding-top: clamp(6px, 1vw, 14px); }
.svc-name { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: -0.01em; font-size: clamp(1.15rem, 2.4vw, 2.1rem); color: #0C0C0C; }
.svc-desc { margin-top: 10px; font-weight: 400; line-height: 1.6; max-width: 40rem; font-size: clamp(0.9rem, 1.5vw, 1.2rem); color: hsl(var(--foreground)/0.65); }

/* ===========================================================
   PROJECTS (sticky stacking)
   =========================================================== */
.projects { position: relative; z-index: 10; background: hsl(var(--background)); border-radius: clamp(40px, 5vw, 60px) clamp(40px, 5vw, 60px) 0 0; margin-top: clamp(-56px, -6vw, -40px); padding: calc(clamp(56px, 8vh, 104px) * var(--sec-space, 1)) clamp(20px, 4vw, 36px) calc(clamp(84px, 11vh, 140px) * var(--sec-space, 1)); }
.projects .browse-row { position: relative; z-index: 20; }
.projects .h-section { margin-bottom: clamp(40px, 6vh, 80px); }
#projWrap { position: relative; display: flex; flex-direction: column; align-items: center; }
.proj-card {
  position: sticky; top: calc(94px + var(--i, 0) * 16px); z-index: calc(var(--i, 0) + 1);
  width: min(1180px, 100%); margin-bottom: clamp(90px, 16vh, 200px);
  border-radius: clamp(34px, 4.4vw, 56px); border: 2px solid hsl(var(--ice)/0.85);
  background: hsl(var(--background)); padding: clamp(16px, 2.2vw, 32px);
  box-shadow: 0 -10px 60px rgba(0,0,0,0.5); overflow: hidden;
  /* will-change:transform restored: these few bounded-width sticky cards are driven
     by a scroll transform (stackUpdate); without a composited layer the stack
     re-rasterised late ("slow render after stacked"). Unlike the carousel's
     max-content rows, these are ~1 viewport wide and few in number, so promoting
     them is cheap and keeps the stacking smooth. */
  transform-origin: top center; will-change: transform;
}
.proj-card:last-child { margin-bottom: clamp(40px, 7vh, 90px); }
/* release room so the final card fully covers the previous one before the next section arrives */
/* --tail is shrunk by site.js as the deck packs (falls back to the static clamp) */
#projWrap::after, #voicesWrap::after { content: ""; display: block; height: var(--tail, clamp(180px, 34vh, 420px)); width: 100%; transition: height 0.15s linear; }
.proj-top { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 6px clamp(8px, 1.5vw, 20px) clamp(14px, 2vw, 22px); }
.proj-top-l { display: flex; align-items: center; gap: clamp(14px, 2.4vw, 30px); }
.proj-num { font-family: var(--font-display); font-weight: 700; font-size: clamp(2.4rem, 7vw, 110px); line-height: 0.8; color: hsl(var(--cream)); }
.proj-meta .cat { font-size: 11px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--gold-400)); }
.proj-meta .pname { font-family: var(--font-display); font-weight: 600; text-transform: uppercase; font-size: clamp(1.2rem, 3vw, 2.4rem); color: hsl(var(--foreground)); line-height: 1.05; margin-top: 4px; }
.proj-grid { display: grid; grid-template-columns: 40% 60%; gap: clamp(10px, 1.4vw, 18px); }
.proj-col1 { display: flex; flex-direction: column; gap: clamp(10px, 1.4vw, 18px); }
.proj-col1 .pi { width: 100%; background-size: cover; background-position: center; border-radius: clamp(26px, 3.4vw, 44px); }
.proj-col1 .pi.a { height: clamp(120px, 15vw, 220px); }
.proj-col1 .pi.b { height: clamp(150px, 21vw, 320px); }
.proj-col2 .pi { width: 100%; height: 100%; min-height: clamp(290px, 37vw, 558px); background-size: cover; background-position: center; border-radius: clamp(26px, 3.4vw, 44px); }
@media (max-width: 720px) {
  .proj-grid { grid-template-columns: 1fr; }
  .proj-col2 .pi { min-height: clamp(220px, 60vw, 360px); }
  .proj-top { padding-bottom: 14px; }
}

/* ===========================================================
   CONTACT (cloned from 79artstudio.com/contact-us)
   =========================================================== */
.contact { position: relative; z-index: 12; overflow-x: clip; background: hsl(var(--background)); border-radius: clamp(40px, 5vw, 60px) clamp(40px, 5vw, 60px) 0 0; margin-top: clamp(-56px, -6vw, -40px); padding: calc(clamp(64px, 9vh, 120px) * var(--sec-space, 1)) clamp(20px, 5vw, 72px) calc(clamp(44px, 6vh, 70px) * var(--sec-space, 1)); }
.contact__grid { max-width: 76rem; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr); gap: clamp(28px, 4vw, 84px) clamp(32px, 5vw, 84px); align-items: start;
  grid-template-areas: "intro form" "details form"; }
.contact__grid > * { min-width: 0; }
.contact__intro { grid-area: intro; }
.contact__details { grid-area: details; }
.contact__form { grid-area: form; align-self: stretch; }
/* phone: searchable country picker + number */
.phone-row { display: flex; gap: 8px; position: relative; }
.phone-cc { flex: none; display: inline-flex; align-items: center; gap: 6px; cursor: pointer; white-space: nowrap; box-sizing: border-box; border: 1px solid hsl(var(--line)/0.18); background: hsl(var(--surface)); color: hsl(var(--foreground)); border-radius: 10px; padding: 0 12px; font-size: 16px; font-family: var(--font-body); }
.phone-cc, .phone-opt, .phone-opt__d { font-family: var(--font-body); -webkit-font-smoothing: antialiased; }
.phone-cc:hover { border-color: hsl(var(--line)/0.4); }
.phone-num { flex: 1; min-width: 0; }
.phone-pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; width: min(340px, 86vw); background: hsl(var(--surface)); border: 1px solid hsl(var(--line)/0.2); border-radius: 12px; box-shadow: 0 24px 60px rgba(0,0,0,0.5); overflow: hidden; }
.phone-pop[hidden] { display: none; }
.phone-search { width: 100%; border: none; border-bottom: 1px solid hsl(var(--line)/0.16); background: none; color: hsl(var(--foreground)); padding: 12px 14px; font-size: 14px; font-family: inherit; }
.phone-search:focus { outline: none; }
.phone-list { max-height: 300px; overflow-y: auto; }
.phone-grouphd { font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0.08em; text-transform: uppercase; color: hsl(var(--foreground)/0.5); padding: 12px 14px 6px; }
.phone-opt { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 14px; background: none; border: none; color: hsl(var(--foreground)/0.9); font-size: 14px; cursor: pointer; }
.phone-opt:hover { background: hsl(var(--foreground)/0.06); }
.phone-opt__n { flex: 1; }
.phone-opt__d { color: hsl(var(--foreground)/0.5); font-size: 13px; }
.phone-empty { padding: 16px; color: hsl(var(--foreground)/0.5); font-size: 13px; }
/* desktop/tablet: form fills the height of the intro+details column, button anchors bottom */
@media (min-width: 901px) {
  .contact__form { display: flex; flex-direction: column; }
  .contact__form > .btn-jewel { margin-top: auto; }
}
/* Mobile: intro → FORM → details, so the form sits between the Thai note and the email block */
@media (max-width: 900px) { .contact__grid { grid-template-columns: 1fr; gap: 32px; max-width: 600px;
    grid-template-areas: "intro" "form" "details"; } }

.contact__eyebrow { font-size: 12px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: hsl(var(--gold-400)); }
.contact__title { font-family: var(--font-display); font-weight: 700; line-height: 1.02; letter-spacing: -0.02em; font-size: calc(clamp(2.4rem, 5.6vw, 4.4rem) * var(--fs-display) * var(--hl-scale, 1) * var(--h-mobile, 1)); color: hsl(var(--cream)); margin: 18px 0 0; }
.contact__title .l1, .contact__title .l2, .contact__title .l3 { display: block; }   /* stack the 3 lines even before the stair JS runs (no-JS fallback) */
/* the contact intro column is left-aligned, so keep the stair LEFT-anchored (the centered
   front/above positions would otherwise centre just this heading, out of line with the
   eyebrow + notes around it). The staircase still steps right from the left edge. */
.contact__title.stair { align-items: flex-start; text-align: left; }
.contact__title.stair .stair-dots { margin-left: 0; margin-right: auto; }
.contact__title em { font-style: normal; background-image: var(--accent-grad); background-size: 280% 100%; -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent; animation: accentPan 9s linear infinite; }
.contact__note { font-size: clamp(0.95rem, 1.5vw, 1.05rem); line-height: 1.65; color: hsl(var(--foreground)/0.72); margin: 22px 0 0; max-width: 30rem; }
.contact__note--th { color: hsl(var(--foreground)/0.5); font-size: clamp(0.82rem, 1.3vw, 0.92rem); margin-top: 8px; }
.contact__meta { list-style: none; margin: clamp(26px, 4vh, 40px) 0 0; padding: 0; display: flex; flex-direction: column; gap: 15px; max-width: 32rem; }
.contact__meta li { display: flex; flex-direction: column; gap: 4px; border-bottom: 1px solid hsl(var(--gold-500)/0.16); padding-bottom: 14px; }
.contact__meta .k { font-size: 10px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--foreground)/0.45); }
.contact__meta .v { font-size: clamp(0.9rem, 1.5vw, 1rem); color: hsl(var(--cream)); }
.contact__meta a.v { transition: color 0.18s ease; }
.contact__meta a.v:hover { color: hsl(var(--gold-400)); }
.contact__social { display: flex; flex-wrap: wrap; gap: 20px; margin-top: 24px; }
.contact__social a { font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: hsl(var(--foreground)/0.6); transition: color 0.18s ease; }
.contact__social a:hover { color: hsl(var(--gold-400)); }
.contact__tax { margin-top: 20px; font-size: 11px; letter-spacing: 0.06em; color: hsl(var(--foreground)/0.4); }

.contact__form { border-radius: clamp(22px, 2.6vw, 32px); padding: clamp(22px, 3vw, 38px); display: flex; flex-direction: column; gap: 16px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 480px) { .field-row { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 8px; }
.field > span { font-size: 10px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: hsl(var(--foreground)/0.66); }
.field > span i { color: hsl(var(--gold-500)); font-style: normal; }
.field input, .field select, .field textarea {
  width: 100%; box-sizing: border-box; font-family: var(--font-body); font-size: 15px; color: hsl(var(--cream));
  background: hsl(0 0% 8%); border: 1px solid hsl(var(--gold-500)/0.2); border-radius: 12px;
  padding: 13px 14px; outline: none; transition: border-color 0.18s ease, box-shadow 0.18s ease; resize: none;
}
.field textarea { resize: vertical; min-height: 84px; }
.field select { appearance: none; -webkit-appearance: none; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, hsl(var(--gold-500)) 50%), linear-gradient(135deg, hsl(var(--gold-500)) 50%, transparent 50%);
  background-position: calc(100% - 19px) center, calc(100% - 14px) center; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 38px; }
.field input::placeholder, .field textarea::placeholder { color: hsl(var(--foreground)/0.32); }
.field input:focus, .field select:focus, .field textarea:focus { border-color: hsl(var(--gold-500)); box-shadow: 0 0 0 3px hsl(var(--gold-500)/0.16); }
.contact__form .btn-jewel { width: 100%; margin-top: 6px; }
.contact__success { font-size: 13px; line-height: 1.5; color: hsl(var(--gold-300)); margin: 4px 0 0; text-align: center; }
.contact__form.sent .field, .contact__form.sent .field-row, .contact__form.sent > .btn-jewel { display: none; }

/* ---- AJAX states: waiting spinner + in-place result message (slow email send) ---- */
.contact__form { position: relative; }
/* fade EVERYTHING in the form out while busy or showing a result — fields, the
   submit button AND the PDPA consent note (so nothing lingers on top). */
.contact__form > :not(.contact__loading):not(.contact__msg) { transition: opacity 0.4s ease; }
.contact__form.is-busy > :not(.contact__loading):not(.contact__msg),
.contact__form.is-result > :not(.contact__loading):not(.contact__msg) { opacity: 0; pointer-events: none; }
/* spinner + message both sit centred over the now-faded form */
.contact__loading, .contact__msg {
  position: absolute; inset: clamp(22px, 3vw, 38px); display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; gap: 16px;
  opacity: 0; pointer-events: none; transition: opacity 0.4s ease;
}
.contact__msg[hidden] { display: none; }
.contact__form.is-busy .contact__loading { opacity: 1; }
.contact__form.is-result .contact__msg { opacity: 1; }
.cload-ring { width: 48px; height: 48px; border-radius: 50%; flex: none;
  border: 3px solid hsl(var(--gold-500)/0.18); border-top-color: hsl(var(--gold-500));
  animation: cloadSpin 0.8s linear infinite; }
.cload-txt { font-size: 13px; letter-spacing: 0.04em; color: hsl(var(--foreground)/0.7); }
@keyframes cloadSpin { to { transform: rotate(360deg); } }
.contact__msg { font-size: 14.5px; line-height: 1.55; padding: 0 16px; }
/* centred circular status icon, then the message below it */
.cmsg-icon { width: 58px; height: 58px; flex: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; }
.cmsg-icon svg { width: 30px; height: 30px; }
.contact__msg.is-ok .cmsg-icon { background: #28c878; box-shadow: 0 6px 20px rgba(40,200,120,0.35); animation: cmsgPop 0.4s cubic-bezier(.2,1.3,.5,1) both; }
.contact__msg.is-err .cmsg-icon { background: var(--accent-1, #ff5a5a); box-shadow: 0 6px 20px rgba(255,90,90,0.32); animation: cmsgPop 0.4s cubic-bezier(.2,1.3,.5,1) both; }
.cmsg-text { display: block; }
.contact__msg.is-ok { color: hsl(var(--gold-300)); }
.contact__msg.is-err { color: var(--accent-1, #ff6b6b); }
@keyframes cmsgPop { from { transform: scale(0); opacity: 0; } to { transform: scale(1); opacity: 1; } }
/* error fallback: email-us-directly line + clickable address */
.cmsg-alt { display: block; font-size: 13.5px; color: hsl(var(--foreground)/0.72); margin-top: -4px; }
.cmsg-alt a { color: hsl(var(--gold-300)); font-weight: 600; text-decoration: underline; word-break: break-all; }
/* close button, top-right of the message box */
.cmsg-close { position: absolute; top: 0; right: 0; width: 34px; height: 34px; display: flex; align-items: center;
  justify-content: center; background: hsl(0 0% 100% / 0.06); border: 1px solid hsl(var(--gold-500)/0.18);
  border-radius: 50%; color: hsl(var(--foreground)/0.8); cursor: pointer; padding: 0; transition: background 0.15s, color 0.15s; }
.cmsg-close:hover { background: hsl(0 0% 100% / 0.12); color: hsl(var(--cream)); }
.cmsg-close svg { width: 16px; height: 16px; }
@media (prefers-reduced-motion: reduce) { .cload-ring { animation-duration: 1.8s; } }

.contact__foot { max-width: 76rem; margin: clamp(48px, 7vh, 90px) auto 0; padding-top: 26px; border-top: 1px solid hsl(var(--gold-500)/0.14); display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.contact__foot img { height: 26px; width: auto; }
.contact__foot span { font-size: 11px; color: hsl(var(--foreground)/0.4); letter-spacing: 0.04em; }
.contact__ver { font-family: var(--font-pixel, monospace); font-size: 9px !important; color: hsl(var(--foreground)/0.32) !important; }

/* ---------- voices / testimonials (card stacking) ---------- */
.voices { position: relative; z-index: 11; background: hsl(var(--background)); border-radius: clamp(40px, 5vw, 60px) clamp(40px, 5vw, 60px) 0 0; margin-top: clamp(-56px, -6vw, -40px); padding: calc(clamp(56px, 8vh, 104px) * var(--sec-space, 1)) clamp(20px, 4vw, 36px) calc(clamp(44px, 6vh, 80px) * var(--sec-space, 1)); }
.voices .h-section { margin-bottom: clamp(40px, 6vh, 80px); }
#voicesWrap { position: relative; display: flex; flex-direction: column; align-items: center; }
.qcard {
  position: sticky; top: calc(102px + var(--i, 0) * 16px); z-index: calc(var(--i, 0) + 1);
  width: min(980px, 100%); margin-bottom: clamp(210px, 34vh, 420px);
  border-radius: clamp(34px, 4.4vw, 56px); border: 2px solid hsl(var(--gold-500)/0.6);
  background: linear-gradient(150deg, hsl(0 0% 9%), hsl(0 0% 5%));
  padding: clamp(30px, 5vw, 72px) clamp(26px, 5vw, 80px); overflow: hidden;
  box-shadow: 0 -10px 60px rgba(0,0,0,0.55), inset 0 1px 1px rgba(255,255,255,0.06);
  transform-origin: top center; will-change: transform; /* restored for smooth sticky-stack */
  display: flex; flex-direction: column; gap: clamp(22px, 3.4vw, 44px);
}
.qcard .qmark { font-family: var(--font-display); font-weight: 700; font-size: clamp(70px, 10vw, 130px); line-height: 0.5; color: hsl(var(--gold-500)); height: clamp(32px, 5vw, 60px); }
.qcard .qtext { font-family: var(--font-body); font-weight: 600; font-size: clamp(1.5rem, 3.4vw, 3rem); line-height: 1.16; letter-spacing: -0.01em; color: hsl(var(--cream)); }
.qcard .qtext em { font-style: normal; color: hsl(var(--gold-400)); }
/* attribution block: short dash divider, then NAME / Position / Company stacked.
   margin-top:auto pushes the dash + name/role to the BOTTOM of the card while the
   quote text stays at the top (cards stretch to equal height in .voice-grid). */
.qcard .qdash { width: 34px; height: 2px; background: hsl(var(--gold-500)); margin: auto 0 clamp(12px, 1.6vw, 18px); }
.qcard .qby { display: flex; align-items: center; gap: 14px; }
.qcard .qby .qav { width: clamp(104px, 14vw, 140px); height: clamp(74px, 9.5vw, 100px); object-fit: cover; object-position: center; flex: none; border: 0; margin: 0 0 0 calc(-1 * var(--qpad, 32px)); border-radius: 0 999px 999px 0; background: hsl(0 0% 16%); }
.qcard .qwho { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.qcard .qwho .nm { font-weight: 700; font-size: clamp(0.95rem, 1.5vw, 1.1rem); color: hsl(var(--gold-400)); letter-spacing: 0.01em; }
.qcard .qwho .pos { font-size: clamp(0.8rem, 1.3vw, 0.95rem); color: hsl(var(--foreground)/0.78); }
.qcard .qwho .co { font-size: clamp(0.78rem, 1.2vw, 0.9rem); color: hsl(var(--foreground)/0.52); }

/* ===========================================================
   HORIZONTAL PORTRAIT CAROUSEL — Showcases + Testimonials
   Replaces the old sticky-stacking decks. A row of portrait cards that drifts
   left/right with page scroll (and drag-and-throw), infinite loop, centre card
   scaled up. site.js (.hrow engine) drives the transforms. Title sits OUTSIDE the
   card on a transparent background.
   =========================================================== */
.hrow-sec { padding-bottom: clamp(56px, 8vh, 96px) !important; }   /* no stacking tail needed */
.hrow-sec .h-section { margin-bottom: clamp(26px, 4vh, 52px); }
.hrow {
  position: relative; width: 100%; overflow: hidden;
  cursor: grab; touch-action: pan-y;                 /* horizontal handled by JS drag; vertical scroll stays native */
  user-select: none; -webkit-user-select: none;
}
.hrow.is-drag { cursor: grabbing; }
/* soft edge fades so cards melt into the dark bg on each side */
.hrow::before, .hrow::after { content: ""; position: absolute; top: 0; bottom: 0; width: clamp(36px, 7vw, 120px); z-index: 4; pointer-events: none; }
.hrow::before { left: 0;  background: linear-gradient(90deg,  hsl(var(--background)), transparent); }
.hrow::after  { right: 0; background: linear-gradient(270deg, hsl(var(--background)), transparent); }
.hrow-track { display: flex; gap: clamp(16px, 2.2vw, 36px); width: max-content; padding: clamp(34px, 5vw, 60px) 0; align-items: center; }

.hcard { flex: none; width: clamp(208px, 21vw, 288px); display: flex; flex-direction: column; gap: 12px; text-decoration: none; transform-origin: center center; }
.hcard-title { text-align: center; font-family: var(--font-pixel); font-size: clamp(8px, 1vw, 11px); letter-spacing: 0.05em; line-height: 1.55; text-transform: uppercase; color: hsl(var(--cream)); min-height: 2.1em; padding: 0 6px; }
.hcard-title .hcard-role { color: hsl(var(--foreground)/0.5); }
.hcard-box {
  position: relative; aspect-ratio: 3 / 4; border-radius: 14px; overflow: hidden;
  background-color: #1a1a1a;                          /* dark grey */
  background-size: cover; background-position: center;
  border: 1px solid hsl(var(--line)/0.16);
  box-shadow: 0 16px 44px rgba(0,0,0,0.5);
}
/* showcase variant — project image + category chip + open icon */
.hcard-box--show .hcard-cat { position: absolute; left: 12px; bottom: 12px; font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; color: #fff; background: rgba(8,8,8,0.6); padding: 5px 8px; border-radius: 6px; }
.hcard-box--show .hcard-open { position: absolute; right: 10px; top: 10px; width: 30px; height: 30px; display: grid; place-items: center; border-radius: 8px; background: rgba(8,8,8,0.55); color: #fff; opacity: 0; transition: opacity 0.2s ease; }
.hcard:hover .hcard-open { opacity: 1; }
/* testimonial variant — quote inside */
.hcard--voice { cursor: pointer; }
.hcard-box--voice { display: flex; flex-direction: column; justify-content: center; gap: 14px; padding: clamp(18px, 1.8vw, 28px); background: linear-gradient(160deg, hsl(0 0% 11%), hsl(0 0% 6%)); }
.hcard-box--voice .qmark { font-family: var(--font-display); font-weight: 700; font-size: clamp(44px, 5vw, 72px); line-height: 0.42; color: hsl(var(--gold-500)); }
.hcard-box--voice .qtext { font-family: var(--font-body); font-weight: 600; font-size: clamp(1rem, 1.5vw, 1.5rem); line-height: 1.24; letter-spacing: -0.01em; color: hsl(var(--cream)); }
.hcard-box--voice .qtext em { font-style: normal; color: hsl(var(--gold-400)); }
.hcard-box--voice .qav { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; aspect-ratio: 1; flex: none; border: 1px solid hsl(var(--line)/0.2); }
@media (prefers-reduced-motion: reduce) { .hrow { cursor: default; } }

/* ===========================================================
   STATIC CARD SECTIONS — original rich Showcase + Testimonial cards, laid out as a
   still grid (NOT the sticky stack, NOT a carousel). Overrides the proj-card/qcard
   sticky positioning so they render in normal flow.
   =========================================================== */
.cards-sec { padding-bottom: clamp(56px, 9vh, 110px) !important; }
.cards-sec .h-section { margin-bottom: clamp(36px, 6vh, 72px); }

/* Showcases — portfolio-style cards, two per row (1 per row on phones).
   Card look (.pitem*) is identical to portfolio.php's grid so the home Showcases
   match the Portfolio page exactly. */
/* Homepage Showcase grid: 4 cols (desktop) · 4 (tablet) · 2×2 (mobile). */
/* Smart, layout-aware columns: VERTICAL (narrow) cards pack more per row than
   HORIZONTAL (photo-left, wider) cards. Double-class selectors keep the
   horizontal counts winning over the vertical media-query steps at every width. */
.show-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(13px, 1.5vw, 22px); max-width: 1320px; margin: 0 auto clamp(36px, 6vh, 64px); }
@media (max-width: 1080px) { .show-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 760px)  { .show-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px)  { .show-grid { grid-template-columns: 1fr; gap: 12px; } }
.show-grid.show-grid--horizontal { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 760px) { .show-grid.show-grid--horizontal { grid-template-columns: 1fr; } }
/* GPU-only hover: the card is promoted to its own compositor layer (translateZ) so
   the lift just composites a transform — no per-frame repaint of the filtered image —
   and the border glow fades via a pseudo-element's opacity, not a border-color repaint. */
.pitem { position: relative; border: 1px solid hsl(var(--line)/0.16); border-radius: 10px; overflow: hidden; background: hsl(var(--surface)); cursor: pointer; text-align: left; width: 100%; display: block; backface-visibility: hidden; transition: var(--lift-down); }
/* promote to a GPU layer only while interacting — a permanent will-change kept EVERY card on its
   own compositor layer (N cards × full-card texture), a major idle-memory cost. The hover lift
   stays smooth because the layer is created on hover-start. */
.pitem:hover, .pitem:focus-within { will-change: transform; }
.pitem::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 1px solid hsl(var(--line)/0.45); opacity: 0; transition: opacity .2s ease; pointer-events: none; }
.pitem:hover { transform: translate3d(0,-8px,0); transition: var(--lift-up); }
.pitem:hover::after { opacity: 1; }
.pitem__img { position: relative; aspect-ratio: 16/10; background-size: cover; background-position: center; filter: saturate(1.06) contrast(1.04); }
.pitem__b { padding: clamp(18px, 2.2vw, 26px); min-width: 0; }
.pitem__cat { font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.04em; text-transform: uppercase; color: hsl(var(--foreground)/0.6); }
/* colourful tag chips over the title — WRAP within the card (never overflow); extras collapse into a +N chip */
.pitem__tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 9px; min-width: 0; }
.pitem__tag { white-space: nowrap; font-size: calc(10px * var(--fs-tag, 1)); font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; padding: 3px 9px; border-radius: 999px; background: hsl(var(--h,0) 70% 52% / 0.16); border: 1px solid hsl(var(--h,0) 72% 58% / 0.45); color: hsl(var(--h,0) 85% 82%); max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* registry-coloured chip (tinted by its own --cc) */
.pitem__tag--col { background: color-mix(in srgb, var(--cc) 16%, transparent); border-color: color-mix(in srgb, var(--cc) 55%, transparent); color: color-mix(in srgb, var(--cc) 72%, white); }
/* service chip — pulled to the front, solid brand red, always first */
.pitem__tag--svc { background: var(--cc, #E11D2A); border-color: var(--cc, #E11D2A); color: #fff; font-size: calc(10px * var(--fs-chip, 1)); }
.pitem__tag--more { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.2); color: hsl(var(--foreground)/0.7); }
.pitem__n { font-family: var(--font-card, var(--font-display)); font-weight: 600; text-transform: uppercase; font-size: clamp(1.05rem, 1.5vw, 1.4rem); line-height: 1.06; }
/* date as a black & white chip under the title — hugs the date text only (not full width) */
.pitem__date { display: inline-block; align-self: flex-start; width: fit-content; margin-top: 9px; font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.05em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.16); color: hsl(var(--foreground)/0.82); }
.pitem__d { margin-top: 8px; font-size: .84rem; line-height: 1.5; color: hsl(var(--foreground)/0.62); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* ===== MASTERPIECE DRAWER =====
   A square-cell grid "drawer": the cover image fills a random 2×3/3×2 block (sliced
   across cells, cover-fit so it never stretches), ~35% of the rest show other gallery
   photos, the remainder are black. A diagonal run of white-bordered transparent cells
   is the "stair" (animates itself in). The whole drawer is clickable → the project.
   Square pagination dots below (active spins + 20% bigger); title + 2-line description
   below that. Switching flips the cells in a diagonal cascade (bottom-left → top-right).
   Layout + motion live in assets/masterpiece.js. */
.mpx { max-width: 1440px; margin: 0 auto clamp(22px,3.5vh,42px); }
.mpx-frame { position: relative; width: 100%; }
/* the grid + frame don't clip, so the one spinning stair cell's corners can poke out
   past the top/bottom edge without being cut off. */
.mpx-grid { display: grid; gap: 4px; background: transparent; perspective: 1600px; overflow: visible; }
.mpx-frame { overflow: visible; }
/* empty cells are near-black (subtle grid on the page) */
.mpx-cell { position: relative; background: rgb(4,4,4) center/cover no-repeat; transform: scale(1); transform-origin: center; transition: transform .3s cubic-bezier(.22,.61,.36,1), opacity .3s ease, filter .5s ease; will-change: transform; overflow: hidden; }   /* .3s MUST match FLIP in masterpiece.js */
.mpx-cell.is-flip { opacity: 0; transform: scale(.82); }   /* soft scale+fade dissolve — replaced the 3D rotateY flip (its edge-on "blink" read as choppy) */
/* gallery photos start greyscale and colourise one-by-one (revealColors in masterpiece.js); the MAIN cover stays colour. */
.mpx-cell--img.mpx-desat { filter: grayscale(1); }
/* (The Masterpiece cursor-follow "see more" hover overlay was removed.) */
/* STAIR — a diagonal run of white-bordered, fully TRANSPARENT cells (one per row): a full-grid
   staircase the falling 79 numerals bounce down. Animates itself in, then a faint highlight
   travels down the steps (staggered by --si). */
.mpx-cell--stair { background: transparent; box-shadow: inset 0 0 0 2px rgba(255,255,255,.5); animation: mpxStairShim 3.2s ease-in-out infinite; animation-delay: calc(var(--si,0) * .22s); }
.mpx-cell--stair.mpx-stair-in { animation: mpxStairIn .5s cubic-bezier(.2,.7,.3,1) backwards, mpxStairShim 3.2s ease-in-out infinite; animation-delay: calc(var(--si,0) * .06s), calc(.5s + var(--si,0) * .22s); }
@keyframes mpxStairIn { from { opacity: 0; transform: scale(.3); } to { opacity: 1; transform: none; } }
@keyframes mpxStairShim { 0%,72%,100% { box-shadow: inset 0 0 0 2px rgba(255,255,255,.5); } 12% { box-shadow: inset 0 0 0 2px rgba(255,255,255,.95), 0 0 9px rgba(255,255,255,.28); } }
/* NUMBERS — the 79 CI numerals fall through the drawer and settle; drawn above the cells */
.mpx-nums { position: absolute; left: 0; top: 0; width: 100%; z-index: 6; pointer-events: none; overflow: visible; }
.mpx-num { position: absolute; left: 0; top: 0; opacity: 0; transform-origin: center center; will-change: transform, opacity; filter: drop-shadow(0 3px 12px rgba(0,0,0,.5)); }
.mpx-num svg { display: block; width: 100%; height: 100%; }
/* square white pagination dots, below the drawer; the active one spins + is 20% bigger.
   A generous top gap keeps the dots + title clear of the drawer (and the numerals that fall
   out its bottom edge). */
.mpx-dots { display: flex; gap: 12px; justify-content: center; margin: clamp(46px,6vh,78px) 0 0; min-height: 10px; }
.mpx-dot { width: 10px; height: 10px; border: 0; padding: 0; background: rgba(255,255,255,.4); cursor: pointer; transition: background .2s, transform .2s; }
.mpx-dot:hover { background: rgba(255,255,255,.7); }
.mpx-dot.is-on { background: #fff; transform: scale(1.2); animation: mpxSpin 2.4s linear infinite; }
@keyframes mpxSpin { to { transform: scale(1.2) rotate(360deg); } }
/* title + 2-line description, below the dots, outside the drawer (extra breathing room). */
.mpx-meta { text-align: center; margin: clamp(16px,2vh,26px) auto 0; max-width: 760px; padding: 0 16px; }
/* no hover underline — the title lifts up on hover and bounces back to rest on mouse-out. */
.mpx-title { display: inline-block; font-family: var(--font-card, var(--font-display)); font-weight: 700; text-transform: uppercase; font-size: clamp(1.15rem,2.2vw,1.9rem); line-height: 1.05; color: #fff; text-decoration: none; transition: transform .45s cubic-bezier(.34,1.56,.64,1); will-change: transform; }
.mpx-title:hover { transform: translateY(-4px); }
.mpx-desc { margin: 9px 0 0; color: rgba(255,255,255,.66); font-size: clamp(.86rem,1.1vw,1rem); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mpx-seo { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@media (prefers-reduced-motion: reduce) {
  .mpx-cell { transition: none; }
  .mpx-cell--stair, .mpx-cell--stair.mpx-stair-in, .mpx-dot.is-on { animation: none; }
  .mpx-num { opacity: 1; }
  .mpx-title, .hover-lift { transition: none; }
  .mpx-title:hover, .hover-lift:hover { transform: none; }
}

/* ===== Smart link hover (site convention) — NEVER hover:underline. A link-like element lifts
   up on hover and, on mouse-out, bounces back past its origin and settles (the back-easing curve
   overshoots in both directions). Add .hover-lift to any standalone link/title. ===== */
.hover-lift { display: inline-block; transition: transform .45s cubic-bezier(.34,1.56,.64,1); will-change: transform; }
.hover-lift:hover { transform: translateY(-4px); }
/* ========================================================================
   GLOBAL: any light text that sits ON TOP of an image gets a readability
   shadow so it stays legible on any photo. Add `txt-on-img` to custom overlay text.
   ======================================================================== */
.txt-on-img,
.lb-caption, .ss-caption, .ss-meta, .gx-item__label {
  text-shadow: 0 1px 3px rgba(0,0,0,0.66), 0 4px 18px rgba(0,0,0,0.5);
}
/* Horizontal card style: photo left, text right (reverts to vertical on phones for legibility). */
.show-grid--horizontal .pitem { display: grid; grid-template-columns: 42% 1fr; align-items: stretch; }
.show-grid--horizontal .pitem__img { aspect-ratio: auto; height: 100%; min-height: 148px; }
.show-grid--horizontal .pitem__b { padding: clamp(12px,1.4vw,18px); display: flex; flex-direction: column; justify-content: center; }
@media (max-width: 680px) { .show-grid--horizontal .pitem { display: block; } .show-grid--horizontal .pitem__img { aspect-ratio: 16/10; height: auto; min-height: 0; } }

/* Testimonials — a tidy responsive grid of quote cards */
.voice-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: clamp(14px, 1.8vw, 24px); max-width: 1180px; margin: 0 auto; align-items: stretch; }
/* compact testimonial cards — recalculated smaller across the board */
.voice-grid .qcard { position: static; top: auto; z-index: auto; will-change: auto; transform: none !important; margin: 0; width: 100%; --qpad: clamp(18px, 2vw, 28px); padding: var(--qpad); gap: clamp(12px, 1.4vw, 18px); }
.voice-grid .qcard .qtext { font-size: clamp(0.95rem, 1.2vw, 1.1rem); line-height: 1.5; font-weight: 500; }
.voice-grid .qcard .qmark { font-size: clamp(34px, 4vw, 52px); height: clamp(16px, 2.4vw, 26px); }
.voice-grid .qcard .qdash { margin: auto 0 clamp(10px, 1.2vw, 14px); }
.voice-grid .qcard .qby { gap: 11px; }
.voice-grid .qcard .qav { width: clamp(64px, 8vw, 82px); height: clamp(48px, 5.6vw, 60px); }
.voice-grid .qcard .qwho .nm { font-size: clamp(0.85rem, 1.05vw, 0.98rem); }
.voice-grid .qcard .qwho .pos { font-size: clamp(0.74rem, 0.95vw, 0.84rem); }
.voice-grid .qcard .qwho .co { font-size: clamp(0.7rem, 0.9vw, 0.79rem); }
@media (max-width: 720px) { .voice-grid { grid-template-columns: 1fr; } }

/* ---------- fade-in on view ---------- */
[data-fade] { opacity: 0; transform: translateY(30px); transition: opacity 0.45s cubic-bezier(.25,.1,.25,1), transform 0.45s cubic-bezier(.25,.1,.25,1); }
[data-fade].in { opacity: 1; transform: none; }
[data-fade="x-"] { transform: translateX(-80px); }
[data-fade="x+"] { transform: translateX(80px); }

@media (prefers-reduced-motion: reduce) {
  .headline .l2, .hm-track, .badge .dot, .hero-seal__ring, .hero-scroll span { animation: none !important; }
  [data-fade] { opacity: 1 !important; transform: none !important; }
  html { scroll-behavior: auto; }
}

/* ===========================================================
   8-BIT MONOCHROME OVERRIDES + CINEMATIC INTRO (loaded last)
   =========================================================== */
/* --- intro overlay --- */
.intro { position: fixed; inset: 0; z-index: 1000; background: hsl(var(--background)); display: grid; place-items: center; transition: opacity 0.55s ease; }
.intro.done { opacity: 0; pointer-events: none; }
.intro.hidden { display: none; }
#introCanvas { width: min(78vw, 680px); height: auto; image-rendering: pixelated; image-rendering: crisp-edges; }
/* On RETINA displays (esp. phones at DPR 3), the 640px logo bitmap is scaled by a fractional factor
   (~1.57x on an iPhone 16 Pro Max). With crisp-edges that fraction drops the 1px gaps in the 3-dot
   frame, so the top/bottom border collapses to 2 dots. Smooth scaling blends instead of dropping, so
   the uniform 3-dot frame is preserved — imperceptible softening on a high-contrast upscale. DPR-1
   screens (near-integer scale) keep the razor-crisp look. The locked intro.js + logo are untouched. */
@media (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) { #introCanvas { image-rendering: auto; } }
.intro__cap { position: absolute; left: 0; right: 0; bottom: 17%; text-align: center; font-family: var(--font-pixel); font-size: clamp(8px, 1.4vw, 11px); letter-spacing: 0.32em; color: hsl(var(--foreground)/0.66); min-height: 1.2em; }
.intro__skip { position: absolute; right: clamp(16px, 4vw, 34px); bottom: clamp(16px, 4vw, 30px); font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.08em; color: hsl(var(--foreground)/0.5); padding: 9px 13px; border: 1px solid hsl(var(--line)/0.2); border-radius: 3px; transition: color .15s, border-color .15s; }
.intro__skip:hover { color: hsl(var(--foreground)); border-color: hsl(var(--foreground)/0.55); }

/* --- pixel labels (unify the 8-bit voice) --- */
.nav-center a { font-family: var(--nav-font, var(--font-pixel)); letter-spacing: 0.02em; }
.nav-center a:hover { color: #0a0a0a; background: transparent; } /* black text on the white travelling chip */
/* A picked nav font must override the Labels/pixel role CSS (which lists .nav-center a !important).
   #nav.nav--font raises specificity above the role's single-class selector; the custom font is a
   real typeface, so re-enable smoothing (the pixel-crisp `none` above would jag it). */
#nav.nav--font .nav-center a { font-family: var(--nav-font) !important; -webkit-font-smoothing: antialiased; }
.contact__eyebrow { font-family: var(--font-pixel); font-size: 10px; letter-spacing: 0.1em; color: hsl(var(--foreground)/0.8); }
.proj-meta .cat { font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.04em; color: hsl(var(--foreground)/0.75); }
.field > span { font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.03em; color: hsl(var(--foreground)/0.7); }
.contact__meta .k { font-family: var(--font-pixel); font-size: 12px; letter-spacing: 0.06em; }   /* +50% — was 8px, too small */
.contact__social a, .contact__tax { font-family: var(--font-pixel); font-size: 12px; letter-spacing: 0.04em; }   /* +50% — was 8px */
.hero-seal__ring text { font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0; fill: hsl(var(--foreground)/0.55); }

/* --- crisp pixel corners --- */
.projects, .voices, .services, .contact { border-radius: 12px 12px 0 0; }
.proj-card, .qcard { border-radius: 8px; }
.mq-tile { border-radius: 6px; }
.contact__form { border-radius: 8px; background: hsl(var(--surface)); border: 1px solid hsl(var(--line)/0.12); }
.field input, .field select, .field textarea { border-radius: 4px; background: hsl(0 0% 9%); border-color: hsl(var(--line)/0.18); color: hsl(var(--cream)); }
.field input:focus, .field select:focus, .field textarea:focus { border-color: hsl(var(--foreground)); box-shadow: 0 0 0 2px hsl(var(--foreground)/0.22); }
/* Make the dropdowns (Service / Attendees / Budget) clearly stand out instead of
   blending into the dark form — lighter surface + a bold chevron on the right. */
.field select {
  background-color: hsl(0 0% 16%);
  border-color: hsl(var(--gold-500)/0.45);
  font-weight: 600;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23d8b878' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: calc(100% - 14px) center;
  background-size: 14px 14px;
  padding-right: 40px;
}
.field select:hover { background-color: hsl(0 0% 20%); border-color: hsl(var(--gold-500)/0.7); }
.field select option { background: hsl(0 0% 12%); color: hsl(var(--cream)); }

/* --- monochrome surfaces / accents --- */
.qcard { background: linear-gradient(160deg, hsl(0 0% 11%), hsl(0 0% 6%)); border-color: hsl(var(--line)/0.18); }
.proj-card { border-color: hsl(var(--line)/0.2); }
.nav.nav--bar.past-hero { border-bottom-color: hsl(var(--line)/0.14); } /* divider only once the top bar reveals past the hero */
.cl .ic { background: hsl(var(--foreground)/0.1); border-radius: 3px; }

/* --- imagery → keep colourful (full colour, slight contrast lift) --- */
.mq-tile { filter: saturate(1.05) contrast(1.03); }
.proj-col1 .pi, .proj-col2 .pi { filter: saturate(1.05) contrast(1.04); transition: filter 0.3s; }
.proj-card:hover .proj-col2 .pi { filter: saturate(1.12) contrast(1.05) brightness(1.04); }

/* --- headline: keep sleek, add a faint pixel echo --- */
.headline .l1 { text-shadow: 3px 3px 0 hsl(var(--foreground)/0.08); }

/* --- Hero text: SOLID WHITE + a strong shadow so the title, eyebrow and lead stay
   crisply readable over busy photo / pixel-art hero backgrounds. Scoped to .hero-wrap
   so section headings on solid panels keep their sleek faint echo (2026-07-26). --- */
.hero-wrap .headline .l1,
.hero-wrap .headline .l2,
.hero-wrap .headline .l3 {
  color: #fff;
  text-shadow:
    0 1px 2px  rgba(0,0,0,.09),
    0 3px 12px rgba(0,0,0,.09),
    0 8px 30px rgba(0,0,0,.07);
}
.hero-wrap .hero .subtitle {
  color: #fff;
  opacity: 1;   /* base .subtitle dims to .9 — keep hero copy full-strength white for contrast */
  /* Strong legibility shadow so the intro text stays readable over ANY hero (bright photo or
     busy pixel-art), not just dark scenes. Tight dark edge + soft halo. */
  text-shadow: 0 1px 3px rgba(0,0,0,.62), 0 2px 14px rgba(0,0,0,.5);
}
.hero-wrap .hero .badge { text-shadow: 0 1px 2px rgba(0,0,0,.08); }

/* ===========================================================
   SERVICES — grid of clickable cards (79artstudio nav set)
   =========================================================== */
.svc-grid { max-width: 72rem; margin: 0 auto; display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(12px, 1.6vw, 18px); }
@media (max-width: 860px) { .svc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px) { .svc-grid { grid-template-columns: 1fr; } }
.svc-card {
  display: block; width: 100%;
  text-align: left; background: hsl(var(--foreground)/0.04); border: 1px solid hsl(var(--foreground)/0.12); border-radius: 8px;
  padding: clamp(20px, 2.4vw, 30px); cursor: pointer; position: relative; overflow: hidden;
  transition: var(--lift-down), box-shadow 0.18s ease, border-color 0.18s ease;
}
.svc-card::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: var(--accent-grad); background-size: 280% 100%; transform: scaleX(0); transform-origin: left; transition: transform 0.28s ease; animation: accentPan 9s linear infinite; z-index: 3; }
/* hover: smooth lift straight up (no scale/diagonal); release drops + bounces once */
.svc-card:hover { transform: translateY(-8px); box-shadow: 0 14px 30px -12px var(--accent-2); border-color: hsl(var(--foreground)); transition: var(--lift-up), box-shadow 0.18s ease, border-color 0.18s ease; }
.svc-card:hover::after { transform: scaleX(1); z-index: 4; }
.svc-card .svc-photo { display: none; }   /* no background swap (it lagged) */
/* content stays put; title turns to the accent gradient on hover */
.svc-card .svc-no, .svc-card .svc-tag, .svc-card .svc-more { position: relative; z-index: 2; }
.svc-card .svc-no { font-family: var(--font-pixel); font-size: 10px; letter-spacing: 0.06em; color: hsl(var(--foreground)/0.5); }
.svc-card .svc-ttl { position: relative; z-index: 2; font-family: var(--font-display); font-weight: 600; text-transform: uppercase; letter-spacing: -0.01em; font-size: clamp(1.1rem, 1.7vw, 1.5rem); color: hsl(var(--foreground)); margin-top: 12px; line-height: 1.05; transition: color 0.35s ease; }
.svc-card:hover .svc-ttl { color: transparent; background: var(--accent-grad); background-size: 280% 100%; -webkit-background-clip: text; background-clip: text; }
.svc-card:hover .svc-more { color: var(--accent-2); }
.svc-card .svc-tag { margin-top: 9px; font-size: 0.85rem; line-height: 1.5; color: hsl(var(--foreground)/0.65); }
.svc-card .svc-more { margin-top: 16px; display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0.06em; text-transform: uppercase; color: hsl(var(--foreground)/0.85); }
.svc-card .svc-more svg { width: 13px; height: 13px; }

/* ===========================================================
   BROWSE-ALL row (projects / voices → full page)
   =========================================================== */
.browse-row { display: flex; justify-content: center; margin-top: clamp(28px, 5vh, 60px); }

/* clickable cards affordance */
.proj-card, .qcard { cursor: pointer; }
.proj-card:hover { border-color: hsl(0 0% 78% / 0.4); box-shadow: 0 -10px 60px rgba(0,0,0,0.5), 0 0 0 1px hsl(0 0% 80% / 0.25); }
.qcard:hover { border-color: hsl(var(--line)/0.4); }
.proj-open, .q-open { position: absolute; top: clamp(14px,2vw,22px); right: clamp(14px,2vw,22px); width: 38px; height: 38px; display: grid; place-items: center; border-radius: 999px; border: 1px solid hsl(var(--line)/0.25); color: hsl(var(--foreground)/0.8); background: hsl(var(--foreground)/0.05); pointer-events: none; transition: background 0.2s, color 0.2s; }
.qcard:hover .q-open { background: hsl(var(--foreground)); color: hsl(var(--background)); }
/* anchor variant: stretch over the whole card so it's clickable to the detail page */
a.proj-open { position: absolute; inset: 0; width: auto; height: auto; display: block; border: none; background: none; border-radius: 8px; pointer-events: auto; z-index: 4; }
a.proj-open svg { position: absolute; top: clamp(14px,2vw,22px); right: clamp(14px,2vw,22px); width: 38px; height: 38px; padding: 9px; box-sizing: border-box; border-radius: 999px; border: 1px solid hsl(var(--line)/0.25); color: hsl(var(--foreground)/0.8); background: hsl(var(--background)/0.6); transition: background 0.2s, color 0.2s; }
.proj-card:hover a.proj-open svg { background: hsl(var(--foreground)); color: hsl(var(--background)); }

/* ===========================================================
   MODAL — AJAX liquid-glass popup
   =========================================================== */
/* closed = visibility:hidden + NO backdrop-filters, so the scrim's 8px and the panel's 20px blur
   backings aren't held while the modal is invisible (they were allocated on every page). */
.modal { position: fixed; inset: 0; z-index: 500; display: grid; place-items: center; padding: clamp(16px, 4vw, 48px); opacity: 0; visibility: hidden; pointer-events: none; transition: opacity 0.3s ease, visibility 0s linear 0.3s; }
.modal.open { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.3s ease, visibility 0s; }
.modal__scrim { position: absolute; inset: 0; background: hsl(0 0% 2% / 0.72); }
.modal.open .modal__scrim { backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.modal__panel {
  position: relative; width: min(720px, 100%); max-height: 88vh; overflow: auto;
  border-radius: 14px; padding: clamp(26px, 3.4vw, 44px);
  background: linear-gradient(160deg, hsl(0 0% 12% / 0.86), hsl(0 0% 7% / 0.92));
  border: 1px solid hsl(var(--line)/0.18); box-shadow: 0 30px 90px rgba(0,0,0,0.6), inset 0 1px 1px rgba(255,255,255,0.08);
  transform: translateY(24px) scale(0.98); transition: transform 0.34s cubic-bezier(.2,.9,.25,1);
}
.modal.open .modal__panel { transform: none; backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2); }
.modal__close { position: absolute; top: 16px; right: 16px; width: 40px; height: 40px; display: grid; place-items: center; border-radius: 999px; border: 1px solid hsl(var(--line)/0.2); color: hsl(var(--foreground)/0.8); background: hsl(var(--foreground)/0.05); transition: background 0.2s, color 0.2s, transform 0.2s; z-index: 2; }
.modal__close:hover { background: hsl(var(--foreground)); color: hsl(var(--background)); transform: rotate(90deg); }
.modal__eyebrow { font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: transparent; background-image: var(--accent-grad); background-size: 280% 100%; -webkit-background-clip: text; background-clip: text; animation: accentPan 9s linear infinite; }
.modal__title { font-family: var(--font-display); font-weight: 700; text-transform: uppercase; letter-spacing: -0.01em; font-size: clamp(1.7rem, 4vw, 2.8rem); line-height: 1.02; color: hsl(var(--cream)); margin: 14px 0 0; padding-top: 0.08em; }
.modal__media { margin: 20px 0 0; border-radius: 10px; overflow: hidden; aspect-ratio: 16/9; background: hsl(0 0% 12%); background-size: cover; background-position: center; }
.modal__body { margin: 20px 0 0; font-size: 1rem; line-height: 1.7; color: hsl(var(--foreground)/0.82); }
.modal__body p + p { margin-top: 12px; }
.modal__list { margin: 18px 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 8px; }
.modal__list li { font-family: var(--font-pixel); font-size: 8px; letter-spacing: 0.04em; text-transform: uppercase; color: hsl(var(--foreground)/0.7); padding: 8px 11px; border: 1px solid hsl(var(--line)/0.18); border-radius: 999px; }
.modal__actions { margin: 26px 0 0; display: flex; gap: 12px; flex-wrap: wrap; }
.modal__loading { display: flex; align-items: center; gap: 12px; padding: 30px 0; color: hsl(var(--foreground)/0.6); font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.08em; }
.modal__spin { width: 16px; height: 16px; border: 2px solid hsl(var(--foreground)/0.25); border-top-color: hsl(var(--foreground)); border-radius: 50%; animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .modal__panel { transition: none; } .modal__spin { animation-duration: 1.4s; } }

/* ===== Pre-footer client logo band (8-bit tiles) ===== */
.clients-band { padding: 56px 24px; }
.clients-band__inner { max-width: 72rem; margin: 0 auto; }
.clients-band__kicker { text-align: center; font-size: 13px; letter-spacing: 0.18em; text-transform: uppercase; color: hsl(var(--foreground)/0.55); margin: 0 0 28px; font-weight: 600; }
/* Centered logo wall — flex-wrap so the last row stays centered (a grid would
   left-align it), with every logo at a consistent half-size box. */
.clients-band__grid { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 18px 26px; }
.clients-band__tile { background: none; border: none; display: grid; place-items: center; padding: 0; transition: transform 0.18s ease; width: var(--clogo, 66px); aspect-ratio: 1 / 1; flex: none; }
.clients-band__tile:hover { transform: translateY(-4px); }
/* logo keeps its ORIGINAL aspect ratio via object-fit:contain, at 0.5× the old size */
.clients-band__tile img { width: 100%; height: 100%; object-fit: contain; display: block; }
@media (max-width: 560px) { .clients-band { padding: 40px 16px; } .clients-band__grid { gap: 14px 18px; } .clients-band__tile { width: calc(var(--clogo, 66px) * 0.7); } }

/* ===== Static hero background (hero-scenes.js) ===== */
/* when a hero image is set, lift the headline a touch for balance */
.hero-wrap.has-scene .hero,
.hero-wrap.subhero .hero { justify-content: flex-start; padding-top: clamp(104px, 16vh, 200px); }   /* subpage heroes top-align even with no scene (Event School) */
.hero-wrap.has-scene .hero .subtitle { text-shadow: 0 2px 14px rgba(12,12,12,0.75); }
@media (max-width: 680px) { .hero-wrap.has-scene .hero, .hero-wrap.subhero .hero { padding-top: clamp(88px, 13vh, 150px); } }

/* ===== Subpage hero scene band (full-bleed, hosts an isometric scene) ===== */
/* Subpage hero matches the home hero: full height, full-bleed, badge + 2-line headline.
   The inner .hero handles centring + the has-scene lift (shared with the homepage). */
.subhero { position: relative; width: 100vw; margin-left: calc(50% - 50vw); min-height: 100vh; min-height: 100svh; display: flex; flex-direction: column; overflow: hidden; }

/* ===== GEO FAQ accordion (before contact, every page) ===== */
/* sit above the contact section (which pulls up with a negative margin) so expanded answers are never clipped */
.site-faq { padding: 64px 24px clamp(72px, 9vw, 120px); position: relative; z-index: 13; }
.site-faq__inner { max-width: 50rem; margin: 0 auto; }
.site-faq__eyebrow { text-align: center; font-family: var(--font-pixel); font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: hsl(var(--foreground)/0.55); margin-bottom: 12px; }
.site-faq__title { text-align: center; font-family: var(--font-display); text-transform: uppercase; font-size: calc(clamp(1.8rem, 5vw, 3rem) * var(--hl-scale, 1)); line-height: 1; margin-bottom: 30px; }
.site-faq__item { border: 1px solid hsl(var(--foreground)/0.1); border-radius: 12px; margin-bottom: 10px; background: hsl(var(--foreground)/0.025); overflow: hidden; }
.site-faq__item summary { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 18px 20px; font-weight: 600; font-size: clamp(0.98rem, 1.6vw, 1.12rem); color: hsl(var(--foreground)/0.95); -webkit-tap-highlight-color: transparent; }
.site-faq__item summary::-webkit-details-marker { display: none; }
.site-faq__chev { width: 12px; height: 12px; border-right: 2px solid hsl(var(--foreground)/0.6); border-bottom: 2px solid hsl(var(--foreground)/0.6); transform: rotate(45deg); transition: transform 0.2s; flex: none; margin-top: -4px; }
.site-faq__item[open] .site-faq__chev,
.site-faq__item.is-open .site-faq__chev { transform: rotate(-135deg); margin-top: 2px; }   /* .is-open flips it immediately on close, before the slide finishes */
.site-faq__a { padding: 0 20px 20px; color: #6cc7e6; line-height: 1.65; font-size: clamp(0.92rem, 1.5vw, 1.02rem); overflow: hidden; will-change: height; transition: height 0.28s cubic-bezier(.4,0,.2,1), opacity 0.28s ease; } /* cyan = informative */
@media (prefers-reduced-motion: reduce) { .site-faq__a { transition: none; } }

/* ===== PERFORMANCE: static gradients =====
   The panning gradient animation (accentPan) drove continuous repaints — heavy on
   large desktop/tablet screens, especially the background-clip:text variants. The
   gradient COLOURS stay; only the movement is removed. (Marquee scroll is kept.) */
.headline .l2,
.h-silver,
.contact__title em,
.svc-card::after,
.modal__eyebrow,
.pager a.on { animation: none !important; }
.headline.blast.lit .l2 { animation: l2In 0.72s cubic-bezier(.2,.85,.25,1) 0.5s both !important; }

/* ===== Floating contact button (every page) ===== */
.contact-fab{position:fixed;right:20px;bottom:22px;z-index:90;display:flex;align-items:center}
.contact-fab__btn{position:relative;width:56px;height:56px;border-radius:50%;display:grid;place-items:center;color:#fff;--ring:30px;perspective:240px;
  background:transparent;border:0;
  transition:transform .2s ease,box-shadow .2s ease}
.contact-fab__btn>svg{position:relative;z-index:1;transition:transform .3s ease}
.contact-fab__btn:hover,.contact-fab.cf-burst .contact-fab__btn{transform:translateY(-3px);background:transparent;border-color:transparent;-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:0 15px 16px -7px rgba(0,0,0,.6)}
.contact-fab__btn:hover>svg,.contact-fab.cf-burst .contact-fab__btn>svg{animation:cffloat 1.8s linear infinite}
@keyframes cffloat{0%{transform:translateY(-7px) scale(1.5) rotateY(0)}100%{transform:translateY(-7px) scale(1.5) rotateY(360deg)}}
.contact-fab__btn:active{transform:scale(.97)}
/* white dotted ring with a "magnifier" scale-wave travelling around it */
.cf-ring{position:absolute;inset:0;pointer-events:none}
.cf-dot{position:absolute;left:50%;top:50%;width:5px;height:5px;margin:-2.5px;border-radius:50%;background:#fff;opacity:.55;
  transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(.45);
  animation:cfwave 2.8s ease-in-out infinite;animation-delay:var(--d)}
@keyframes cfwave{
  0%,16%,100%{transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(.45);opacity:.5}
  8%{transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(1.7);opacity:1}
}
/* hover / tap → every dot scales up then explodes outward and disappears */
.contact-fab__btn:hover .cf-dot,.contact-fab.cf-burst .cf-dot{animation:cfburst .6s cubic-bezier(.34,.1,.2,1) forwards}
@keyframes cfburst{
  0%{transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(.6);opacity:1}
  35%{transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(2);opacity:1}
  100%{transform:rotate(var(--a)) translateY(calc(-1*var(--ring) - 42px)) scale(.25);opacity:0}
}
@media (prefers-reduced-motion:reduce){.cf-dot{animation:none!important;transform:rotate(var(--a)) translateY(calc(-1*var(--ring))) scale(.6)}.contact-fab__btn:hover>svg{animation:none;transform:scale(1.4) translateY(-5px)}}
@media (max-width:560px){.contact-fab{right:14px;bottom:16px}.contact-fab__btn{width:50px;height:50px;--ring:27px}}

/* circular avatars + round buttons always 1:1 (never oval) — task 99 */
.qcard .qby .qav { flex: none; }
.nav-search-btn, .contact-fab__btn, .contact-fab__mail { aspect-ratio: 1 / 1; flex: none; }

/* ============================================================
   Unified page eyebrow — cloned from the home-hero badge.
   Every page's eyebrow is a subtle pill + blinking square dot +
   solid pixel text. NO animated/dynamic gradient (the !important
   rules override the per-page inline gradient definitions).
   ============================================================ */
.subpage__eyebrow, .pb-eyebrow, .contact__eyebrow, .site-faq__eyebrow,
.article__cat, .modal__eyebrow, .tagp__eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 16px; border-radius: 3px;
  border: 1px solid hsl(var(--line)/0.16);
  font-family: var(--font-pixel); font-weight: 400;
  letter-spacing: 0.04em; text-transform: uppercase;
  /* kill any gradient/clipped-text treatment → solid badge text */
  color: hsl(var(--foreground)/0.86) !important;
  background: none !important;
  -webkit-text-fill-color: currentColor !important;
  -webkit-background-clip: border-box !important; background-clip: border-box !important;
  animation: none !important;
}
.subpage__eyebrow::before, .pb-eyebrow::before, .contact__eyebrow::before,
.site-faq__eyebrow::before, .article__cat::before, .modal__eyebrow::before, .tagp__eyebrow::before {
  content: ""; width: 7px; height: 7px; border-radius: 1px; flex: none;
  background: hsl(var(--foreground)); animation: blink 1.1s steps(2) infinite;
}
.site-faq__eyebrow { justify-content: center; }
@media (prefers-reduced-motion: reduce) {
  .subpage__eyebrow::before, .pb-eyebrow::before, .contact__eyebrow::before,
  .site-faq__eyebrow::before, .article__cat::before, .modal__eyebrow::before, .tagp__eyebrow::before { animation: none; }
}

/* ============================================================
   Performance layer (lazy-load + paint deferral).
   ============================================================ */
/* smooth fade-in for lazily-loaded images (added by assets/lazyload.js) */
img.lz { opacity: 0; }
img.lz.lz-in { opacity: 1; transition: opacity 0.45s ease; }
@media (prefers-reduced-motion: reduce) { img.lz, img.lz.lz-in { opacity: 1; transition: none; } }

/* Skip rendering + painting of below-the-fold sections until they're scrolled
   near. This also defers their CSS background-images (project/service photos),
   giving a much faster first paint and smoother scrolling. contain-intrinsic-size
   keeps the scrollbar stable (the "auto" keyword remembers each section's real
   height after it first renders). */
/* content-visibility:auto was removed — it skips rendering off-screen sections, so
   on a fast scroll (especially scrolling back UP) the section/cards aren't painted
   yet and flash BLACK until they catch up (very visible on a weak/Intel GPU). Keeping
   every section always-rendered trades a little first-paint cost for no black flash. */

/* ---- Default AJAX loading scene: white 79 logo fading-blink on a dark alpha overlay ----
   Drawn over any container being fetched (window.Loader.show/hide in site.js). */
.fetch-overlay { position: absolute; inset: 0; z-index: 20; display: flex; align-items: center; justify-content: center;
  background: rgba(8,8,8,0.62); backdrop-filter: blur(1.5px); -webkit-backdrop-filter: blur(1.5px);
  animation: fetchFadeIn .18s ease both; }
.fetch-overlay__logo { width: 56px; height: auto; filter: drop-shadow(0 2px 12px rgba(0,0,0,0.45));
  animation: fetchBlink 1.15s ease-in-out infinite; }
@keyframes fetchBlink { 0%,100% { opacity: 0.16; transform: scale(0.95); } 50% { opacity: 0.95; transform: scale(1.02); } }
@keyframes fetchFadeIn { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .fetch-overlay__logo { animation: none; opacity: 0.85; } }


/* ===== CMS-customizable text sizes (Appearance → Font sizes): title bullets / tags /
   service chips. Placed last + !important so they win over the per-component font-size
   declarations earlier in this file (e.g. the full .badge rule, the eyebrow rules). ===== */
.contact__eyebrow, .subpage__eyebrow, .pb-eyebrow, .site-faq__eyebrow { font-size: calc(10px * var(--fs-pixel) * var(--fs-eyebrow, 1)) !important; }
.badge { font-size: calc(9px * var(--fs-pixel) * var(--fs-chip, 1)) !important; }
.pitem__tag { font-size: calc(10px * var(--fs-tag, 1)) !important; }
.pitem__tag--svc { font-size: calc(10px * var(--fs-chip, 1)) !important; }

/* ===== Solid black background (the 45° pinstripe texture was removed by request). ===== */
:root { --bg-stripes: none; }

/* ===== Liquid Glass (CMS → Appearance → Liquid Glass) =====
   Two layers, applied by assets/liquidglass-init.js:
   • .lg-glass — a real, always-works CSS frosted-glass (backdrop blur + saturate + brightness
     + edge highlight) driven by the chosen params. Applied immediately to each selected surface
     so toggling the setting ALWAYS visibly changes the surface, even where WebGL can't paint.
   • .lg-surface — used only if the heavier WebGL refraction (ybouane/liquidglass) successfully
     initialises: the surface goes fully transparent so the live refracted page shows through.
   A surface is upgraded from .lg-glass → .lg-surface on WebGL success, and stays on .lg-glass
   otherwise (WebGL unavailable / reduced-motion / snapshot can't rasterise). ===== */
.lg-glass {
  background: var(--lg-bg, rgba(13,16,22,.5)) !important;
  -webkit-backdrop-filter: blur(var(--lg-blur,11px)) saturate(var(--lg-sat,1.4)) brightness(var(--lg-bright,1)) !important;
  backdrop-filter: blur(var(--lg-blur,11px)) saturate(var(--lg-sat,1.4)) brightness(var(--lg-bright,1)) !important;
  border: 1px solid color-mix(in srgb, #ffffff calc(var(--lg-edge,12) * 1%), transparent) !important;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), inset 0 -1px 0 rgba(0,0,0,.22), 0 16px 44px -18px rgba(0,0,0,.7) !important;
}
.lg-glass.contact-fab__btn { border-radius: 50% !important; }

.lg-surface { background-color: transparent !important; -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
.lg-surface.pitem { border-color: transparent !important; }

/* ===== reCAPTCHA v3 is invisible/score-based — hide Google's floating badge sitewide so
   a key/domain error (e.g. "not in supported domains") never shows to visitors. Google's
   terms allow this as long as the attribution text is shown near the form (.rc-attribution
   in the contact form). Live reCAPTCHA status lives in CMS → Connection & Security. ===== */
.grecaptcha-badge { visibility: hidden !important; }
.rc-attribution { font-size: 11px; line-height: 1.5; color: hsl(var(--foreground)/0.42); margin-top: 4px; }
.rc-attribution a { color: hsl(var(--foreground)/0.6); text-decoration: underline; }

/* Inline sprite-sheet animation (Page Builder [sprite_id=…]) — sizing is set on the canvas by sprite.js
   (max 128×256 desktop/tablet, 0.7× mobile). Keep it inline with the text baseline. */
/* Float so the paragraph text wraps down the sprite's full height and overflows below it,
   rather than leaving one line beside it and a tall gap. pointer-events:none keeps the canvas
   from swallowing text selection / editor clicks. */
.sprite79{float:left;margin:.15em 16px .35em 0;line-height:0;pointer-events:none}
.sprite79 canvas{display:block;image-rendering:auto}
/* loading placeholder: reserves the box + a soft shimmer until the sheet is keyed/sliced */
.sprite79__ph{border-radius:8px;background:linear-gradient(100deg,rgba(255,255,255,.04) 30%,rgba(255,255,255,.12) 50%,rgba(255,255,255,.04) 70%);background-size:200% 100%;animation:sprite79sh 1.1s ease-in-out infinite}
@keyframes sprite79sh{from{background-position:200% 0}to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.sprite79__ph{animation:none}}
