/* ============================================================
   KIVO MARKETING — "SIGNAL // VOID" — Command Bay
   Pure black void · a single living lime signal (#D7FF37)
   Real glass materiality: two motivated recipes (visor / canopy),
   used only where a surface genuinely floats over content.
   ============================================================ */

/* ---------- Fonts, self-hosted ----------
   Host Grotesk and JetBrains Mono (SIL Open Font License 1.1), variable
   woff2 from Google Fonts, latin + latin-ext only (EN, ES, CA). One file
   covers every weight in the range. Served from our own server so no
   visitor data goes to Google. Regenerate: see README. */
@font-face { font-family: "Host Grotesk"; font-style: italic; font-weight: 400 600; font-display: swap; src: url("/assets/fonts/host-grotesk-italic-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Host Grotesk"; font-style: italic; font-weight: 400 600; font-display: swap; src: url("/assets/fonts/host-grotesk-italic-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "Host Grotesk"; font-style: normal; font-weight: 400 800; font-display: swap; src: url("/assets/fonts/host-grotesk-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "Host Grotesk"; font-style: normal; font-weight: 400 800; font-display: swap; src: url("/assets/fonts/host-grotesk-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
@font-face { font-family: "JetBrains Mono"; font-style: normal; font-weight: 400 600; font-display: swap; src: url("/assets/fonts/jetbrains-mono-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: "JetBrains Mono"; font-style: normal; font-weight: 400 600; font-display: swap; src: url("/assets/fonts/jetbrains-mono-latin-ext.woff2") format("woff2"); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }

/* ---------- Tokens ---------- */
:root {
  --bg-deep: #000000;
  --bg: #050505;
  --surface: #0a0a0a;
  --surface-2: #101013;
  --hairline: rgba(255, 255, 255, 0.09);
  --hairline-2: rgba(255, 255, 255, 0.16);
  --ink: #ffffff;
  --ink-soft: #eaeaea;
  --ink-dim: #a1a1a1;
  --ink-mute: #6b6b6b;
  --accent: #d7ff37;
  --accent-press: #c2e82f;
  --accent-glow: rgba(215, 255, 55, 0.35);
  --accent-soft: rgba(215, 255, 55, 0.12);
  --accent-seam: rgba(215, 255, 55, 0.18);
  --on-accent: #000000;

  --font-display: "Host Grotesk", "Host Grotesk Fallback", system-ui, -apple-system, sans-serif;
  --font-body: "Host Grotesk", "Host Grotesk Fallback", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, monospace;

  --maxw: 1320px;
  --pad: clamp(1.25rem, 4.5vw, 4.5rem);
  --ease: cubic-bezier(0.22, 0.61, 0.28, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ---- Radius system: angular structure vs. round action ----
     Content/panels get the card/panel scale below. Interactive
     controls (buttons, nav pills, language switch) stay full pill.
     One rule, applied everywhere — see Section 4.4 shape lock. ---- */
  --r-chip: 8px;
  --r-card: 18px;
  --r-panel: 28px;
  --r-pill: 100px;

  /* fixed nav height: anchor targets clear it via scroll-margin-top */
  --nav-h: 72px;

  /* ---- Motion / 3D tokens (SIGNAL DIVE) ---- */
  --persp: 1200px;
  --z-recede: -380px;
  --z-arrive: -640px;
  --bank: 16deg;

  --fs-hero: clamp(2rem, 2.6vw + 1rem, 3.6rem);
  --fs-h1: clamp(2.6rem, 6vw, 6.5rem);
  --fs-h2: clamp(2rem, 4.4vw, 3.8rem);
  --fs-h3: clamp(1.3rem, 2.1vw, 1.9rem);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
/* smooth only after load (set by main.js), never under reduced motion */
@media (prefers-reduced-motion: no-preference) { html.is-smooth { scroll-behavior: smooth; } }
main:focus { outline: none; }
/* decorative 3D entries (the product panel docking in, scaled stage objects)
   may poke past the viewport mid-animation. clip, not hidden: it creates no
   scroll container, so the sticky stage and the pinned wheel keep working. */
main { overflow-x: clip; }

body {
  font-family: var(--font-body);
  background: var(--bg-deep);
  color: var(--ink-soft);
  line-height: 1.65;
  font-size: clamp(1rem, 0.97rem + 0.2vw, 1.15rem);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, canvas { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
::selection { background: var(--accent); color: #000; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 700; line-height: 0.98; letter-spacing: -0.03em; }

.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad); }
.wrap--wide { max-width: 1560px; }
.nowrap { white-space: nowrap; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ---------- WebGL canvas ---------- */
#fx {
  position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: 0; pointer-events: none;
  background:
    radial-gradient(120% 80% at 50% 120%, var(--accent-soft) 0%, transparent 55%),
    var(--bg-deep);
}
main, .nav, .console, .spine, .hud { position: relative; z-index: 2; }

/* ---------- Grain + scanline overlay ---------- */
.grain {
  position: fixed; inset: 0; z-index: 3; pointer-events: none; opacity: 0.42;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
.grain::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(0deg, rgba(0,0,0,0.14) 0 1px, transparent 1px 3px);
  opacity: 0.3;
}

/* ---------- Glass system ----------
   .glass-light  — visor: nav, badges, tags, stat tiles. Marks a
     surface at the same depth as content, barely lifted.
   .glass-dense  — canopy: work cards, CRM panel, contact panel,
     feature modules. Marks a surface that floats above content,
     with a lit top seam like an illuminated cockpit edge.
   Both fall back to a solid fill under prefers-reduced-transparency. */
.glass-light {
  background: rgba(255, 255, 255, 0.065);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  backdrop-filter: blur(12px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, 0.17);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09);
}
.glass-dense {
  position: relative;
  background:
    radial-gradient(120% 140% at 12% -10%, rgba(215, 255, 55, 0.06), transparent 55%),
    linear-gradient(160deg, rgba(255, 255, 255, 0.085), rgba(255, 255, 255, 0.02)),
    rgba(17, 17, 19, 0.82);
  -webkit-backdrop-filter: blur(26px) saturate(170%);
  backdrop-filter: blur(26px) saturate(170%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.11),
    inset 0 -18px 40px rgba(0, 0, 0, 0.35),
    0 30px 70px -24px rgba(0, 0, 0, 0.8);
}
.glass-dense::before {
  content: "";
  position: absolute; left: 10%; right: 10%; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-seam) 40%, var(--accent-seam) 60%, transparent);
}
@media (prefers-reduced-transparency: reduce) {
  .glass-light { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .glass-dense { background: var(--surface); backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* ---------- Spine ---------- */
.spine { position: fixed; left: max(18px, 2.2vw); top: 0; bottom: 0; width: 1px; z-index: 4; background: var(--hairline); pointer-events: none; }
.spine__fill { position: absolute; top: 0; left: 0; width: 100%; height: 100%; transform-origin: top; transform: scaleY(0); background: linear-gradient(180deg, transparent, var(--accent) 12%, var(--accent) 88%, transparent); box-shadow: 0 0 12px var(--accent-glow); }
.spine__node { position: absolute; left: 50%; width: 7px; height: 7px; border-radius: 50%; background: var(--bg-deep); border: 1px solid var(--hairline-2); transform: translate(-50%, -50%); }
@media (max-width: 720px) { .spine { display: none; } }

/* ---------- Skip link ---------- */
.skip { position: absolute; left: -9999px; top: 0; z-index: 10000; background: var(--accent); color: #000; font-family: var(--font-mono); font-size: .8rem; padding: .6rem 1rem; }
.skip:focus { left: .5rem; top: .5rem; }

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 500;
  display: flex; align-items: center; gap: 1.5rem;
  padding: 1rem var(--pad);
  transition: background .4s var(--ease), padding .4s var(--ease), border-color .4s, backdrop-filter .4s;
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  background: rgba(4, 4, 4, 0.68);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
  border-bottom-color: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.05);
  padding-top: .7rem; padding-bottom: .7rem;
}

.brand { display: inline-flex; align-items: center; gap: .6rem; }
.brand__badge { width: 30px; height: 30px; object-fit: contain; display: block; }
.brand__word { font-family: var(--font-display); font-weight: 600; font-size: 1.5rem; letter-spacing: -0.03em; }

.nav__links { display: flex; gap: 1.7rem; margin-left: auto; }
.nav__links a { font-family: var(--font-mono); font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--ink-dim); position: relative; padding: .3rem 0; transition: color .25s; }
.nav__links a:hover, .nav__links a[aria-current="page"] { color: var(--ink); }
.nav__links a::after { content: ""; position: absolute; left: 0; bottom: -2px; height: 1px; width: 0; background: var(--accent); box-shadow: 0 0 8px var(--accent-glow); transition: width .3s var(--ease); }
.nav__links a:hover::after, .nav__links a[aria-current="page"]::after { width: 100%; }

.lang { display: inline-flex; align-items: center; gap: .1rem; font-family: var(--font-mono); font-size: .72rem; border: 1px solid var(--hairline); border-radius: var(--r-pill); padding: .18rem; }
.lang a { padding: .15rem .5rem; border-radius: var(--r-pill); color: var(--ink-mute); letter-spacing: .06em; transition: .25s; }
.lang a[aria-current="true"] { background: var(--accent); color: #000; }

.nav__burger { display: none; width: 40px; height: 40px; align-items: center; justify-content: center; margin-left: auto; }
.nav__burger span { display: block; width: 22px; height: 2px; background: var(--ink); position: relative; transition: .3s; }
.nav__burger span::before, .nav__burger span::after { content: ""; position: absolute; left: 0; width: 22px; height: 2px; background: var(--ink); transition: .3s; }
.nav__burger span::before { top: -7px; } .nav__burger span::after { top: 7px; }
.nav.is-open .nav__burger span { background: transparent; }
.nav.is-open .nav__burger span::before { top: 0; transform: rotate(45deg); background: var(--accent); }
.nav.is-open .nav__burger span::after { top: 0; transform: rotate(-45deg); background: var(--accent); }

/* ---------- Buttons ---------- */
.btn { display: inline-flex; align-items: center; gap: .6ch; font-family: var(--font-mono); font-size: .8rem; font-weight: 500; text-transform: uppercase; letter-spacing: .08em; padding: .95em 1.5em; border-radius: var(--r-pill); position: relative; transition: transform .25s var(--ease), background .25s, color .25s, box-shadow .3s, border-color .3s; will-change: transform; white-space: nowrap; }
.btn--primary { background: var(--accent); color: #000; box-shadow: 0 0 0 rgba(215,255,55,0); }
.btn--primary:hover { box-shadow: 0 0 34px var(--accent-glow); }
.btn--primary:active { transform: translateY(1px) scale(.98); }
.btn--ghost { border: 1px solid rgba(255,255,255,.16); color: var(--ink); background: rgba(255,255,255,.03); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 24px var(--accent-soft); }
.btn--ghost:active { transform: translateY(1px) scale(.98); }
.btn__arrow { transition: transform .3s var(--ease); }
.btn:hover .btn__arrow { transform: translateX(4px); }
.nav .btn { padding: .7em 1.2em; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* ---------- Mobile drawer ---------- */
/* closed = visibility:hidden, so its links are not five invisible Tab
   stops before the content; it hides only after the slide-out ends */
.drawer { position: fixed; inset: 0; z-index: 480; background: var(--bg-deep); display: flex; flex-direction: column; justify-content: center; gap: .2rem; padding: var(--pad); transform: translateY(-100%); visibility: hidden; transition: transform .5s var(--ease-out), visibility 0s linear .5s; }
.drawer.is-open { transform: translateY(0); visibility: visible; transition: transform .5s var(--ease-out); }
.drawer a { font-family: var(--font-display); font-size: clamp(2.2rem, 11vw, 4rem); font-weight: 600; text-transform: none; padding: .3rem 0; }
.drawer a:hover { color: var(--accent); }
.drawer__meta { margin-top: 2rem; font-family: var(--font-mono); font-size: .8rem; color: var(--ink-dim); display: flex; flex-direction: column; gap: .4rem; }

/* ============================================================
   SECTION SYSTEM
   ============================================================ */
.sec { padding-block: clamp(4.5rem, 12vh, 9rem); position: relative; }
.sec--tight { padding-block: clamp(3rem, 7vh, 5rem); }

.kicker { display: inline-flex; align-items: center; gap: .6rem; font-family: var(--font-mono); font-size: .74rem; text-transform: uppercase; letter-spacing: .16em; color: var(--ink-dim); margin-bottom: 1.6rem; }
.kicker::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent-glow); }
.kicker b { color: var(--accent); font-weight: 500; }

.h-sec { font-size: var(--fs-h2); color: var(--ink); max-width: 20ch; text-wrap: balance; }
.lead { font-size: clamp(1.15rem, 1.6vw, 1.45rem); color: var(--ink-dim); max-width: 56ch; line-height: 1.55; }
.eyebrow { font-family: var(--font-mono); font-size: .78rem; text-transform: uppercase; letter-spacing: .14em; color: var(--ink-dim); }
.accent { color: var(--accent); }
.mono { font-family: var(--font-mono); }

/* ---------- Reveal (IntersectionObserver + CSS transitions — reliable in any
   tab; fail-safe visible if JS absent) ---------- */
.anim .reveal { opacity: 0; transform: translateY(26px); }
.anim .reveal.is-in { opacity: 1; transform: none; transition: opacity .9s var(--ease-out), transform .9s var(--ease-out); }
.anim .reveal[data-delay="1"].is-in { transition-delay: .08s; }
.anim .reveal[data-delay="2"].is-in { transition-delay: .16s; }
.anim .reveal[data-delay="3"].is-in { transition-delay: .24s; }
.anim .reveal[data-delay="4"].is-in { transition-delay: .32s; }
.anim .reveal[data-delay="5"].is-in { transition-delay: .40s; }

/* ---------- Hero intro — pure CSS keyframes (reliable, smooth, runs even
   without JS/GSAP and in background tabs). ---------- */
@keyframes heroUp { from { transform: translateY(102%); } to { transform: translateY(0); } }
@keyframes heroFadeUp { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: translateY(0); } }
@keyframes heroLine { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes heroFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes heroStage { from { opacity: 0; transform: scale(1.05); } to { opacity: 1; transform: scale(1); } }
@keyframes sweepOnce { 0% { transform: translateX(-120%) skewX(-12deg); opacity: 0; } 12% { opacity: .8; } 34% { opacity: 0; } 100% { transform: translateX(220%) skewX(-12deg); opacity: 0; } }
.hero__eyebrow { animation: heroFadeUp .7s cubic-bezier(.33,1,.68,1) both .15s; }
.hero__title-inner { animation: heroUp .95s cubic-bezier(.33,1,.68,1) both .35s; }
.hero__sub { animation: heroFadeUp .8s cubic-bezier(.33,1,.68,1) both .85s; }
.hero__cta { animation: heroFadeUp .7s cubic-bezier(.33,1,.68,1) both 1.05s; }
.hero__stage { animation: heroStage 1.4s cubic-bezier(.16,1,.3,1) both .3s; }
.hud { animation: heroFade 1s ease both 1.3s; }

/* mask-reveal for big titles */
.mask { display: block; overflow: hidden; padding-bottom: 0.08em; margin-bottom: -0.08em; }
.mask > span { display: block; }
.anim .mask > span { transform: translateY(110%); will-change: transform; }
.anim .mask.is-in > span { transform: translateY(0); transition: transform 1s var(--ease-out); }

/* ============================================================
   HERO — asymmetric copy / canopy stage
   ============================================================ */
.hero { min-height: 100svh; display: flex; flex-direction: column; justify-content: center; padding-top: clamp(5.5rem, 9vh, 6.5rem); padding-bottom: 2.5rem; position: relative; overflow: hidden; }
.hero .wrap { position: relative; z-index: 2; }

.hero__grid { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, .4fr); gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
.hero__copy { max-width: none; }

.hero__top { display: flex; align-items: center; gap: 1rem; margin-bottom: 2.2rem; }
.boot { font-family: var(--font-mono); font-size: .72rem; letter-spacing: .12em; color: var(--accent); text-transform: uppercase; white-space: nowrap; }
.hero__line { height: 1px; width: 100%; max-width: 340px; background: linear-gradient(90deg, var(--accent), transparent); box-shadow: 0 0 12px var(--accent-glow); transform-origin: left; }

.hero__eyebrow { margin-bottom: 0.9rem; color: var(--ink-dim); }
.hero__title { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-hero); line-height: 0.98; letter-spacing: -0.04em; color: var(--ink); overflow: hidden; padding-bottom: 0.14em; }
.hero__title-inner { display: block; }
.hero__title .accent-word { color: var(--accent); text-shadow: 0 0 26px var(--accent-glow); }
.hero__sub { margin-top: 1.3rem; max-width: 42ch; font-size: clamp(0.98rem, 1.05vw, 1.2rem); color: var(--ink-dim); }
.hero__cta { margin-top: 1.7rem; display: flex; flex-wrap: wrap; gap: 1rem; }

/* the canopy: a single framed glass viewport holding the hero asset */
.hero__stage { position: relative; aspect-ratio: 1 / 1.12; max-width: 360px; justify-self: end; width: 100%; }
.hero__canopy { position: absolute; inset: 0; border-radius: var(--r-panel); overflow: hidden; }
.hero__canopy-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 60% 35%; opacity: .92; will-change: transform; }
/* brand mode: the lockup is a logo, not a photo — contain it, center it, let
   it glow, instead of cropping it like a full-bleed image. */
.hero__canopy--brand { background: radial-gradient(120% 90% at 50% 50%, rgba(215,255,55,.16), transparent 65%), var(--bg-deep); }
.hero__canopy--brand .hero__canopy-img { object-fit: contain; object-position: center; padding: 15%; opacity: 1; filter: drop-shadow(0 0 44px rgba(215,255,55,.25)); }
.hero__canopy-sweep { position: absolute; inset: -20% -60%; background: linear-gradient(100deg, transparent 42%, rgba(255,255,255,.16) 50%, transparent 58%); animation: sweepOnce 5s cubic-bezier(.4,0,.2,1) both 1.4s; pointer-events: none; }
.hero__corner { position: absolute; width: 22px; height: 22px; border: 1px solid rgba(255,255,255,.28); opacity: .75; }
.hero__corner--tl { top: 14px; left: 14px; border-right: 0; border-bottom: 0; }
.hero__corner--br { bottom: 14px; right: 14px; border-left: 0; border-top: 0; }
.hero__readout { position: absolute; font-family: var(--font-mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: rgba(255,255,255,.55); }
.hero__readout--tr { top: 20px; right: 22px; text-align: right; }
.hero__readout--bl { bottom: 20px; left: 22px; }
.hero__readout b { color: var(--accent); font-weight: 500; }

@media (max-width: 1220px) {
  .hero__grid { grid-template-columns: 1fr; }
  .hero__stage { max-width: 420px; justify-self: center; margin-top: 2.5rem; aspect-ratio: 16/11; }
  .hero__copy { max-width: none; }
}

/* section background image accents (parallax) */
.bg-accent { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.bg-accent img { position: absolute; width: 105%; height: 135%; left: 0; top: -18%; object-fit: cover; opacity: .16; will-change: transform; -webkit-mask-image: linear-gradient(180deg, transparent, #000 30%, #000 70%, transparent); mask-image: linear-gradient(180deg, transparent, #000 30%, #000 70%, transparent); }
.sec > .wrap, .cta-band > .wrap { position: relative; z-index: 2; }

/* HUD telemetry — justified here: Kivo is a genuinely two-timezone
   studio (Miami/Barcelona), not decorative locale-dressing. */
.hud { position: absolute; z-index: 3; font-family: var(--font-mono); font-size: .68rem; letter-spacing: .1em; color: var(--ink-mute); text-transform: uppercase; display: flex; flex-direction: column; gap: .3rem; }
.hud--br { right: var(--pad); bottom: 2.2rem; text-align: right; align-items: flex-end; }
.hud b { color: var(--ink-dim); font-weight: 400; }
.hud .dot { color: var(--accent); }
@media (max-width: 720px) { .hud--br { display: none; } }

/* ============================================================
   HERO — cinematic full-bleed (home only)
   The monolith render bleeds off the top of the frame instead of sitting
   inside a glass card; a mask fades it to black on the left so copy reads
   clean without a scrim. No glass here on purpose — glass stays UI
   vocabulary (nav/cards/panels), the hero stays raw image + type.
   ============================================================ */
/* opaque black: the fixed lime nebula (#fx) shows on every other section
   but never inside the hero, not even through the video's masked edge */
.hero--cinematic { justify-content: flex-end; padding-bottom: clamp(3rem, 7vh, 4.5rem); background: var(--bg-deep); }
.hero__frame { position: absolute; inset: -15svh 0 0 0; height: 130svh; z-index: 0; overflow: hidden; }
.hero__frame-img {
  width: 100%; height: 100%; object-fit: cover; object-position: 68% 22%;
  filter: grayscale(35%) contrast(1.12) brightness(.85);
  -webkit-mask-image: linear-gradient(112deg, transparent 0%, transparent 30%, #000 56%, #000 100%);
  mask-image: linear-gradient(112deg, transparent 0%, transparent 30%, #000 56%, #000 100%);
  animation: heroFrameIn 1.8s var(--ease-out) both .2s;
}
@keyframes heroFrameIn { from { -webkit-mask-position: -18% 0; mask-position: -18% 0; opacity: 0; } to { -webkit-mask-position: 0 0; mask-position: 0 0; opacity: 1; } }
/* photographic hero: pushed near-monochrome so the shot's own colour cast
   (warm studio light) cannot fight the single lime accent, and darkened
   further because a photo carries far more mid-tone noise than a render. */
/* The source is a 1080x1350 social piece with Spanish copy across its top
   band. The hero crops hard in landscape, so anchoring low keeps the
   photographic half and pushes the baked-in text out of frame. */
.hero__frame-img--photo { filter: grayscale(100%) contrast(1.22) brightness(.42); object-position: 50% 62%; }
/* 3D render (dev/render "hero"): already in the palette, so no grey filter.
   The composition sits in the right 60% of a 4:3 frame; anchoring right
   keeps the orbit whole when the frame crops a narrower viewport. */
.hero__frame-img--render { filter: none; object-position: 72% 46%; }
.hero--cinematic .wrap { width: 100%; }
/* both live left-aligned, stacked: that's the zone the frame mask
   guarantees dark, unlike the right side where the image sits at full
   opacity and busy chrome detail would fight the text for contrast. */
.hero__masthead { display: flex; flex-direction: column; gap: .5rem; margin-bottom: clamp(3rem, 20vh, 9rem); }
.hero__masthead .eyebrow { margin: 0; }
.hero__masthead .hud { position: static; flex-direction: row; gap: 1.2rem; }
.hero--cinematic .hero__title { font-size: clamp(2.1rem, 2.4vw + 1.3rem, 4rem); max-width: 25ch; text-shadow: 0 2px 24px rgba(0,0,0,.7); }
.hero--cinematic .hero__sub { max-width: 46ch; text-shadow: 0 2px 16px rgba(0,0,0,.85); }
.hero--cinematic .hero__eyebrow, .hero--cinematic .hero__masthead .hud { text-shadow: 0 1px 12px rgba(0,0,0,.9); }
@media (max-width: 720px) {
  .hero__frame { inset: 0; height: 62svh; -webkit-mask-image: linear-gradient(180deg, transparent, #000 55%); mask-image: linear-gradient(180deg, transparent, #000 55%); }
  .hero__frame-img { object-position: 60% 30%; -webkit-mask-image: none; mask-image: none; animation: heroFadeUp .9s var(--ease-out) both .1s; }
  .hero--cinematic { justify-content: flex-end; padding-top: clamp(5.5rem, 9vh, 6.5rem); }
  .hero__masthead { margin-bottom: 1.5rem; }
  .hero__masthead .hud { display: none; }
}
/* phones: the frame is a short band, so a 4:3 render is cropped hard on the
   sides. The orbit spans ~41-95% of the render's width; anchoring near the
   right edge keeps all of it in view. Must follow the generic phone rule. */
@media (max-width: 720px) { .hero__frame-img--render { object-position: 90% 30%; } }
/* video hero: rendered in the palette already (no filter). The wall sits
   right of centre with its hub at ~63% / 34%, so the crop is anchored right
   and the lower left stays dark for the headline; on phones the crop centres
   the hub. */
.hero__frame-img--video { filter: none; object-position: 75% 40%; }
/* the video frame is exactly the hero (no bleed above it like the photo had),
   so the wall's top row clears the nav and the video is upscaled less */
@media (min-width: 721px) { .hero__frame--video { inset: 0; height: 100%; } }
/* wider than 16:9 (most browser windows, ultrawides) a cover crop would cut
   the wall into the nav and the headline: fit the height instead, pinned
   right; the uncovered strip on the left is black like the hero and sits
   under the mask anyway */
@media (min-width: 721px) and (min-aspect-ratio: 16/9) { .hero__frame-img--video { object-fit: contain; object-position: 100% 50%; } }
@media (max-width: 720px) { .hero__frame-img--video { object-position: 72% 40%; } }
@media (prefers-reduced-motion: reduce) { .hero__frame-img { animation: none !important; } }

/* ============================================================
   MASTHEAD — the header every page but the home shares.
   Replaces the old copy-left / glass-viewport-right hero, whose
   viewport was a small dark box holding a render that read as an
   empty rectangle, and whose type was sized like body copy.
   Here the page title IS the header: display scale, bottom-aligned
   on a hairline, over a dot field with a signal crossing it
   (js/sonar.js). One component, four pages, identical geometry —
   only the type and the telemetry change.
   ============================================================ */
.masthead {
  position: relative; overflow: hidden;
  min-height: clamp(30rem, 68svh, 46rem);
  display: flex; flex-direction: column; justify-content: flex-end;
  padding-top: clamp(7rem, 16vh, 11rem); padding-bottom: clamp(2.5rem, 6vh, 4rem);
  border-bottom: 1px solid var(--hairline);
}
/* Static floor: the dot field as plain CSS, so a page with no JS still
   gets the texture. sonar.js hides it once the canvas is live. */
.masthead::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image: radial-gradient(circle, var(--accent-soft) 0 1.15px, transparent 1.6px);
  background-size: 26px 26px; opacity: .9; transition: opacity .4s;
}
.masthead.sonar-on::before { opacity: 0; }
.masthead > .sonar { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
/* The field is loudest where nothing is written and quietest under the
   type, so the rings stay visible without ever fighting the headline. */
.masthead::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(90% 70% at 22% 82%, rgba(0,0,0,.92) 0%, rgba(0,0,0,.55) 42%, transparent 72%),
    linear-gradient(180deg, rgba(0,0,0,.55) 0%, transparent 26%, transparent 62%, rgba(0,0,0,.6) 100%);
}
.masthead .wrap { position: relative; z-index: 2; }

.masthead__top {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 2rem;
  padding-bottom: clamp(1.6rem, 5vh, 3.4rem);
}
.masthead__eyebrow { margin: 0; color: var(--ink-dim); }
.masthead__tel {
  display: flex; gap: clamp(1rem, 2.4vw, 2.2rem); text-align: right;
  font-family: var(--font-mono); font-size: .68rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-mute); white-space: nowrap;
}
.masthead__tel b { display: block; color: var(--accent); font-weight: 500; font-size: .95rem; letter-spacing: .04em; margin-bottom: .2rem; }

/* Masked line-rise, driven by CSS keyframes rather than the JS .mask
   observer: this sits above the fold on every page, and an observer that
   has not fired yet leaves the page title parked off-screen. */
.masthead__title {
  font-family: var(--font-display); font-weight: 700; color: var(--ink);
  font-size: clamp(2.5rem, 5.6vw, 5.4rem); line-height: .94; letter-spacing: -0.038em;
  max-width: 17ch; overflow: hidden; padding-bottom: .12em;
}
.masthead__title-inner { display: block; animation: heroUp .95s cubic-bezier(.33,1,.68,1) both .28s; }
.masthead__sub {
  margin-top: clamp(1.1rem, 2.6vh, 1.8rem); max-width: 54ch;
  font-size: clamp(1rem, .45vw + .9rem, 1.2rem); color: var(--ink-dim);
}
/* A single lime tick on the baseline: the page's entry point on the spine,
   drawn rather than decorative — it grows as the header settles. */
.masthead__rule {
  position: relative; z-index: 2; margin-top: clamp(2rem, 5vh, 3.2rem);
  height: 1px; background: var(--hairline);
}
.masthead__rule::before {
  content: ""; position: absolute; inset: 0 auto 0 0; width: clamp(90px, 14vw, 220px);
  background: var(--accent); box-shadow: 0 0 14px var(--accent-glow);
  transform-origin: left; animation: mastRule 1.1s var(--ease-out) both .55s;
}
@keyframes mastRule { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.masthead__eyebrow { animation: heroFadeUp .7s cubic-bezier(.33,1,.68,1) both .12s; }
.masthead__tel { animation: heroFade 1s ease both .9s; }
.masthead__sub { animation: heroFadeUp .8s cubic-bezier(.33,1,.68,1) both .62s; }

@media (max-width: 760px) {
  .masthead { min-height: clamp(26rem, 62svh, 34rem); }
  .masthead__tel { display: none; }
  .masthead__title { font-size: clamp(2.1rem, 9vw, 3.2rem); max-width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .masthead__rule::before, .masthead__eyebrow, .masthead__tel,
  .masthead__sub, .masthead__title-inner { animation: none !important; }
}

/* ============================================================
   STATEMENT / MANIFESTO
   ============================================================ */
/* Manifesto: a lead statement carries the claim at display scale, the
   argument follows as readable body text on an indented column. Replaces
   the old 50-word block set entirely at display size with four words lit
   at random, which read as arbitrary rather than emphatic. One deliberate
   accent ("We keep going") marks the turn of the sentence. */
.manifesto__lead {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.9rem, 3.4vw + .6rem, 3.6rem);
  line-height: 1.04; letter-spacing: -0.028em; color: var(--ink); max-width: 22ch;
}
.manifesto__turn { color: var(--accent); }
.manifesto__body {
  margin-top: clamp(1.5rem, 3.5vh, 2.4rem);
  padding-top: clamp(1.3rem, 3vh, 1.9rem);
  border-top: 1px solid var(--hairline);
  margin-left: 38%; max-width: 52ch;
  font-size: clamp(1rem, .4vw + .9rem, 1.15rem); line-height: 1.7; color: var(--ink-dim);
}
@media (max-width: 860px) {
  .manifesto__lead { max-width: 100%; }
  .manifesto__body { margin-left: 0; max-width: 100%; }
}
.em-accent { color: var(--accent); }

/* ============================================================
   SERVICES PAGE — index, chapters, combinations
   One family of six objects (assets/img/services/svc-0N, alpha WebP)
   is the page's whole iconography: glyph in the index, lead on the
   stage while its chapter is read, unit in the combinations.
   ============================================================ */

/* ---- 1 · Index: one display sentence that is also the navigation ---- */
.toc-sec { padding-block: clamp(3.5rem, 9vh, 6.5rem); }
/* Sized to the container, not the viewport, so the sentence always sets as
   three lines of two. --toc-fit is the widest pair measured in font-sizes,
   plus slack: EN "AI & Automation, Custom Software & CRM," is 21.9, ES
   "IA y Automatización, Software a Medida y CRM," is 23.6. */
.toc { --toc-fit: 23; }
:lang(es) .toc { --toc-fit: 24.6; }
.toc {
  font-family: var(--font-display); font-weight: 600;
  font-size: min(3.3rem, calc((min(100vw, var(--maxw)) - 2 * var(--pad)) / var(--toc-fit)));
  line-height: 1.14; letter-spacing: -0.03em;
  color: var(--ink);
}
.toc__item { --i: 0; color: var(--ink); transition: color .3s var(--ease); }
.toc__g {
  display: inline-block; width: 1.15em; height: 1.15em; margin-right: .18em;
  vertical-align: -0.18em;
}
.toc__g img { width: 100%; height: 100%; display: block; transition: rotate .3s var(--ease), scale .3s var(--ease); }
.toc__sep { color: var(--ink-mute); }
.toc__item:hover, .toc__item:focus-visible { color: var(--accent); }
.toc__item:hover .toc__g img, .toc__item:focus-visible .toc__g img { scale: 1.12; rotate: -8deg; }
.toc__item:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 4px; }
/* entry: only the glyphs move, and never from opacity 0. This block can sit
   in the first viewport, and an opacity-0 start would push LCP.
   The entry lives on the .toc__g wrapper and the hover on the <img>, so
   the two transitions (one delayed, one instant) never share a property. */
.anim .toc__g { transform: scale(.6); transition: transform .7s var(--ease-out) calc(var(--i) * 70ms); }
.anim .toc-sec.is-glyphs .toc__g { transform: none; }

/* each discipline stays whole; the sentence only breaks at its commas */
@media (min-width: 768px) { .toc__item { white-space: nowrap; } }
@media (max-width: 767px) {
  /* the sentence becomes a 2x3 grid of tappable units: same markup */
  /* two children per cell (glyph over name) and minmax(0,1fr): the earlier
     glyph-beside-name cell overflowed 390px phones by 20px */
  .toc {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.4rem 1rem;
    font-size: 1.05rem; line-height: 1.2; letter-spacing: -0.01em;
  }
  .toc__sep { display: none; }
  .toc__item { display: flex; flex-direction: column; align-items: flex-start; gap: .45rem; min-width: 0; min-height: 44px; }
  .toc__g { width: 44px; height: 44px; flex: none; margin: 0; }
}

/* ---- 2 · Chapters: text runs left, the stage holds the current object ---- */
.chapters { position: relative; }
/* single column (phones, tablets, no JS): same measure as .wrap */
.chapters__text { max-width: var(--maxw); margin-inline: auto; }
.ch {
  display: flex; flex-direction: column;
  padding: 3.5rem var(--pad); scroll-margin-top: calc(var(--nav-h) + 1rem);
}
.ch__media { margin: 0 calc(-1 * var(--pad)) 1.6rem; aspect-ratio: 4 / 3; max-height: 56svh; overflow: hidden; }
.ch__media img { width: 100%; height: 100%; object-fit: contain; display: block; }
.ch__tick {
  display: block; width: 48px; height: 2px; margin-bottom: 1.4rem;
  background: var(--accent); transform-origin: left; transform: scaleX(1);
}
.ch__name {
  font-family: var(--font-display); font-weight: 700; color: var(--ink);
  font-size: clamp(2.1rem, 4.2vw, 4.2rem); line-height: .96; letter-spacing: -0.035em; max-width: 13ch; text-wrap: balance;
}
.ch__stand {
  margin-top: 1.1rem; max-width: 30ch;
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.15rem, 1.5vw, 1.45rem); line-height: 1.3; color: var(--ink);
}
.ch__desc { margin-top: .9rem; max-width: 44ch; font-size: 1.05rem; line-height: 1.65; color: var(--ink-dim); }

/* colophon: one hairline over the group, none between rows */
.ch__colophon { margin-top: 2rem; padding-top: 1.4rem; border-top: 1px solid var(--hairline); display: grid; gap: .9rem; }
.ch__colophon > div { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 1rem; align-items: baseline; }
/* --ink-dim, not --ink-mute: #6b6b6b on black is 3.9:1, under AA at this size */
.ch__colophon dt { font-size: .85rem; color: var(--ink-dim); }
.ch__get {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(3, auto);
  grid-auto-flow: column; gap: .5rem 1.8rem; font-size: 1rem; line-height: 1.45; color: var(--ink-soft);
}
.ch__stack { font-family: var(--font-mono); font-size: .78rem; line-height: 1.6; color: var(--ink-dim); }
.ch__proof a {
  white-space: nowrap; /* a client name never breaks across lines */
  color: var(--ink-soft); border-bottom: 1px solid var(--hairline-2); padding-bottom: 1px;
  transition: color .25s, border-color .25s;
}
.ch__proof a:hover, .ch__proof a:focus-visible { color: var(--accent); border-color: var(--accent); }

.chapters__aside { display: none; }

/* Two-column scrollytelling only when JS is present (set in <head>, so no
   jump on load) and there is room for it. Without JS the stage could not
   switch objects, so every chapter carries its own image instead. */
@media (min-width: 1024px) and (min-height: 600px) {
  .js .chapters {
    display: grid;
    grid-template-columns:
      [gutter] max(var(--pad), calc((100% - var(--maxw)) / 2 + var(--pad)))
      [text] minmax(0, 36rem)
      [gap] clamp(2rem, 5vw, 5rem)
      [stage] minmax(0, 1fr);
  }
  .js .chapters__text { grid-column: text; grid-row: 1; max-width: none; margin: 0; }
  .js .ch { min-height: 88svh; justify-content: center; padding: clamp(4rem, 11vh, 7rem) 0; }
  .js .ch__media { display: none; }
  .js .ch__tick { transform: scaleX(0); transition: transform .5s var(--ease-out); }
  .js .ch.is-active .ch__tick { transform: scaleX(1); }

  .js .chapters__aside {
    display: grid; place-items: center; grid-column: stage; grid-row: 1;
    position: sticky; top: 0; height: 100svh; align-self: start;
    overflow: clip; /* the scaled .is-after objects widened touch viewports */
  }
  .stage { width: 100%; height: 100%; display: grid; place-items: center; }
  .stage__frame { position: relative; width: min(90%, 80svh); aspect-ratio: 1; }
  .stage__obj { position: absolute; inset: 0; opacity: 0; transition: opacity .8s var(--ease-out), transform .8s var(--ease-out); }
  .stage__obj img { width: 100%; height: 100%; object-fit: contain; display: block; }
  /* directional crossfade: the old object sinks the way you scroll, the new
     one rises into place */
  .stage__obj.is-on { opacity: 1; transform: none; }
  .stage__obj.is-before { opacity: 0; transform: scale(.94) translateY(-3%); }
  .stage__obj.is-after { opacity: 0; transform: scale(1.06) translateY(3%); }

  /* stage nav: plain type in the zone the objects have already faded to
     black; a lime tick slides to the current chapter. No glass: nothing
     here floats over content. */
  .stage-nav {
    position: absolute; right: var(--pad); bottom: clamp(1.5rem, 6vh, 3rem);
    display: flex; flex-direction: column; padding-left: 1rem;
    font-family: var(--font-mono); font-size: .7rem; line-height: 1.9em; letter-spacing: .04em;
  }
  .stage-nav a { color: var(--ink-dim); transition: color .25s; white-space: nowrap; }
  .stage-nav a:hover, .stage-nav a:focus-visible { color: var(--ink); }
  .stage-nav a[aria-current="true"] { color: var(--accent); }
  .stage-nav__tick {
    position: absolute; left: 0; top: .3em; width: 1px; height: 1.3em; background: var(--accent);
    transform: translateY(calc(var(--i) * 1.9em)); transition: transform .5s var(--ease-out);
  }
}
@media (max-width: 767px) {
  .ch__colophon > div { grid-template-columns: 1fr; gap: .45rem; }
  .ch__get { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-flow: row; }
}

/* ---- 3 · Combinations: real builds written as sums of disciplines ---- */
.mix { margin-top: clamp(2.5rem, 6vh, 4rem); border-top: 1px solid var(--hairline); }
.mix__row {
  display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: 2.5rem; align-items: center; padding-block: 1.8rem;
}
.mix__client {
  font-family: var(--font-display); font-weight: 600; color: var(--ink);
  font-size: clamp(1.5rem, 2.4vw, 2.2rem); line-height: 1.05; letter-spacing: -0.025em;
  transition: color .25s;
}
.mix__client:hover, .mix__client:focus-visible { color: var(--accent); }
.mix__formula { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem .7rem; margin-top: .9rem; }
.mix__unit { display: inline-flex; align-items: center; gap: .45rem; font-size: .9rem; color: var(--ink-soft); transition: color .25s; }
.mix__unit:hover, .mix__unit:focus-visible { color: var(--accent); }
.mix__g { width: 40px; height: 40px; flex: none; }
.mix__g img { width: 100%; height: 100%; display: block; }
.mix__plus { display: inline-block; font-family: var(--font-mono); color: var(--accent); font-size: .95rem; }
.mix__line { color: var(--ink-dim); max-width: 44ch; }
/* the sum assembles itself: the plus lands, then the second term slides in */
.anim .mix__row .mix__plus { transform: scale(0); transition: transform .3s var(--ease-out) .15s; }
.anim .mix__row .mix__unit ~ .mix__unit { transform: translateX(-14px); opacity: 0; transition: transform .45s var(--ease-out) .25s, opacity .45s var(--ease-out) .25s; }
.anim .mix__row.is-in .mix__plus { transform: none; }
.anim .mix__row.is-in .mix__unit ~ .mix__unit { transform: none; opacity: 1; }
@media (max-width: 767px) {
  .mix__row { grid-template-columns: 1fr; gap: .9rem; padding-block: 2rem 0; }
  .mix__g { width: 36px; height: 36px; }
}
@media (prefers-reduced-motion: reduce) {
  .toc__g, .toc__g img, .stage__obj, .ch__tick, .stage-nav__tick,
  .mix__row .mix__plus, .mix__row .mix__unit { animation: none !important; transition: none !important; }
  .anim .mix__row .mix__plus { transform: none; }
  .anim .mix__row .mix__unit ~ .mix__unit { transform: none; opacity: 1; }
  .anim .toc__g { transform: none; }
}

/* ============================================================
   PROOF — home's work teaser: five real client sites in two
   staggered columns. Every capture keeps its own 1.45 ratio (they
   carry headlines; cropping cuts them mid-word). The wide column,
   led by EIMEC, starts lower than the narrow one: an editorial
   spread, not a grid of equal tiles. Captions sit under the image.
   ============================================================ */
.proof {
  display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(1rem, 2vw, 1.6rem); align-items: start;
  margin-top: clamp(2.5rem, 6vh, 4rem);
}
.proof__col { display: flex; flex-direction: column; gap: clamp(2.2rem, 5vh, 3.4rem); min-width: 0; }
.proof__col--wide { padding-top: clamp(3rem, 9vh, 6.5rem); }
.proof__cell { display: flex; flex-direction: column; min-width: 0; color: inherit; }
.proof__media {
  position: relative; margin: 0; overflow: hidden; border-radius: var(--r-card);
  border: 1px solid var(--hairline); background: var(--surface-2); aspect-ratio: 1200 / 827;
}
.proof__media img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: saturate(.8); transition: transform .9s var(--ease), filter .5s;
}
.proof__cell:hover .proof__media img, .proof__cell:focus-visible .proof__media img { transform: scale(1.03); filter: saturate(1); }
.proof__cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: var(--r-card); }
.proof__cap { padding-top: 1rem; }
.proof__cat { font-family: var(--font-mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--accent); }
.proof__client {
  margin-top: .45rem; font-family: var(--font-display); font-weight: 600; color: var(--ink);
  font-size: clamp(1.35rem, 2vw, 1.9rem); line-height: 1.05; letter-spacing: -0.025em; transition: color .25s;
}
.proof__col--wide .proof__client { font-size: clamp(1.6rem, 2.6vw, 2.4rem); }
.proof__cell:hover .proof__client { color: var(--accent); }
.proof__scope { margin-top: .4rem; max-width: 46ch; font-size: .95rem; line-height: 1.55; color: var(--ink-dim); }
@media (max-width: 760px) {
  /* one column in data order: the column wrappers dissolve and each cell's
     inline `order` puts EIMEC, Nadiva, JD Yuversity... back in sequence */
  .proof { display: flex; flex-direction: column; gap: 2.4rem; }
  .proof__col { display: contents; }
}

/* ============================================================
   OWN PRODUCT — Kivo CRM, shown with a capture of its real site
   ============================================================ */
.proofnode { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.proofnode__panel { margin: 0; padding: .6rem; border-radius: var(--r-panel); }
.proofnode__panel img { display: block; width: 100%; height: auto; border-radius: calc(var(--r-panel) - .6rem); }
.proofnode .lead { margin-top: clamp(1rem, 2.4vh, 1.5rem); }
.proofnode .btn { margin-top: clamp(1.4rem, 3.4vh, 2.2rem); }

/* ============================================================
   STATS — floating glass tiles (bento, distinct from the
   connected timeline used by "how we work" below)
   ============================================================ */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(.8rem, 1.6vw, 1.2rem); }
.stat { padding: clamp(1.6rem, 3vw, 2.4rem) clamp(1.4rem, 2.4vw, 1.8rem); border-radius: var(--r-card); }
.stat__value { font-family: var(--font-display); font-size: clamp(2.4rem, 5.4vw, 4.2rem); color: var(--ink); line-height: 1; }
.stat__value .accent { color: var(--accent); }
.stat__label { font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-dim); margin-top: .8rem; }
@media (max-width: 720px) { .stats { grid-template-columns: 1fr 1fr; } }

/* ============================================================
   APPROACH / STEPS — connected ladder, not a tile grid
   ============================================================ */
/* Process as an editorial ledger, not a timeline widget: index / title /
   description across three columns, hairline between rows. The previous
   version used filled lime discs on a connector line, which is the
   decorative-dot + "Step 01" badge pattern the skill flags. Here the step
   title carries the meaning and the lime is spent only on hover. */
.steps { margin-top: clamp(2rem, 5vh, 3rem); border-top: 1px solid var(--hairline); }
.step {
  display: grid; grid-template-columns: 3.5rem minmax(0, .85fr) minmax(0, 1.15fr);
  gap: clamp(1rem, 3vw, 3rem); align-items: start;
  padding-block: clamp(1.6rem, 3.5vh, 2.6rem); border-bottom: 1px solid var(--hairline);
}
.step__n { font-family: var(--font-mono); font-size: .78rem; letter-spacing: .12em; color: var(--ink-mute); padding-top: .5rem; transition: color .3s var(--ease); }
.step:hover .step__n { color: var(--accent); }
.step__title { font-family: var(--font-display); font-size: clamp(1.35rem, 2.3vw, 2rem); line-height: 1.05; color: var(--ink); }
.step__desc { color: var(--ink-dim); font-size: .98rem; max-width: 54ch; }
@media (max-width: 860px) {
  .step { grid-template-columns: 2.8rem minmax(0, 1fr); gap: 1rem; }
  .step__desc { grid-column: 2; margin-top: .7rem; }
}

/* ============================================================
   VALUES / LOCATIONS (about)
   ============================================================ */
.values { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); border-radius: var(--r-panel); overflow: hidden; margin-top: 2.5rem; }
.value { background: var(--bg-deep); padding: clamp(1.8rem, 3.5vw, 3rem); }
.value h3 { font-size: var(--fs-h3); color: var(--ink); margin-bottom: .8rem; }
.value p { color: var(--ink-dim); }
.value__idx { font-family: var(--font-mono); font-size: .74rem; color: var(--accent); display: block; margin-bottom: 1rem; }

.nodes { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.5rem, 4vw, 3rem); margin-top: 2.5rem; }
.node { border-radius: var(--r-panel); padding: clamp(1.8rem, 3.5vw, 2.8rem); position: relative; }
.node__city { font-family: var(--font-display); font-size: clamp(1.8rem, 3.5vw, 3rem); color: var(--ink); }
.node__coord { font-family: var(--font-mono); font-size: .78rem; color: var(--accent); margin: .6rem 0 1rem; letter-spacing: .06em; }
.node__note { color: var(--ink-dim); }
.node::after { content: ""; position: absolute; right: 1.4rem; top: 1.4rem; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px var(--accent-glow); animation: blink 2.6s infinite; }
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: .3; } }

.story { max-width: 62ch; display: flex; flex-direction: column; gap: 1.4rem; }
.story p { font-size: clamp(1.1rem, 1.5vw, 1.35rem); color: var(--ink-soft); line-height: 1.6; }
.story p:first-child::first-letter { font-family: var(--font-display); font-size: 3.6em; line-height: .8; float: left; padding: .05em .12em 0 0; color: var(--accent); }

/* ============================================================
   CONTACT
   ============================================================ */
.contact-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; margin-top: 2rem; }
.contact-grid form { padding: clamp(1.6rem, 3vw, 2.4rem); border-radius: var(--r-panel); }
.field { display: flex; flex-direction: column; gap: .5rem; margin-bottom: 1.4rem; }
.field label { font-family: var(--font-mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: var(--ink-dim); }
.field input, .field textarea { background: rgba(255,255,255,.035); border: 1px solid var(--hairline); border-radius: var(--r-chip); padding: .9rem 1rem; color: var(--ink); font: inherit; transition: border-color .3s, box-shadow .3s; }
.field input:focus, .field textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.field textarea { min-height: 130px; resize: vertical; }
.contact-detail { display: flex; flex-direction: column; gap: 1.6rem; }
.contact-detail a { font-family: var(--font-display); font-size: clamp(1.3rem, 2.4vw, 1.9rem); color: var(--ink); transition: color .3s; }
.contact-detail a:hover { color: var(--accent); }
.contact-detail .lbl { font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .12em; color: var(--ink-mute); display: block; margin-bottom: .3rem; }
.contact-note { font-size: .9rem; color: var(--ink-dim); margin-top: 1rem; }
@media (max-width: 980px) { .contact-grid { grid-template-columns: 1fr; } }
/* form states (js/main.js adds .is-invalid / .is-ok / .is-err). Errors take
   the one colour outside the palette on purpose: a state, not decoration. */
.field__err { display: none; margin: 0; font-size: .82rem; color: #ff8a7a; }
.field.is-invalid .field__err { display: block; }
.field.is-invalid input, .field.is-invalid textarea { border-color: #ff8a7a; }
.field.is-invalid input:not([type=checkbox]):focus, .field.is-invalid textarea:focus { border-color: var(--accent); }
/* consent: checkbox + sentence on one row, its error under both */
.field--check { flex-direction: row; flex-wrap: wrap; align-items: flex-start; gap: .75rem; }
.field--check input { flex: 0 0 auto; width: 1.15rem; height: 1.15rem; margin: .2rem 0 0; padding: 0; border: 0; background: none; accent-color: var(--accent); }
.field--check input:focus { box-shadow: none; }
.field--check input:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.field--check label { flex: 1 1 0; min-width: 0; font-family: var(--font-body); font-size: .9rem; line-height: 1.5; text-transform: none; letter-spacing: 0; color: var(--ink-dim); }
.field--check .field__err { flex-basis: 100%; }
.field--check a, .form-info a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); text-underline-offset: 3px; }
/* honeypot: off-screen for people, a plain field for bots */
.hp { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.form-status { min-height: 1.5em; margin: 1rem 0 0; font-size: .92rem; color: var(--ink-dim); }
.form-status.is-ok { color: var(--accent); }
.form-status.is-err { color: #ff8a7a; }
.form-info { margin-top: 1.2rem; font-size: .8rem; line-height: 1.6; color: var(--ink-dim); }
/* no-JS result: the server redirects to #sent or #error */
.form-flash { display: none; margin: 0 0 1.4rem; padding: .9rem 1rem; border-radius: var(--r-chip); font-size: .92rem; scroll-margin-top: calc(var(--nav-h) + 2rem); }
.form-flash:target { display: block; }
.form-flash--ok { border: 1px solid var(--accent); color: var(--accent); background: var(--accent-soft); }
.form-flash--err { border: 1px solid #ff8a7a; color: #ffb4a8; background: rgba(255, 138, 122, .08); }

/* ---------- legal documents + 404 ---------- */
.sec--doc { padding-top: clamp(2.5rem, 6vh, 4rem); }
.prose { max-width: 72ch; font-size: 1rem; line-height: 1.7; color: var(--ink-dim); }
.prose h2 { margin: 2.6rem 0 .8rem; font-family: var(--font-display); font-weight: 600; font-size: clamp(1.15rem, 1rem + .6vw, 1.45rem); letter-spacing: -.01em; color: var(--ink); }
.prose p, .prose ul, .prose dl { margin: 0 0 1rem; }
.prose ul { padding-left: 1.2rem; list-style: disc; }
.prose li { margin-bottom: .45rem; }
.prose li::marker { color: var(--accent); }
.prose a, .nf__es a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--accent); text-underline-offset: 3px; }
.prose a:hover, .nf__es a:hover { color: var(--accent); }
.prose__updated { font-family: var(--font-mono); font-size: .75rem; letter-spacing: .1em; text-transform: uppercase; }
.prose__dl { border-top: 1px solid var(--hairline); }
.prose__dl div { display: grid; grid-template-columns: minmax(8rem, 14rem) 1fr; gap: 1rem; padding: .7rem 0; border-bottom: 1px solid var(--hairline); }
.prose__dl dt { padding-top: .2rem; font-family: var(--font-mono); font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; }
.prose__dl dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
@media (max-width: 560px) { .prose__dl div { grid-template-columns: 1fr; gap: .2rem; } }
.prose__note { margin-top: 2.5rem; font-size: .85rem; font-style: italic; }
.nf__actions { display: flex; flex-wrap: wrap; gap: 1rem; }
.nf__es { margin-top: 1.6rem; color: var(--ink-dim); }
.console__legal { display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; }
.console__legal a { color: var(--ink-dim); transition: color .3s; }
.console__legal a:hover { color: var(--accent); }

/* ============================================================
   MARQUEE
   ============================================================ */
.marquee { overflow: hidden; border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); padding: 1.4rem 0; display: flex; user-select: none; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee__track { display: flex; flex-shrink: 0; gap: 2.5rem; padding-right: 2.5rem; animation: scrollx 32s linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__track span { font-family: var(--font-display); font-size: clamp(1.4rem, 3vw, 2.6rem); font-weight: 500; color: var(--ink); white-space: nowrap; display: inline-flex; align-items: center; gap: 2.5rem; }
.marquee__track span::after { content: "✦"; color: var(--accent); font-size: .6em; }
/* uses the standalone `translate` property (not `transform`) so it composes
   cleanly with mqBank's `transform: rotate...` in motion.css instead of the
   two animations fighting over the same `transform` property and one
   silently winning (that was the marquee-doesn't-move bug). */
@keyframes scrollx { to { translate: -50% 0; } }

/* ============================================================
   WORKS WHEEL — the portfolio index as a wheel you turn.
   Ring at rest, vertical drum once you start turning. Geometry is
   written by js/wheel.js; everything here is surface and type.

   Two deliberate fallbacks, both of which drop the 3D entirely and
   render a plain card list (.wheel--flat):
     · under 760px — a drum needs vertical drag, and claiming the
       vertical axis on a phone traps the page scroll
     · prefers-reduced-motion — a rotating drum is exactly the kind
       of motion that setting is asking us not to do
   ============================================================ */
/* The section is a runway; the pane inside pins to the viewport while you
   turn it. Without the pin the wheel took the scroll wheel the moment the
   pointer crossed it, so it started turning while still half below the
   fold and could never be brought fully into view: the ring stayed cut
   off no matter how you scrolled. Now it locks to the full viewport
   first, turns, and releases the page at either end. */
.wheel { position: relative; height: 128svh; user-select: none; }
.wheel__pin {
  position: sticky; top: 0; height: 100svh; min-height: 32rem;
  overflow: hidden; border-block: 1px solid var(--hairline);
}
.wheel__stage {
  position: absolute; inset: 0; outline: none;
  cursor: grab; touch-action: pan-x;
}
.wheel__stage:active { cursor: grabbing; }
.wheel__stage:focus-visible { outline: 2px solid var(--accent); outline-offset: -6px; }
.wheel__drum { position: absolute; top: 50%; left: 50%; transform-style: preserve-3d; }

.wheel__card { position: absolute; backface-visibility: hidden; display: block; text-decoration: none; }
.wheel__face {
  position: relative; display: block; width: 100%; height: 100%; overflow: hidden;
  border-radius: var(--r-card); background: var(--surface-2);
  border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 26px 60px -22px rgba(0,0,0,.9);
}
.wheel__face img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.62) contrast(1.06); }
/* Only the card at the front is a live target, so the affordance only
   shows there: on the neighbours it would be an invitation to click
   something that is turned half away. */
.wheel__go {
  position: absolute; right: .75rem; bottom: .75rem; z-index: 2;
  display: inline-flex; align-items: center; gap: .4ch;
  font-family: var(--font-mono); font-size: .66rem; letter-spacing: .1em; text-transform: uppercase;
  color: #000; background: var(--accent); border-radius: var(--r-pill); padding: .35rem .7rem;
  opacity: 0; transform: translateY(6px); transition: opacity .3s, transform .3s var(--ease);
}
.wheel__card[aria-selected="true"]:hover .wheel__go { opacity: 1; transform: none; }

/* Ring label and front-card panel trade places across the transition. */
.wheel__label {
  position: absolute; inset: 0; z-index: 3; display: grid; place-items: center;
  pointer-events: none; font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.1rem, 2.2vw, 2rem); letter-spacing: -0.03em; color: var(--ink);
  text-shadow: 0 2px 20px rgba(0,0,0,.9);
}
.wheel__now {
  position: absolute; z-index: 3; left: clamp(1.25rem, 5vw, 4.5rem); top: 50%;
  transform: translateY(-50%); width: min(30%, 26rem); opacity: 0;
}
.wheel__now::before {
  content: ""; position: absolute; inset: -2rem -3rem; z-index: -1;
  background: radial-gradient(70% 60% at 30% 50%, rgba(0,0,0,.92), transparent 78%);
}
.wheel__now-cat { font-family: var(--font-mono); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; color: var(--accent); }
.wheel__now-title { font-family: var(--font-display); font-size: clamp(1.5rem, 2.6vw, 2.4rem); line-height: 1.02; color: var(--ink); margin-top: .7rem; }
.wheel__now-scope { margin-top: .8rem; font-size: .92rem; line-height: 1.6; color: var(--ink-dim); }
.wheel__now-stack { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 1.1rem; }
.wheel__now-stack li {
  font-family: var(--font-mono); font-size: .64rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-dim); border: 1px solid var(--hairline-2); border-radius: var(--r-pill); padding: .25rem .6rem;
}

.wheel__index {
  position: absolute; z-index: 3; top: clamp(1.5rem, 6%, 3rem); right: clamp(1.25rem, 3%, 2.5rem);
  text-align: right; font-family: var(--font-mono); font-size: .68rem; line-height: 1.85;
}
.wheel__index button { color: var(--ink-mute); letter-spacing: .04em; transition: color .25s; padding: 0; }
.wheel__index button:hover { color: var(--ink); }
.wheel__index button[aria-current="true"] { color: var(--accent); }

/* A control that is turned rather than clicked needs to say so once.
   Not a scroll cue for the page: an instruction for this widget, and
   it leaves for good the moment the ring opens. */
.wheel__hint {
  position: absolute; z-index: 3; left: 50%; bottom: clamp(1.2rem, 4vh, 2.2rem); transform: translateX(-50%);
  font-family: var(--font-mono); font-size: .64rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-mute); pointer-events: none; transition: opacity .5s;
}
.wheel.is-drum .wheel__hint { opacity: 0; }

/* ---- flat fallback: phones and reduced motion ---- */
.wheel--flat { height: auto; }
.wheel--flat .wheel__pin { position: static; height: auto; min-height: 0; overflow: visible; border: 0; }
.wheel--flat .wheel__stage { position: static; cursor: auto; touch-action: auto; }
.wheel--flat .wheel__drum {
  position: static; display: grid; gap: clamp(1.6rem, 4vw, 2.4rem);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr));
  transform: none !important;
  /* Same gutter and measure as every other section, so the list reads as
     part of the page rather than a full-bleed strip bolted underneath. */
  width: 100%; max-width: var(--maxw); margin-inline: auto;
  padding: clamp(2.5rem, 7vh, 4.5rem) var(--pad);
}
.wheel--flat .wheel__card {
  position: static; width: auto !important; height: auto !important; margin: 0 !important;
  transform: none !important; opacity: 1 !important;
}
/* height:100% from the drum state would win over aspect-ratio and leave
   the covers stretched into portrait. */
.wheel--flat .wheel__face { transform: none !important; height: auto; aspect-ratio: 1.45 / 1; }
.wheel--flat .wheel__go { opacity: 1; transform: none; }
.wheel--flat .wheel__label,
.wheel--flat .wheel__now,
.wheel--flat .wheel__index,
.wheel--flat .wheel__hint { display: none; }
/* In the flat list each card carries its own caption, because there is
   no front panel to read it off. */
.wheel__caption { display: none; }
.wheel--flat .wheel__caption { display: block; margin-top: .8rem; }
.wheel--flat .wheel__caption b { display: block; font-family: var(--font-display); font-weight: 600; font-size: 1.15rem; color: var(--ink); }
.wheel--flat .wheel__caption i { display: block; font-style: normal; font-family: var(--font-mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase; color: var(--accent); margin-bottom: .35rem; }
.wheel--flat .wheel__caption span { display: block; margin-top: .4rem; font-size: .88rem; color: var(--ink-dim); }

/* ============================================================
   CTA BAND
   ============================================================ */
.cta-band { text-align: center; padding-block: clamp(5rem, 14vh, 10rem); position: relative; overflow: hidden; }
.cta-band__title { font-family: var(--font-display); font-size: clamp(2.4rem, 6.5vw, 6rem); color: var(--ink); line-height: .98; letter-spacing: -0.03em; max-width: 16ch; margin: 0 auto 1.4rem; }
.cta-band__sub { color: var(--ink-dim); max-width: 46ch; margin: 0 auto 2.4rem; font-size: 1.15rem; }

/* ============================================================
   FOOTER / SIGNAL CONSOLE
   ============================================================ */
.console { border-top: 1px solid var(--hairline); padding-block: clamp(3rem, 6vh, 5rem) 2rem; margin-top: 2rem; }
.console__grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr; gap: 2rem; }
.console__tag { font-family: var(--font-body); color: var(--ink-dim); max-width: 34ch; margin-top: 1.2rem; }
.console__col .console__h { font-family: var(--font-mono); font-size: .7rem; text-transform: uppercase; letter-spacing: .14em; color: var(--ink-mute); margin-bottom: 1rem; }
.console__col a, .console__col span { display: block; font-family: var(--font-mono); font-size: .85rem; color: var(--ink-dim); margin-bottom: .55rem; transition: color .25s; }
.console__col a:hover { color: var(--accent); }
.console__base { display: flex; justify-content: space-between; align-items: center; gap: 1rem; border-top: 1px solid var(--hairline); margin-top: 2.5rem; padding-top: 1.5rem; font-family: var(--font-mono); font-size: .72rem; color: var(--ink-mute); flex-wrap: wrap; }
.console__standby { display: inline-flex; align-items: center; gap: .5rem; }
.console__standby::before { content: ""; width: 60px; height: 1px; background: linear-gradient(90deg, var(--accent), transparent); }

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 980px) {
  .proofnode, .nodes, .values { grid-template-columns: 1fr; }
  .console__grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 760px) {
  .nav__links, .nav .lang, .nav > .btn { display: none; }
  .nav__burger { display: inline-flex; }
  .console__grid { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
  .stats { grid-template-columns: 1fr; }
}

/* ---------- reduced motion / no-webgl ---------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .anim .reveal, .anim .mask > span { opacity: 1 !important; transform: none !important; }
  .marquee__track { animation: none; }
  .boot, .hero__line, .hero__eyebrow, .hero__title-inner, .hero__sub, .hero__cta, .hero__stage, .hero__canopy-sweep, .hud { animation: none !important; }
}
.no-js .reveal { opacity: 1 !important; transform: none !important; }
.no-js .nav { background: rgba(4,4,4,.68); -webkit-backdrop-filter: blur(16px) saturate(150%); backdrop-filter: blur(16px) saturate(150%); border-bottom-color: rgba(255,255,255,.1); }
