/* ==========================================================================
   Rubikal — ONE-PAGE EXPERIENCE  ·  assets/css/one.css
   A self-contained design system for the flagship single page (en/index.html).
   Direction: "precision-engineered cinematic" — deep blue-black space, film
   grain, HUD mono labels, oversized display type, hairline rules. The tri-
   colour cube is the only fully-saturated object; UI colour is rationed.
   Old pages keep tokens.css/main.css; this file owns the new page alone.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0 · FONTS (self-hosted, variable)
   -------------------------------------------------------------------------- */
/* The three Latin faces are subset to the glyphs this site actually uses
   (Latin + punctuation + currency + arrows + the handful of symbols in CSS
   content), which halves them: 164,396 → 82,232 bytes. Arabic is deliberately
   absent — Tajawal owns it. The full faces stay in assets/fonts/ as the source
   for re-subsetting; they are never served. Regenerate with:

     pyftsubset assets/fonts/<face>.woff2 \
       --output-file=assets/fonts/<face>-sub.woff2 --flavor=woff2 \
       --layout-features='*' --name-IDs='*' --notdef-outline \
       --unicodes="$(cat scripts/subset-unicodes.txt)"

   --layout-features and --name-IDs keep kerning/ligatures and the licence
   records; the variable weight axes (Clash 200-700, Satoshi 300-900) survive,
   which the fractional weights on this page depend on. */
@font-face {
  font-family: "Clash Display";
  src: url("../fonts/clash-display-variable-sub.woff2") format("woff2");
  font-weight: 200 700;
  font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("../fonts/satoshi-variable-sub.woff2") format("woff2");
  font-weight: 300 900;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../fonts/jetbrains-mono-400-sub.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

/* Arabic. Clash/Satoshi/JetBrains carry zero Arabic glyphs (verified by cmap),
   so the Arabic one-pager pairs Clash with Tajawal: the unicode-range keeps
   these faces off the English page, and per-character fallback routes Arabic
   runs here while Latin runs (brand names, MVP, URLs) stay on Clash/Satoshi.
   Display weights snap to 700/800 — Clash's 560-620 has no Tajawal equivalent. */
@font-face {
  font-family: "Tajawal";
  src: url("../fonts/tajawal-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF;
}
@font-face {
  font-family: "Tajawal";
  src: url("../fonts/tajawal-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF;
}
@font-face {
  font-family: "Tajawal";
  src: url("../fonts/tajawal-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF;
}
@font-face {
  font-family: "Tajawal";
  src: url("../fonts/tajawal-800.woff2") format("woff2");
  font-weight: 800;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF;
}

/* Metric-compatible stand-in for Tajawal, so the swap cannot move layout.
   Calibrated in the browser, not from a font file: Chrome will not match
   local("Geeza Pro") — macOS keeps its Arabic system faces out of local() —
   so the candidate that actually wins is Tahoma, which also ships on Windows.
   Calibrated on real prose, not on a letter sample: Tahoma's space glyph is
   ~50% wider than Tajawal's, so matching the letters (107.9%) still wrapped
   paragraphs a line early. Measured on the hero lead at 100px — Tajawal 7051px
   vs stand-in 7800px — the ratio that makes running Arabic text occupy the same
   width is size-adjust 97.5%, with Tajawal's own metrics (upem 1000, typo asc
   643 / desc -357 / gap 200) divided by that factor. The original 123.7% came
   from Arial, which never renders here: it left the stand-in ~15% too wide, the
   hero lead wrapped to five lines instead of four, and the hero grid moved when
   Tajawal arrived. Where none of these faces exist (Android, most Linux) the
   platform's own Arabic font renders unadjusted — nothing to calibrate against
   without shipping a second file, which is why the hero also reserves its box. */
@font-face {
  font-family: "Tajawal Fallback";
  src: local("Tahoma"), local("Segoe UI"), local("Noto Sans Arabic"), local("Geeza Pro");
  size-adjust: 97.5%;
  ascent-override: 65.9%;
  descent-override: 36.6%;
  line-gap-override: 20.5%;
}

/* --------------------------------------------------------------------------
   1 · TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Brand — sRGB with wide-gamut upgrades */
  --blue:   #1E60FD;
  --pink:   #FF0054;
  --yellow: #F8CE3A;
  --violet: #6A47F0;

  /* Space (page) ramp — blue-black, never pure black */
  --void-950: #04050A;
  --void-900: #060810;
  --void-850: #090B16;
  --void-800: #0D101E;
  --void-700: #141830;
  --paper:    #F3F0E9;   /* the one light act */
  --paper-2:  #EAE6DB;

  --fg:        #EAECF6;
  --fg-strong: #FFFFFF;
  --muted:     #8E93AD;
  --faint:     #575D78;   /* decoration only — below 4.5:1 on the void ramp */
  /* --blue is a UI colour, not a text colour: at #1E60FD it measures 4.02:1 on
     --void-950. Small text that must be read uses this lifted sibling, which
     clears 4.5:1 on every surface in the ramp (7.2:1 worst case). */
  --blue-ink:  #6C9FFF;

  --line:        rgb(255 255 255 / .08);
  --line-strong: rgb(255 255 255 / .16);

  --glow-blue: 0 0 90px -18px rgb(30 96 253 / .55);

  /* Type */
  --f-display: "Clash Display", "Tajawal", system-ui, sans-serif;
  --f-body: "Satoshi", "Tajawal", system-ui, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;

  --t-hero: clamp(2.7rem, 6.6vw, 6.3rem);
  --t-h2:   clamp(2.1rem, 4.6vw, 4rem);
  --t-h3:   clamp(1.25rem, 1.9vw, 1.6rem);
  --t-lead: clamp(1.06rem, 1.45vw, 1.32rem);
  --t-body: 1.05rem;
  --t-sm:   .92rem;
  --t-mono: .74rem;

  /* Rhythm */
  --sect: clamp(96px, 12vw, 190px);
  --gut:  clamp(20px, 4.5vw, 72px);
  --maxw: 1440px;
  --hdr: 72px;

  /* Motion */
  --snap: cubic-bezier(.83, 0, .17, 1);          /* quarter-turn feel */
  --out:  cubic-bezier(.16, 1, .3, 1);
  --spring: cubic-bezier(.34, 1.45, .5, 1);

  --r-sm: 10px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-xl: 36px;
}

/* Wide-gamut accents where supported */
@supports (color: color(display-p3 0 0 0)) {
  :root {
    --blue:   color(display-p3 .18 .38 1);
    --pink:   color(display-p3 1 .08 .36);
    --yellow: color(display-p3 .98 .81 .27);
  }
}

/* --------------------------------------------------------------------------
   2 · BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
}
html:has(dialog[open]) { overflow: hidden; }

body {
  margin: 0;
  background: var(--void-950);
  color: var(--fg);
  font-family: var(--f-body);
  font-size: var(--t-body);
  line-height: 1.65;
  font-weight: 420;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

h1, h2, h3, h4 { margin: 0; font-family: var(--f-display); color: var(--fg-strong); line-height: 1.02; letter-spacing: -.015em; text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
/* <picture> wrappers must not become the flex/grid item — the <img> stays
   the box every layout rule in this file already targets. */
picture { display: contents; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
::selection { background: var(--blue); color: #fff; }

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

.skip-link {
  position: fixed; inset-block-start: -60px; inset-inline-start: 16px;
  background: var(--blue); color: #fff; padding: 10px 18px; border-radius: 8px;
  z-index: 200; transition: inset-block-start .25s var(--out);
}
.skip-link:focus-visible { inset-block-start: 12px; }

.container { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gut); }

/* Film grain — static texture, normal blending: near-identical look at a
   fraction of the compositing cost (no animation, no blend-mode) */
.grain {
  position: fixed; inset: 0; z-index: 90; pointer-events: none; contain: strict;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.72' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
  opacity: .035;
}
@media (max-width: 899px), (pointer: coarse) {
  .grain { display: none; }
}

/* Scroll progress — CSS scroll-driven where supported (no JS) */
.progress {
  position: fixed; inset-inline: 0; inset-block-start: 0; block-size: 2px; z-index: 130;
  background: linear-gradient(90deg, var(--blue), var(--violet) 52%, var(--pink));
  transform-origin: 0 50%; transform: scaleX(0);
  display: none;
}
@supports (animation-timeline: scroll()) {
  .progress { display: block; animation: progress-fill linear both; animation-timeline: scroll(root); }
  @keyframes progress-fill { to { transform: scaleX(1); } }
}

/* WebGL stage — fixed behind everything */
.stage {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0; transition: opacity .8s var(--out);
}
.stage.is-on { opacity: 1; }
.stage canvas { width: 100%; height: 100%; display: block; }

/* Aurora backwash behind the stage */
.aurora {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(42% 55% at 78% 18%, rgb(30 96 253 / .16), transparent 70%),
    radial-gradient(34% 44% at 12% 82%, rgb(255 0 84 / .09), transparent 72%),
    radial-gradient(30% 38% at 55% 108%, rgb(106 71 240 / .12), transparent 75%);
}

main { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   3 · HUD FURNITURE (labels, rules, section headers)
   -------------------------------------------------------------------------- */
.hud {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--f-mono); font-size: var(--t-mono);
  letter-spacing: .22em; text-transform: uppercase; color: var(--muted);
}
.hud b { white-space: nowrap; }
.hud::before { content: ""; inline-size: 26px; block-size: 1px; background: linear-gradient(90deg, var(--blue), transparent); }
.hud b { color: var(--blue-ink); font-weight: 400; }

.sect { padding-block: var(--sect); position: relative; }
/* Off-screen acts skip paint. Intrinsic size keeps the scrollbar honest. */
.sect, .client-index, .process {
  content-visibility: auto;
  contain-intrinsic-size: auto 800px;
}
/* (deliberately no content-visibility: its estimated placeholder heights
   shift real layout as sections enter view — the jank costs more than the
   paint saving on a page this size) */
.sect-head { max-width: 880px; margin-block-end: clamp(40px, 6vw, 84px); }
.sect-head h2 { font-size: var(--t-h2); font-weight: 570; margin-block-start: 18px; }
.sect-head .lead { margin-block-start: 18px; }

.lead { font-size: var(--t-lead); line-height: 1.55; color: var(--muted); font-weight: 430; }
.lead strong, .lead b { color: var(--fg); font-weight: 560; }

/* Inline highlight: the cube's rarest hue. (Replaces an animated blue→violet
   →pink gradient — the one true slop signal on a page whose colour is
   rationed. Yellow is otherwise only the badge dot and the Friday node.) */
.hi { color: var(--yellow); font-style: normal; }

.hairline { border: 0; block-size: 1px; margin: 0; background: var(--line); }

/* --------------------------------------------------------------------------
   4 · BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: var(--blue);
  position: relative; isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--f-body); font-weight: 560; font-size: 1rem;
  padding: 15px 28px; border-radius: 999px;
  color: #fff; background: var(--btn-bg);
  transition: transform .3s var(--spring), box-shadow .3s var(--out), background-color .3s;
  will-change: transform;
}
.btn .arr { transition: transform .3s var(--spring); display: inline-block; }
.btn:hover .arr, .btn:focus-visible .arr { transform: translateX(5px); }
.btn-primary { box-shadow: 0 10px 34px -12px rgb(30 96 253 / .65), inset 0 1px 0 rgb(255 255 255 / .18); }
.btn-primary:hover { background: #1A56E8; box-shadow: 0 14px 44px -12px rgb(30 96 253 / .85), inset 0 1px 0 rgb(255 255 255 / .2); }
.btn-ghost {
  background: rgb(255 255 255 / .05); color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  backdrop-filter: blur(8px);
}
.btn-ghost:hover { background: rgb(255 255 255 / .09); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .3); }
.btn-lg { padding: 18px 34px; font-size: 1.06rem; }
.btn-sm { padding: 10px 20px; font-size: .92rem; }
.btn-wa { gap: 9px; }
.btn-wa svg { flex: none; }

.link-arrow {
  display: inline-flex; align-items: center; gap: 8px; font-weight: 560; color: var(--fg-strong);
}
.link-arrow .arr { transition: transform .3s var(--spring); }
.link-arrow:hover .arr { transform: translateX(5px); }

/* --------------------------------------------------------------------------
   5 · HEADER + NAV
   -------------------------------------------------------------------------- */
.hdr {
  position: fixed; inset-inline: 0; inset-block-start: 0; z-index: 120;
  transition: transform .45s var(--out);
}
.hdr-in {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  max-width: calc(var(--maxw) + 40px); margin-inline: auto;
  padding: 14px var(--gut);
  transition: background-color .4s, backdrop-filter .4s, box-shadow .4s, padding .4s var(--out);
}
.hdr.is-scrolled .hdr-in {
  /* solid tint, no backdrop-filter — blur behind a sticky header repaints
     on every scrolled frame and is a main source of scroll jank */
  background: rgb(7 9 18 / .94);
  box-shadow: 0 1px 0 var(--line);
  padding-block: 10px;
}
.hdr.is-hidden { transform: translateY(-110%); }

.brand { display: inline-flex; align-items: center; }
.brand img { block-size: 34px; inline-size: auto; }

.nav { display: flex; align-items: center; gap: 4px; }
.nav a {
  position: relative; padding: 9px 14px; padding-inline: 16px 14px; font-size: .95rem; font-weight: 500; color: var(--muted);
  border-radius: 999px; transition: color .25s;
}
.nav a::after {
  content: ""; position: absolute; inset-inline: 14px; inset-block-end: 5px; block-size: 1px;
  background: var(--blue); transform: scaleX(0); transform-origin: 0 50%; transition: transform .3s var(--out);
}
.nav a:hover, .nav a.is-live, .nav-item:is(.is-open, :focus-within) > a { color: var(--fg-strong); }
.nav a:hover::after, .nav a.is-live::after, .nav-item:is(.is-open, :focus-within) > a::after { transform: scaleX(1); }

/* ---- Nav submenus (one-pager) --------------------------------------------
   Same contract as the subpage tree: real links, no script, opens on hover for
   a mouse and :focus-within for a keyboard, closes when focus leaves. The top
   link stays the section anchor — the panel adds the pages that section is
   about. The header is a blur panel over the hero, so the dropdown carries an
   opaque background of its own rather than inheriting that translucency. */
.nav-item { position: relative; display: inline-flex; align-items: center; }
/* No chevron: dropdown affordance is the hover/focus state itself. An
   absolutely-positioned icon kept drifting onto the label across locales
   and widths; removed for good rather than re-geometried. */
/* Hover bridge, no-script path only: there :hover is the whole mechanism and
   the panel is unreachable across the gap without it. one.js defers the close
   on a timer instead, so with script this would be an invisible hit target
   over the page for no gain. */
html:not(.nav-js) .nav-item::after {
  content: ""; position: absolute; inset-inline: -6px; inset-block-start: 100%; block-size: 12px;
}
.nav .sub {
  position: absolute; inset-block-start: calc(100% + 10px); inset-inline-start: -6px;
  z-index: 60; margin: 0; padding: 8px; list-style: none;
  min-inline-size: 238px; display: grid; gap: 2px;
  background: var(--void-900, #0A0D18); border: 1px solid var(--line);
  border-radius: 14px;
  /* inset hairline = blue accent; enter fades+rises 8px, exit fades in place */
  box-shadow: 0 28px 64px -16px rgb(0 0 0 / .8), inset 0 1px 0 rgb(30 96 253 / .4);
  opacity: 0; visibility: hidden;
  transition: opacity .2s var(--out), visibility .2s;
}
/* Enhanced: one.js/main.js owns the open state, because CSS :hover cannot
   close a panel when the page scrolls out from under the pointer — which
   is exactly the bug this replaces. The un-enhanced selectors stay behind
   html:not(.nav-js) so the panels still open without script. */
.nav-item.is-open > .sub,
html:not(.nav-js) .nav-item:hover > .sub,
html:not(.nav-js) .nav-item:focus-within > .sub {
  opacity: 1; visibility: visible; animation: sub-in .2s var(--out);
}
@keyframes sub-in { from { opacity: 0; translate: 0 -8px; } }
.nav .sub li { display: block; }
.nav .sub a {
  display: flex; align-items: center; min-block-size: 44px;
  padding: 0 12px; border-radius: 9px; font-size: .9rem; color: var(--fg);
  transition: background .2s, color .2s;
}
.nav .sub a::after { display: none; }   /* the sliding underline is top-level only */
.nav .sub li:first-child a { border-start-start-radius: 6px; border-start-end-radius: 6px; }
.nav .sub li:last-child a { border-end-start-radius: 6px; border-end-end-radius: 6px; }
.nav .sub a:hover { background: rgb(255 255 255 / .07); color: var(--fg-strong); }
.nav .sub a[aria-current="page"] { background: rgb(255 255 255 / .04); color: var(--blue-ink); font-weight: 560; }
@media (prefers-reduced-motion: reduce) {
  .nav .sub { transition: none !important; animation: none !important; }
  .sheet[open] { animation: none !important; }
}
/* Mobile sheet: the same sub-items, indented and always open — no hover to
   depend on and nothing to tap twice. */
.sheet-sub { display: flex; flex-direction: column; gap: 2px; padding-inline-start: 18px; margin-block: 2px 10px; }
.sheet-sub a { font-size: .95rem; color: var(--muted); padding-block: 8px; }
.sheet-sub a:hover { color: var(--fg-strong); }

.hdr-end { display: flex; align-items: center; gap: 14px; }
.lang {
  font-family: var(--f-mono); font-size: .8rem; color: var(--muted); letter-spacing: .04em;
  padding: 8px 12px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--line);
  transition: color .25s, box-shadow .25s;
}
.lang:hover { color: var(--fg-strong); box-shadow: inset 0 0 0 1px var(--line-strong); }

.burger { display: none; position: relative; inline-size: 44px; block-size: 44px; border-radius: 50%; box-shadow: inset 0 0 0 1px var(--line-strong); }
.burger span {
  position: absolute; inset-inline: 13px; block-size: 1.6px; background: var(--fg);
  transition: transform .35s var(--snap), opacity .25s;
}
.burger span:nth-child(1) { inset-block-start: 17px; }
.burger span:nth-child(2) { inset-block-start: 26px; }

/* Mobile nav dialog */
.sheet {
  inset: 0; inline-size: 100%; block-size: 100dvh; max-inline-size: none; max-block-size: none;
  border: 0; margin: 0; padding: 0; background: rgb(4 5 10 / .92);
  -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); color: var(--fg);
}
.sheet::backdrop { background: transparent; }
.sheet-in { display: flex; flex-direction: column; gap: 6px; padding: calc(var(--hdr) + 24px) 28px 40px; }
.sheet a.big {
  font-family: var(--f-display); font-size: clamp(2rem, 9vw, 3rem); font-weight: 560;
  color: var(--fg-strong); padding-block: 10px; border-block-end: 1px solid var(--line);
  display: flex; justify-content: space-between; align-items: center;
}
.sheet a.big small { font-family: var(--f-mono); font-size: .72rem; color: var(--muted); letter-spacing: .2em; }
.sheet-foot { margin-block-start: 28px; display: flex; flex-wrap: wrap; gap: 12px; }
.sheet-x { position: fixed; inset-block-start: 16px; inset-inline-end: 18px; inline-size: 46px; block-size: 46px; border-radius: 50%; box-shadow: inset 0 0 0 1px var(--line-strong); font-size: 1.5rem; line-height: 1; color: var(--fg); }
.sheet[open] { animation: sheet-in .45s var(--out); }
@keyframes sheet-in { from { opacity: 0; transform: translateY(-3%); } }

/* --------------------------------------------------------------------------
   5b · SIGNATURE MOVES
   Three grammars borrowed from the object the brand is named after. All of it
   is decoration: every rule sits behind prefers-reduced-motion: no-preference,
   and nothing here carries text or meaning.
   -------------------------------------------------------------------------- */

/* THE QUARTER TURN. The cube solves in quarter turns, so that is the hover
   grammar: a 6px tri-colour chip that snaps 90° with the same --snap curve the
   FAQ control already used. */
.nav a, .foot-col a, .link-arrow, .sheet a.big { --turn: 0deg; }
.nav a::before, .foot-col a::before {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 0;
  inline-size: 6px; block-size: 6px; translate: 0 -50%;
  background: conic-gradient(from -45deg, var(--blue) 0 33%, var(--pink) 0 66%, var(--yellow) 0);
  opacity: 0; rotate: var(--turn);
  transition: opacity .25s var(--out), rotate .45s var(--snap);
}
.nav a::before { inset-inline-start: 4px; }
.foot-col a { position: relative; padding-inline-start: 0; transition: color .25s, padding-inline-start .3s var(--snap); }
.foot-col a:hover { padding-inline-start: 14px; }
@media (prefers-reduced-motion: no-preference) {
  .nav a:hover::before, .nav a.is-live::before,
  .nav-item:is(.is-open, :focus-within) > a::before,
  .foot-col a:hover::before { opacity: 1; --turn: 90deg; }
}
/* the FAQ control and the burger take the same quarter turn on hover */
@media (prefers-reduced-motion: no-preference) {
  .faq-list summary { --turn: 0deg; }
  .faq-list summary:hover .pm { rotate: 90deg; }
  .faq-list summary .pm { transition: rotate .45s var(--snap), box-shadow .3s; }
  .burger:hover span:nth-child(1) { transform: translateY(2px) rotate(90deg) scaleX(.55); }
  .burger:hover span:nth-child(2) { transform: translateY(-2px) rotate(90deg) scaleX(.55); }
}

/* THE DATA PULSE. The cube's seams carry pulses of light; on hover a 1px
   tri-colour dash travels the same way along a link's hairline. One rule for
   the nav and the footer, sharing the ::after surface the nav already had. */
.foot-col a::after {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: -3px; block-size: 1px;
  background: var(--line-strong); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .3s var(--out);
}
.foot-col a:hover::after { transform: scaleX(1); }
@media (prefers-reduced-motion: no-preference) {
  .nav a:hover::after, .foot-col a:hover::after {
    background-image: linear-gradient(90deg, var(--blue) 0 18%, var(--pink) 18% 36%, var(--yellow) 36% 54%, transparent 54%);
    background-size: 260% 100%; background-repeat: repeat-x;
    animation: pulse-travel 1.15s linear infinite;
  }
  @keyframes pulse-travel { from { background-position: 0 0; } to { background-position: -260% 0; } }
}

/* --------------------------------------------------------------------------
   6 · HERO
   -------------------------------------------------------------------------- */
.hero { min-block-size: 100svh; display: grid; align-items: center; padding-block: calc(var(--hdr) + 40px) 60px; position: relative; }
/* The reserved hero box. The hero is exactly 100svh and centres this grid, so
   any change in the grid's own height moves it — which is how a late webfont
   swap shows up as layout shift (measured as the culprit on /ar/). The
   reservation is viewport-derived, comfortably above the settled content height
   at every breakpoint (610-653px measured at 390-1920px wide) and always below
   the hero's own height, so the box is stable whichever face renders first. */
.hero-grid {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, .78fr); gap: 36px;
  align-items: center; align-content: center;
  min-block-size: min(76svh, 700px);
}

.hero-copy { max-width: 760px; }
.hero-badge {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--f-mono); font-size: .76rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--fg); padding: 9px 16px; border-radius: 999px;
  background: rgb(255 255 255 / .04); box-shadow: inset 0 0 0 1px var(--line);
  margin-block-end: 26px;
}
.hero-badge .dot { inline-size: 7px; block-size: 7px; border-radius: 50%; background: var(--yellow); box-shadow: 0 0 12px 1px rgb(248 206 58 / .8); }
@media (prefers-reduced-motion: no-preference) {
  .hero-badge .dot { animation: pulse 2.2s ease-in-out infinite; }
  /* One shimmer across the eyebrow, once, on arrival. Deliberately a
     pseudo-element sweep rather than background-clip: clipping the text would
     mean color: transparent, and a paint that never finishes would leave the
     line unreadable. This keeps the real ink at full contrast throughout. */
  html.js .hero-badge { position: relative; overflow: hidden; }
  html.js .hero-badge::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(100deg, transparent 38%, rgb(255 255 255 / .16) 50%, transparent 62%);
    translate: -100% 0; animation: badge-sweep 2.2s var(--out) .7s 1 both;
  }
  @keyframes badge-sweep { to { translate: 100% 0; } }
  @keyframes pulse { 50% { opacity: .35; } }
}

.hero h1 {
  font-size: var(--t-hero); font-weight: 590; letter-spacing: -.025em; line-height: .98;
  margin-block-end: 26px;
}
.hero h1 .line { display: block; overflow: clip; padding-block-end: .06em; margin-block-end: -.06em; }
.hero h1 .ln { display: block; }
.hero .lead { max-width: 560px; }

.hero-cta { display: flex; flex-wrap: wrap; gap: 14px; margin-block-start: 38px; }

/* Tertiary beat: what the primary CTA costs you, then the work link */
.hero-tert { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 24px; margin-block-start: 24px; }
.hero-note { font-family: var(--f-mono); font-size: var(--t-mono); letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.hero-tert .link-arrow { font-size: .95rem; font-weight: 540; color: var(--muted); transition: color .25s; }
.hero-tert .link-arrow:hover { color: var(--fg-strong); }

/* Right column — reserved for the WebGL cube; fallback art lives here */
.hero-stage { position: relative; min-block-size: 0; display: grid; place-items: center; }
/* The living cube is the hero's visual on EVERY path, from first paint.
   It used to be hidden by `html.js:not(.no-gl)` — the moment one.js added
   .js, which is synchronous at parse. But .no-gl only ever arrives if the
   WebGL path is ruled out, and the WebGL path itself is post-load +
   intersection + requestIdleCallback + a ~212KB dynamic import. Between
   those two moments — seconds, or forever if the main thread never idles —
   the hero's right column was empty. Worse: .stage is a fixed full-page
   backdrop, not this column, so even a successful boot never filled it.
   So the SVG now yields only to a stage that is genuinely rendering:
   cube.js adds .is-on after its first frame, and nothing else does. */
.gl-fallback { position: absolute; inset: 0; display: grid; place-items: center; }
html:has(.stage.is-on) .gl-fallback { display: none; }
.gl-fallback svg,
.gl-fallback img { inline-size: min(78%, 344px); block-size: auto; filter: drop-shadow(0 30px 60px rgb(30 96 253 / .35)); }
.hero-stage .gl-fallback { position: relative; inset: auto; }
.hero-stage .gl-fallback img { inline-size: 100%; max-inline-size: 360px; }

/* Every animated property here is transform, opacity or stroke-dashoffset —
   nothing that touches layout. Gated on html.js so ?static=1 (which turns off
   MOTION in one.js and therefore never adds .js) composes the same still
   frame that prefers-reduced-motion gets. */
.lc-seams path { stroke-dashoffset: 0; }
.lc-o2 { rotate: 132deg; }
.lc-o3 { rotate: 248deg; }
.lc-o1, .lc-o2, .lc-o3 { transform-box: view-box; transform-origin: 120px 120px; }
.lc-inner { transform-box: view-box; transform-origin: 120px 120px; }
.lc-grid { opacity: .5; }
@media (prefers-reduced-motion: no-preference) {
  html.js .lc-float { animation: lc-float 9s ease-in-out infinite; }
  /* the inner cube counter-turns inside the fixed outer frame: a gyroscope
     read, not a spinning logo — the outer silhouette never moves */
  html.js .lc-inner { animation: lc-spin 26s linear infinite reverse; }
  /* one short dash travelling each of the three vertical seams; the paths are
     70 units long, so 10 + 60 is exactly one lap */
  html.js .lc-s1 { animation: lc-seam 3.4s linear infinite; }
  html.js .lc-s2 { animation: lc-seam 4.1s linear infinite .5s; }
  html.js .lc-s3 { animation: lc-seam 4.8s linear infinite 1.1s; }
  html.js .lc-o1 { animation: lc-orb 11s linear infinite; }
  html.js .lc-o2 { animation: lc-orb 14s linear infinite -5s; }
  html.js .lc-o3 { animation: lc-orb 17s linear infinite -9s; }
  @keyframes lc-float { 50% { transform: translateY(-6px); } }
  @keyframes lc-spin { to { rotate: 360deg; } }
  @keyframes lc-seam { to { stroke-dashoffset: -70; } }
  @keyframes lc-orb { to { rotate: 360deg; } }
  /* Ambient: the aurora breathes and the floor grid drifts. Both are inside
     the SVG, both are transform/opacity, and both start at t=0 rather than
     waiting on anything. */
  html.js .lc-aurora { animation: lc-breathe 11s ease-in-out infinite; }
  html.js .lc-grid { animation: lc-drift 24s linear infinite; }
  @keyframes lc-breathe { 50% { scale: 1.06; opacity: .78; } }
  @keyframes lc-drift { to { translate: 26px 0; } }
}
.lc-aurora { transform-box: view-box; transform-origin: 120px 114px; opacity: 1; }
.lc-grid { transform-box: view-box; }

/* (scroll hint removed — it collided with the proof chips; the progress
   bar and section rail already communicate scrollability) */

/* --------------------------------------------------------------------------
   6b · READOUT (proof, below the fold)
   An instrument-panel strip, not a chip row: mono keys, Clash Display values
   in tabular-nums, hairline cells. The H1 owns the hero column instead.
   -------------------------------------------------------------------------- */
.readout { border-block-start: 1px solid var(--line); }
.readout-in {
  display: grid; grid-template-columns: repeat(3, 1fr);
  padding-block: clamp(22px, 3vw, 34px);
}
.ro {
  display: grid; align-content: start; gap: 6px;
  padding-inline-end: clamp(16px, 3vw, 44px);
}
.ro + .ro { border-inline-start: 1px solid var(--line); padding-inline-start: clamp(20px, 3.5vw, 52px); }
.ro-k { font-family: var(--f-mono); font-size: var(--t-mono); letter-spacing: .22em; text-transform: uppercase; color: var(--muted); }
/* live indicator on the one cell that is a live third-party number */
.ro-dot {
  display: inline-block; inline-size: 6px; block-size: 6px; border-radius: 50%;
  background: var(--yellow); margin-inline-end: 8px; vertical-align: middle;
  box-shadow: 0 0 10px 1px rgb(248 206 58 / .7);
}
@media (prefers-reduced-motion: no-preference) {
  html.js .ro-dot { animation: pulse 2.6s ease-in-out infinite; }
}
.ro-v {
  font-family: var(--f-display); font-weight: 590; font-size: clamp(1.6rem, 2.6vw, 2.2rem);
  line-height: 1; letter-spacing: -.02em; color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
}
.ro-u { font-size: .5em; font-weight: 500; color: var(--blue-ink); margin-inline-start: .3em; letter-spacing: 0; }
.ro-n { font-size: .84rem; color: var(--muted); }
a.ro .arr { display: inline-block; color: var(--faint); transition: transform .3s var(--spring), color .3s; }
a.ro:hover .ro-v { color: var(--yellow); }
a.ro:hover .arr { transform: translateX(4px); color: var(--fg); }

/* --------------------------------------------------------------------------
   7 · CLIENT INDEX — a structured register, not a conveyor belt
   The marquee moved logos past you and told you nothing: no order, no reason
   two names sat next to each other, and half the wall unreadable at any
   instant. This is a dossier index instead — named rows, each with its own
   count and a caption line that answers "and what did you do for them" as you
   move across it.

   Two rows of eight, not four groups of assorted size: twenty-six logos read
   as an inventory, the strongest sixteen read as a client list. The ten that
   left are still on /work and the portfolio pages. Eight halves to four then
   two, so no viewport gets a ragged last line.
   -------------------------------------------------------------------------- */
.client-index { padding-block: clamp(46px, 6vw, 84px); border-block: 1px solid var(--line); }
.client-index .marquee-label { display: flex; justify-content: center; text-align: center; margin-block-end: 10px; }
.mq-chip {
  display: block; text-align: center; margin-block-end: clamp(30px, 4vw, 52px);
  font-family: var(--f-mono); font-size: var(--t-mono); letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted);
}
[dir="rtl"] .mq-chip { letter-spacing: 0; font-family: var(--f-body); text-transform: none; }
.ci-groups { display: grid; gap: clamp(30px, 3.6vw, 46px); }
.ci-head { display: flex; align-items: center; gap: 14px; block-size: 24px; margin-block-end: 6px; }
.ci-k {
  font-family: var(--f-mono); font-size: var(--t-mono); letter-spacing: .2em;
  text-transform: uppercase; color: var(--fg-strong); white-space: nowrap;
}
[dir="rtl"] .ci-k { letter-spacing: 0; font-family: var(--f-body); text-transform: none; font-weight: 700; }
.ci-rule { flex: 1; block-size: 1px; background: var(--line); }
.ci-n { font-family: var(--f-mono); font-size: var(--t-mono); color: var(--muted); font-variant-numeric: tabular-nums; }
/* The caption row is reserved, always — the swap must never move the grid. */
.ci-cap { min-block-size: 1.5em; margin-block-end: 16px; font-size: .88rem; color: var(--muted); transition: color .2s var(--out); }
.ci-cap.swap { color: var(--fg-strong); }
/* Fixed counts, not auto-fill: auto-fill picks 9 at 1440 and orphans the
   ninth logo on a line of its own. A row of eight has to stay a row of eight. */
.ci-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px; list-style: none; margin: 0; padding: 0;
}
@media (min-width: 560px) { .ci-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* Per-group columns at desktop: global has 9 logos, regional 7 — each row fills
   exactly, no orphan wrap. Below 560 three columns keep 9 a perfect 3×3. */
@media (min-width: 900px) {
  .ci-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 10px; }
  .ci-grid--9 { grid-template-columns: repeat(9, minmax(0, 1fr)); }
  .ci-grid--7 { grid-template-columns: repeat(7, minmax(0, 1fr)); }
}
.ci-cell {
  display: grid; place-items: center; min-inline-size: 0; min-block-size: 76px;
  padding: 10px 12px; overflow: hidden; border-radius: 10px; cursor: default;
  transition: background .25s;
}
.ci-cell img {
  max-inline-size: 100%; max-block-size: 100%; inline-size: auto;
  block-size: clamp(20px, 2.2vw, 27px); object-fit: contain; object-position: center;
  filter: grayscale(1) brightness(3.2) contrast(.72);
  opacity: .5; transition: opacity .25s, filter .25s, scale .25s var(--out);
  transform-origin: center;
}
/* One height for every mark locks the wrong thing: a 1:1 mark beside an 11:1
   wordmark then carries a quarter of the weight. Area is what should match, so
   the squarer a mark is the more height it gets back — two tiers brings the
   wall from 2.5× of itself to ~1.7×. Widths stay capped. */
.ci-cell img[src*="wizeline"], .ci-cell img[src*="mc.svg"] {        /* ~1–1.4:1  */
  block-size: clamp(28px, 3.2vw, 40px);
}
.ci-cell img[src*="snb"], .ci-cell img[src*="hajj"] {               /* ~2:1  */
  block-size: clamp(22px, 2.6vw, 33px);
}
.ci-word {
  font-family: var(--f-display); font-weight: 600; font-size: clamp(.9rem, 1.3vw, 1.06rem);
  color: var(--fg); opacity: .5; transition: opacity .25s, scale .25s var(--out);
  transform-origin: center;
}
.ci-cell:hover, .ci-cell:focus-visible { background: rgb(255 255 255 / .04); }
.ci-cell:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.ci-cell:is(:hover, :focus-visible) img { opacity: 1; filter: grayscale(0) brightness(1); }
.ci-cell:is(:hover, :focus-visible) .ci-word { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .ci-cell:is(:hover, :focus-visible) img,
  .ci-cell:is(:hover, :focus-visible) .ci-word { scale: 1.04; }
}
/* The caption swap lives in one.js now: the hovered/focused cell's data-what
   writes into the group's single reserved caption slot, so a description can
   never overlap the default line the way the old ::after overlay did. */
@media (prefers-reduced-motion: reduce) {
  .ci-cap, .ci-cell, .ci-cell img { transition: none; }
}

/* --------------------------------------------------------------------------
   8 · SPRINT STRIP (idea → prototype in days)
   -------------------------------------------------------------------------- */
.sprint { position: relative; }
.sprint-card {
  position: relative; overflow: clip;
  border-radius: var(--r-xl);
  background: linear-gradient(140deg, var(--void-800), var(--void-900) 60%);
  box-shadow: inset 0 0 0 1px var(--line), 0 40px 90px -50px rgb(0 0 0 / .8);
  padding: clamp(30px, 5vw, 64px);
}
.sprint-card::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  background:
    radial-gradient(52% 70% at 12% 0%, rgb(30 96 253 / .16), transparent 65%),
    radial-gradient(40% 60% at 95% 100%, rgb(255 0 84 / .1), transparent 70%);
}
.sprint-grid { position: relative; display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: clamp(28px, 4vw, 64px); align-items: center; }
.sprint h2 { font-size: clamp(1.9rem, 3.4vw, 3rem); font-weight: 570; margin-block: 16px 14px; }
.sprint p.lead { font-size: 1.08rem; }
.sprint-grid .hero-cta { margin-block-start: 28px; }

.week { display: grid; gap: 0; position: relative; }
.day {
  display: grid; grid-template-columns: 84px 1fr; gap: 18px; align-items: start;
  padding-block: 13px; position: relative;
}
.day + .day { border-block-start: 1px dashed rgb(255 255 255 / .07); }
.day .d {
  font-family: var(--f-mono); font-size: .72rem; letter-spacing: .18em; color: var(--muted);
  padding-block-start: 4px; text-transform: uppercase;
}
.day .t { font-weight: 560; color: var(--fg-strong); }
.day .s { display: block; font-size: .9rem; color: var(--muted); font-weight: 420; margin-block-start: 2px; }
.day.fri .t { color: var(--yellow); }
.day .node {
  position: absolute; inset-inline-start: 62px; inset-block-start: 20px;
  inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: var(--blue); box-shadow: 0 0 0 4px rgb(30 96 253 / .15);
}
.day.fri .node { background: var(--yellow); box-shadow: 0 0 0 4px rgb(248 206 58 / .18), 0 0 18px rgb(248 206 58 / .8); }

/* --------------------------------------------------------------------------
   9 · SERVICES
   -------------------------------------------------------------------------- */
.svc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
.svc {
  position: relative; overflow: clip; border-radius: var(--r-lg);
  background: rgb(255 255 255 / .025);
  box-shadow: inset 0 0 0 1px var(--line);
  padding: clamp(26px, 3.4vw, 46px);
  transition: transform .45s var(--out), box-shadow .45s var(--out), background-color .45s;
}
.svc:hover { transform: translateY(-6px); background: rgb(255 255 255 / .04); box-shadow: inset 0 0 0 1px var(--line-strong), 0 30px 70px -40px rgb(0 0 0 / .9); }
.svc::after {   /* pointer spotlight */
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0; transition: opacity .4s;
  background: radial-gradient(420px 320px at var(--mx, 50%) var(--my, 0%), rgb(30 96 253 / .1), transparent 65%);
}
.svc:hover::after { opacity: 1; }
.svc .no {
  font-family: var(--f-mono); font-size: .76rem; letter-spacing: .22em; color: var(--muted);
}
.svc h3 { font-size: clamp(1.5rem, 2.3vw, 2rem); font-weight: 570; margin-block: 14px 12px; }
.svc p { color: var(--muted); max-width: 52ch; }
.svc-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-block-start: 22px; }
/* Same as main.css .tag: the ring was the faintest line token and there was no
   fill at all, so these read as bare text rather than chips. */
.tag {
  font-family: var(--f-mono); font-size: .72rem; letter-spacing: .06em; color: var(--muted);
  padding: 7px 12px; border-radius: 999px;
  background: rgb(255 255 255 / .04); box-shadow: inset 0 0 0 1px var(--line-strong);
}
.svc .link-arrow { margin-block-start: 26px; }
.svc-consult {
  grid-column: 1 / -1; display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: center;
  border-radius: var(--r-lg); padding: clamp(24px, 3vw, 38px) clamp(26px, 3.4vw, 46px);
  background: linear-gradient(120deg, rgb(30 96 253 / .12), rgb(106 71 240 / .08) 55%, rgb(255 0 84 / .07));
  box-shadow: inset 0 0 0 1px rgb(94 133 255 / .25);
}
.svc-consult h3 { font-size: 1.35rem; font-weight: 570; }
.svc-consult p { color: var(--muted); margin-block-start: 6px; max-width: 60ch; }

/* Maturity path — the sprint week's grammar (dashed hairline + node dots)
   applied to the tiers, so they read as a route through the work rather than
   a SaaS pricing grid. No boxes, no "most common" highlight card. */
.path {
  position: relative; display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(22px, 3vw, 48px); margin-block-start: 60px; padding-block-start: 34px;
}
.path::before {
  content: ""; position: absolute; inset-block-start: 5px; inset-inline: 0;
  border-block-start: 1px dashed rgb(255 255 255 / .1);
}
.tier { position: relative; }
.tier .node {
  position: absolute; inset-block-start: -33px; inset-inline-start: 0;
  inline-size: 9px; block-size: 9px; border-radius: 50%;
  background: var(--blue); box-shadow: 0 0 0 4px rgb(30 96 253 / .15);
}
.tier.mid .node { background: var(--yellow); box-shadow: 0 0 0 4px rgb(248 206 58 / .18), 0 0 18px rgb(248 206 58 / .7); }
.tier .hud { margin-block-end: 14px; }
.tier h4 { font-size: 1.2rem; font-weight: 570; }
.tier .tier-lead { font-size: .93rem; color: var(--muted); margin-block: 8px 16px; }
.tier ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 9px; }
.tier li { display: flex; gap: 10px; font-size: .93rem; color: var(--fg); }
.tier li::before { content: "→"; color: var(--blue); flex: none; }
.tier-cta { margin-block-start: 22px; font-size: .95rem; color: var(--fg); }
.tier-cta:hover { color: var(--fg-strong); }

/* --------------------------------------------------------------------------
   10 · PROCESS (pinned act)
   -------------------------------------------------------------------------- */
.process { position: relative; }
/* Not pinned any more — an ordinary section with ordinary padding */
.process-stage { padding-block: var(--sect); }
.process-grid { display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); gap: 40px; align-items: stretch; }
.process .sect-head { margin-block-end: 34px; }
.steps { display: grid; gap: 0; counter-reset: step; position: relative; }
/* progress rail behind the number chips — fills as the act plays */
.rail-line {
  position: absolute; inset-block: 30px; inset-inline-start: 21px; inline-size: 2px; z-index: 0;
  background: var(--line); border-radius: 2px;
}
.rail-line i {
  position: absolute; inset: 0; transform: scaleY(0); transform-origin: top;
  background: linear-gradient(180deg, var(--blue), var(--violet));
  box-shadow: 0 0 14px rgb(30 96 253 / .65); border-radius: inherit;
}
html:not(.js) .rail-line { display: none; }
/* The act powers up through colour, the number chip, the nudge and the rail —
   not through opacity. The old .16/.45 ramp put step copy at 1.2:1 and 2.1:1,
   and with JS disabled nothing ever became live, so the whole act read as
   greyed-out placeholder text. Every state is legible now. */
.step {
  position: relative; padding-block: 22px; padding-inline: 66px 0; opacity: .85;
  transition: opacity .45s var(--out), transform .45s var(--out);
}
.step + .step { border-block-start: 1px solid var(--line); }
.step.is-past { opacity: .92; }
.step.is-live { opacity: 1; transform: translateX(8px); }
.step h3 { color: var(--muted); transition: color .45s var(--out); }
.step.is-past h3 { color: var(--fg); }
.step.is-live h3 { color: var(--fg-strong); }
.step.is-past .k {
  color: #9db4ff; background: #111c3a;
  box-shadow: inset 0 0 0 1.5px rgb(94 133 255 / .5);
}
.step .k {
  position: absolute; inset-inline-start: 0; inset-block-start: 20px; z-index: 1;
  inline-size: 44px; block-size: 44px; border-radius: 12px;
  display: grid; place-items: center;
  font-family: var(--f-mono); font-size: .8rem; color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: color .35s, box-shadow .35s, background-color .35s, scale .35s var(--spring);
  /* opaque so the glowing progress rail passes behind, not through */
  background: #0a0c17;
}
/* live chip: solid fill + lift (the old quarter-turn belonged to the Rubik
   metaphor and read as a glitch mid-transition) */
.step.is-live .k { color: #fff; background: var(--blue); box-shadow: 0 8px 26px -8px rgb(30 96 253 / .8); scale: 1.08; }
.step .k i { font-style: normal; }
.step h3 { font-size: clamp(1.3rem, 2vw, 1.7rem); font-weight: 570; }
.step p { color: var(--muted); margin-block-start: 6px; max-width: 52ch; }
.process-stage { position: relative; min-block-size: min(52vw, 520px); }
.process-stage .gl-fallback {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center; padding: 6%;
}
.process-stage .gl-fallback svg,
.process-stage .gl-fallback img {
  inline-size: 400px; max-inline-size: 84%; block-size: auto;
}
.process-stage .anim-layer {
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center; padding: 6%;
  pointer-events: none;
}
.process-stage .anim-layer svg {
  inline-size: 400px; max-inline-size: 84%; block-size: auto;
}
.process-stage .anim-layer.on { opacity: 1; }
html:not(.js) .process-stage .anim-layer,
html:not(.js) .process-stage .process-note,
html:not(.js) .process-stage .process-count { display: none; }
/* Giant step counter — the act's clearest progress signal. It sticks near
   the top of the viewport while the steps scroll past, which keeps it well
   clear of the WebGL cube (which sits vertically centred). */
.process-count {
  position: sticky; inset-block-start: calc(var(--hdr) + 34px);
  display: flex; align-items: baseline; justify-content: flex-end; gap: 10px;
  font-family: var(--f-display); padding-inline-end: 4%;
}
.process-count b {
  display: inline-block; font-size: clamp(3rem, 5vw, 4.6rem); font-weight: 620;
  letter-spacing: -.02em; line-height: 1; color: var(--fg-strong);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 34px rgb(30 96 253 / .5), 0 0 90px rgb(4 5 10 / .9);
}
.process-count span {
  font-family: var(--f-mono); font-size: .9rem; letter-spacing: .22em; color: var(--muted);
}
.process-count.is-flip b { animation: count-flip .42s var(--out); }
@keyframes count-flip { from { opacity: 0; transform: translateY(38%); } }
html:not(.js) .process-count { display: none; }
.process-note {
  position: absolute; inset-block-end: 6%; inset-inline: 0; text-align: center;
  font-family: var(--f-mono); font-size: .72rem; letter-spacing: .26em; text-transform: uppercase; color: var(--muted);
}
/* Without WebGL the cube column is dead weight — let the steps breathe */
html.no-gl .process-stage .anim-layer,
html.no-gl .process-stage .process-note,
html.no-gl .process-stage .process-count { display: none; }
html.no-gl .process-grid { grid-template-columns: minmax(0, 1fr); max-width: 880px; }

/* --------------------------------------------------------------------------
   11 · WORK (the paper act)
   -------------------------------------------------------------------------- */
.work { background: var(--paper); color: #14151E; border-radius: clamp(24px, 4vw, 56px); position: relative; z-index: 2; }
/* The dark acts have film grain; without it the paper act reads as SaaS white
   rather than archival stock. Same feTurbulence, sepia-tinted and half as loud. */
.work::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none; border-radius: inherit;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='p'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.72' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23p)' opacity='.5'/%3E%3C/svg%3E");
  opacity: .04; mix-blend-mode: multiply; filter: sepia(1);
}
.work > * { position: relative; z-index: 1; }
.work .hud { color: #6B6455; }
.work .hud b { color: #B0331E; }
.work .hud::before { background: linear-gradient(90deg, #B0331E, transparent); }
.work h2, .work h3 { color: #0F1017; }
.work .lead { color: #57544B; }

.case-rows { display: grid; gap: clamp(18px, 2.4vw, 28px); }
.case {
  display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: clamp(22px, 3.5vw, 56px); align-items: center;
  background: #FFFDF8; border-radius: var(--r-lg);
  box-shadow: 0 1px 0 rgb(20 21 30 / .06), 0 24px 60px -42px rgb(20 21 30 / .35);
  padding: clamp(20px, 2.6vw, 34px);
  transition: transform .45s var(--out), box-shadow .45s var(--out);
}
.case:hover { transform: translateY(-5px); box-shadow: 0 1px 0 rgb(20 21 30 / .06), 0 40px 90px -46px rgb(20 21 30 / .5); }
.case:nth-child(even) .case-media { order: 1; }
.case-media { overflow: clip; border-radius: var(--r-md); aspect-ratio: 16 / 10.6; position: relative; background: #E8E4D8; }
.case-media img { inline-size: 100%; block-size: 100%; object-fit: cover; transition: transform .8s var(--out), filter .8s; }
.case:hover .case-media img { transform: scale(1.045); }
.case-body { padding-block: 8px; }
/* Dossier spine: every case gets a file number on a ruler, the way a physical
   project folder would be labelled. Static text, logical properties, mirrors. */
.case-spine {
  display: flex; align-items: center; gap: 12px;
  font-family: var(--f-mono); font-size: .66rem; letter-spacing: .2em; text-transform: uppercase;
  color: #6B6455; margin-block-end: 12px;
}
.case-spine::after {
  content: ""; flex: 1; block-size: 7px;
  background-image: repeating-linear-gradient(90deg, rgb(20 21 30 / .28) 0 1px, transparent 1px 9px);
  background-position: 0 100%; background-repeat: repeat-x; background-size: 100% 7px;
  border-block-end: 1px solid rgb(20 21 30 / .18);
}
.case-body .hud { margin-block-end: 14px; }
.case h3 { font-size: clamp(1.5rem, 2.6vw, 2.2rem); font-weight: 580; letter-spacing: -.01em; }
.case p { color: #57544B; margin-block-start: 10px; max-width: 54ch; }
.case-stat { display: flex; gap: 30px; margin-block-start: 20px; }
.case-stat div b { display: block; font-family: var(--f-display); font-weight: 620; font-size: clamp(1.4rem, 2.4vw, 2rem); color: #0F1017; letter-spacing: -.01em; }
.case-stat div span { font-size: .84rem; color: #6B6455; }
.case .link-arrow { margin-block-start: 22px; color: #B0331E; }
.work-more { margin-block-start: 40px; display: flex; justify-content: center; }
.work-more .btn-ghost { color: #14151E; box-shadow: inset 0 0 0 1px rgb(20 21 30 / .22); background: transparent; backdrop-filter: none; }
.work-more .btn-ghost:hover { background: rgb(20 21 30 / .05); box-shadow: inset 0 0 0 1px rgb(20 21 30 / .4); }

/* --------------------------------------------------------------------------
   12 · STATS
   -------------------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; text-align: start; }
.stat { padding: 26px 0 0; border-block-start: 1px solid var(--line-strong); }
.stat b {
  display: block; font-family: var(--f-display); font-weight: 620; letter-spacing: -.02em;
  font-size: clamp(2.6rem, 5.4vw, 4.6rem); color: var(--fg-strong); line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat b .u { color: var(--blue-ink); }
.stat > span { display: block; margin-block-start: 10px; color: var(--muted); font-size: .95rem; }

/* --------------------------------------------------------------------------
   13 · TESTIMONIALS
   -------------------------------------------------------------------------- */
.proof-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px; margin-block-start: var(--sect); }
.proof-head .sect-head { margin-block-end: 0; }
.clutch {
  display: inline-flex; align-items: center; gap: 10px; padding: 12px 18px; border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--line-strong); font-size: .92rem; color: var(--fg);
  transition: box-shadow .3s, background-color .3s;
}
.clutch:hover { background: rgb(255 255 255 / .05); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .32); }
.clutch b { font-family: var(--f-display); font-weight: 620; font-size: 1.06rem; font-variant-numeric: tabular-nums; color: var(--fg-strong); }

.t-rail-wrap { position: relative; margin-block-start: 48px; }
.t-rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: min(430px, 82vw);
  gap: 18px; overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; padding-block-end: 18px;
  scrollbar-width: none;
  mask: linear-gradient(90deg, #000 92%, transparent);
  -webkit-mask: linear-gradient(90deg, #000 92%, transparent);
}
.t-rail::-webkit-scrollbar { display: none; }
.t-card {
  scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 22px; justify-content: space-between;
  border-radius: var(--r-lg); padding: 30px;
  background: rgb(255 255 255 / .025); box-shadow: inset 0 0 0 1px var(--line);
}
.t-card blockquote { margin: 0; font-size: 1.08rem; line-height: 1.6; color: var(--fg); font-weight: 450; }
.t-card blockquote::before { content: "“"; display: block; font-family: var(--f-display); font-size: 3rem; line-height: .6; color: var(--blue); margin-block-end: 14px; }
.t-card.mcd blockquote::before { color: var(--yellow); }
.t-who { display: flex; align-items: center; gap: 13px; }
.t-who img { inline-size: 44px; block-size: 44px; border-radius: 50%; object-fit: cover; }
/* the McDonald's card carries a logo, not a portrait */
.t-card.mcd .t-who img { border-radius: 10px; object-fit: contain; background: rgb(255 255 255 / .06); padding: 8px; }
.t-who .n { font-weight: 620; color: var(--fg-strong); font-size: .96rem; }
.t-who .r { font-size: .82rem; color: var(--muted); }
.t-who a.li { margin-inline-start: auto; color: var(--muted); transition: color .25s; }
.t-who a.li:hover { color: #fff; }
.t-nav { display: flex; gap: 10px; justify-content: flex-end; margin-block-start: 10px; }
.t-btn {
  inline-size: 46px; block-size: 46px; border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--fg);
  display: grid; place-items: center; transition: background-color .25s, box-shadow .25s;
}
.t-btn:hover { background: rgb(255 255 255 / .07); }
.t-btn:disabled { opacity: .3; cursor: default; }

/* --------------------------------------------------------------------------
   14 · GUARANTEES
   -------------------------------------------------------------------------- */
.guar { position: relative; }
/* A ledger, not an icon grid: four numbered terms, mono numerals, hairlines.
   The other four risk-removers live in the FAQ, where they are read. */
.ledger { display: grid; grid-template-columns: 1fr 1fr; border-block-start: 1px solid var(--line-strong); }
.led {
  display: grid; grid-template-columns: auto 1fr; gap: 20px;
  padding-block: 26px 30px; padding-inline-end: clamp(16px, 3vw, 44px);
  border-block-end: 1px solid var(--line);
}
.led:nth-child(2n) { border-inline-start: 1px solid var(--line); padding-inline-start: clamp(24px, 4vw, 56px); }
.led-n {
  font-family: var(--f-mono); font-size: var(--t-mono); letter-spacing: .22em;
  color: var(--blue-ink); font-variant-numeric: tabular-nums; padding-block-start: 5px;
}
.led h3 { font-family: var(--f-body); font-size: 1.06rem; font-weight: 590; color: var(--fg-strong); }
.led p { font-size: .9rem; color: var(--muted); margin-block-start: 7px; line-height: 1.55; max-width: 48ch; }

/* --------------------------------------------------------------------------
   15 · PLANNER (lead magnet)
   -------------------------------------------------------------------------- */
.planner-shell {
  border-radius: var(--r-xl); overflow: clip; position: relative;
  background:
    radial-gradient(60% 90% at 85% -10%, rgb(30 96 253 / .16), transparent 60%),
    radial-gradient(50% 70% at 0% 110%, rgb(255 0 84 / .1), transparent 65%),
    var(--void-900);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  padding: clamp(28px, 4.5vw, 60px);
}
.planner-grid { display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); gap: clamp(30px, 4.5vw, 70px); }
.planner-shell h2 { font-size: clamp(1.9rem, 3.2vw, 2.8rem); margin-block-start: 16px; }
.planner-shell .lead { margin-block-start: 14px; }
.planner-q { margin-block-start: 34px; }
.planner-q .q {
  font-family: var(--f-display); font-weight: 570; font-size: 1.3rem; color: var(--fg-strong);
  margin-block-end: 16px; display: flex; gap: 12px; align-items: baseline;
}
.planner-q .q .qn { font-family: var(--f-mono); font-size: .74rem; color: var(--blue-ink); letter-spacing: .2em; }
.opts { display: flex; flex-wrap: wrap; gap: 10px; }
.opt { position: relative; }
.opt input { position: absolute; opacity: 0; inset: 0; cursor: pointer; }
.opt label, .opt .lbl {
  display: inline-flex; padding: 11px 18px; border-radius: 999px; cursor: pointer;
  font-size: .95rem; font-weight: 500; color: var(--muted);
  box-shadow: inset 0 0 0 1px var(--line); background: rgb(255 255 255 / .02);
  transition: color .25s, box-shadow .25s, background-color .25s, transform .25s var(--spring);
}
.opt:hover .lbl { color: var(--fg); box-shadow: inset 0 0 0 1px var(--line-strong); }
.opt input:checked + .lbl {
  color: #fff; background: rgb(30 96 253 / .18);
  box-shadow: inset 0 0 0 1.5px var(--blue); transform: scale(1.03);
}
.opt input:focus-visible + .lbl { outline: 2px solid var(--blue); outline-offset: 2px; }

.plan-out { position: relative; border-radius: var(--r-lg); background: rgb(255 255 255 / .03); box-shadow: inset 0 0 0 1px var(--line-strong); padding: clamp(24px, 3vw, 40px); display: grid; align-content: start; gap: 20px; min-block-size: 420px; }
.plan-out .hud { margin-block-end: -6px; }
/* The column ships showing a real worked plan rather than an empty 420px box,
   so it has to say so until the visitor's own answers replace it. renderPlanner
   sets .is-ready on the third answer, which is also when every field below is
   overwritten — one flag, no second copy of the markup to keep in sync. */
.plan-eg {
  font-family: var(--f-mono); font-size: .62rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--yellow); padding: 3px 8px; border-radius: 999px;
  background: rgb(248 206 58 / .1); box-shadow: inset 0 0 0 1px rgb(248 206 58 / .34);
}
[dir="rtl"] .plan-eg { letter-spacing: 0; font-family: var(--f-body); text-transform: none; }
.plan-out.is-ready .plan-eg { display: none; }
.plan-name { font-family: var(--f-display); font-weight: 590; font-size: clamp(1.7rem, 2.6vw, 2.4rem); color: var(--fg-strong); }
.plan-tl { display: grid; gap: 8px; }
.plan-tl .bar { position: relative; block-size: 8px; border-radius: 99px; background: rgb(255 255 255 / .07); overflow: clip; }
.plan-tl .bar i {
  position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: var(--w, 30%);
  border-radius: inherit; background: linear-gradient(90deg, var(--blue), var(--violet));
  transition: inline-size .8s var(--out);
}
.plan-tl .lab { display: flex; justify-content: space-between; font-family: var(--f-mono); font-size: .7rem; letter-spacing: .12em; color: var(--muted); text-transform: uppercase; }
.plan-team { display: flex; flex-wrap: wrap; gap: 8px; }
.plan-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.plan-list li { display: flex; gap: 10px; font-size: .95rem; color: var(--fg); }
.plan-list li::before { content: "✓"; color: var(--yellow); flex: none; font-weight: 700; }
.plan-desc { color: var(--muted); font-size: .98rem; }
.plan-cta { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-block-start: 6px; }
.plan-foot { font-size: .8rem; color: var(--muted); }
.plan-foot b { color: var(--yellow); font-weight: 560; }

/* Tertiary: WhatsApp the plan. Mono/neutral — the dock's rule applies here. */
.plan-wa, .plan-tert {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--f-mono); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); padding: 10px 4px; transition: color .25s;
}
.plan-wa svg { flex: none; }
.plan-wa:hover, .plan-tert:hover { color: var(--fg-strong); }

/* Micro-conversion: mail a copy of the plan. The plan itself is never gated. */
.plan-mail { display: grid; gap: 12px; margin-block-start: -6px; }
.plan-mail-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 12px; align-items: start; }
.plan-mail .field input { padding: 13px 14px 12px; font-size: .95rem; }
.plan-mail .field label { inset-block-start: 13px; font-size: .95rem; }
.plan-mail .field:has(input:focus) label,
.plan-mail .field:has(input:not(:placeholder-shown)) label { background: var(--void-800); }
.plan-mail-note { font-size: .78rem; color: var(--muted); }
.plan-mail-state { font-size: .9rem; color: var(--yellow); display: none; }
.plan-mail.is-sent .plan-mail-row, .plan-mail.is-sent .plan-mail-note { display: none; }
.plan-mail.is-sent .plan-mail-state, .plan-mail.is-error .plan-mail-state { display: block; }
.plan-mail.is-error .plan-mail-state { color: var(--pink); }
.plan-mail.is-sending .btn { opacity: .6; pointer-events: none; }

/* Honeypot — off-screen but not display:none, which some bots skip */
.hp { position: absolute; opacity: 0; pointer-events: none; }

/* --------------------------------------------------------------------------
   16 · STUDIO / ABOUT STRIP
   -------------------------------------------------------------------------- */
.studio-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(30px, 5vw, 80px); align-items: start; }
.studio-grid h2 { font-size: var(--t-h2); font-weight: 570; margin-block-start: 16px; }
.studio-grid .lead { margin-block-start: 18px; }
.studio-grid .lead + .lead { margin-block-start: 14px; }
.studio-note { margin-block-start: 22px; color: var(--muted); font-size: .95rem; }
.city-list { display: grid; gap: 0; }
.city { display: grid; grid-template-columns: 1fr auto; align-items: center; padding-block: 20px; border-block-end: 1px solid var(--line); }
.city:first-child { border-block-start: 1px solid var(--line); }
.city .nm { font-family: var(--f-display); font-weight: 570; font-size: clamp(1.4rem, 2.4vw, 2rem); color: var(--fg-strong); display: flex; align-items: baseline; gap: 14px; }
.city .ad { font-size: .86rem; color: var(--muted); font-weight: 420; }
/* Real coordinates beside the live clocks — the studio list reads as an
   instrument panel rather than an address block. */
.city .nm { flex-wrap: wrap; }
.city .coord { flex-basis: 100%; font-family: var(--f-mono); font-size: .68rem; letter-spacing: .12em; color: var(--muted); margin-block-start: 4px; font-variant-numeric: tabular-nums; }
.city .tm { font-family: var(--f-mono); font-size: .95rem; color: var(--blue-ink); font-variant-numeric: tabular-nums; }
/* "next" city reads quieter than the live ones — via colour, not opacity,
   which used to drag its clock to 1.92:1 and its tag to 4.42:1 */
.city.soon .nm { color: var(--muted); }
.city.soon .tm { color: var(--muted); }
.city .soon-tag { font-family: var(--f-mono); font-size: .66rem; letter-spacing: .2em; color: var(--yellow); text-transform: uppercase; }

/* --------------------------------------------------------------------------
   17 · FAQ
   -------------------------------------------------------------------------- */
.faq-list { max-width: 900px; }
.faq-list details { border-block-end: 1px solid var(--line); }
.faq-list details:first-child { border-block-start: 1px solid var(--line); }
.faq-list summary {
  list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding-block: 24px; font-family: var(--f-display); font-weight: 560; font-size: clamp(1.1rem, 1.8vw, 1.45rem); color: var(--fg-strong);
  transition: color .3s;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary:hover { color: var(--blue-ink); }
.faq-list summary .pm { position: relative; inline-size: 34px; block-size: 34px; flex: none; border-radius: 50%; box-shadow: inset 0 0 0 1px var(--line-strong); }
.faq-list summary .pm::before, .faq-list summary .pm::after {
  content: ""; position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  inline-size: 12px; block-size: 1.6px; background: currentColor; translate: -50% -50%;
  transition: rotate .4s var(--snap);
}
.faq-list summary .pm::after { rotate: 90deg; }
.faq-list details[open] summary .pm::after { rotate: 0deg; }
.faq-list details[open] summary { color: var(--blue-ink); }
.faq-a { padding-block: 0 26px; color: var(--muted); max-width: 66ch; }
.faq-a a { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }
@supports (interpolate-size: allow-keywords) {
  .faq-list details::details-content {
    block-size: 0; overflow: clip; interpolate-size: allow-keywords;
    transition: block-size .5s var(--out), content-visibility .5s allow-discrete;
  }
  .faq-list details[open]::details-content { block-size: auto; }
}

/* The full stop that answers the cube's heartbeat (one.js listens for
   rbk:beat). Inherits its own colour so it is invisible until it pulses. */
.beat { color: var(--yellow); }
@media (prefers-reduced-motion: no-preference) {
  .beat.is-beat { animation: beat-echo .62s var(--out); }
  @keyframes beat-echo {
    0%   { text-shadow: none; }
    28%  { text-shadow: 0 0 20px rgb(248 206 58 / .85), 0 0 46px rgb(248 206 58 / .35); }
    100% { text-shadow: none; }
  }
}

/* --------------------------------------------------------------------------
   18 · CONTACT
   -------------------------------------------------------------------------- */
.contact { position: relative; }
.contact-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: clamp(30px, 5vw, 80px); align-items: start; }

.cform { display: grid; gap: 18px; }
.field { position: relative; }
.field input, .field select, .field textarea {
  inline-size: 100%; padding: 17px 16px 15px; border-radius: var(--r-sm);
  background: rgb(255 255 255 / .03); color: var(--fg-strong);
  border: 1px solid var(--line); font: inherit; font-size: 1rem;
  transition: border-color .3s, background-color .3s, box-shadow .3s;
}
.field textarea { min-block-size: 130px; resize: vertical; field-sizing: content; max-block-size: 320px; }
.field input:hover, .field textarea:hover, .field select:hover { border-color: var(--line-strong); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--blue); background: rgb(30 96 253 / .05);
  box-shadow: 0 0 0 3px rgb(30 96 253 / .18);
}
/* Labels are the field's only visible name — they read as text, so they take a
   text-grade colour, resting and floated alike. */
.field label {
  position: absolute; inset-inline-start: 16px; inset-block-start: 16px; pointer-events: none;
  color: var(--muted); transition: all .25s var(--out); font-size: 1rem; background: transparent;
}
.field:has(input:focus) label, .field:has(input:not(:placeholder-shown)) label,
.field:has(textarea:focus) label, .field:has(textarea:not(:placeholder-shown)) label {
  inset-block-start: -8px; font-size: .72rem; color: var(--blue-ink);
  background: var(--void-950); padding-inline: 6px; border-radius: 4px; font-family: var(--f-mono); letter-spacing: .08em;
}
.cform .row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.cform-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.cform-note { font-size: .84rem; color: var(--muted); }
.cform-note b { color: var(--yellow); font-weight: 560; }
.form-state { font-size: .95rem; display: none; align-items: center; gap: 10px; }
.form-state a { text-decoration: underline; text-underline-offset: 3px; }
.cform.is-success .form-state.ok { display: flex; color: var(--yellow); }
.cform.is-error .form-state.err { display: flex; color: var(--pink); }
.cform.is-sending .btn-primary { opacity: .6; pointer-events: none; }

.book-card {
  border-radius: var(--r-lg); padding: clamp(26px, 3vw, 40px);
  background: linear-gradient(150deg, rgb(30 96 253 / .14), rgb(255 255 255 / .02) 58%);
  box-shadow: inset 0 0 0 1px rgb(94 133 255 / .28);
  display: grid; gap: 16px;
}
.book-card h3 { font-size: 1.5rem; font-weight: 580; }
.book-card p { color: var(--muted); }
.alt-contact { display: grid; gap: 12px; margin-block-start: 26px; }
.alt-line { display: flex; align-items: center; gap: 12px; font-size: .95rem; color: var(--muted); }
.alt-line a { color: var(--fg); font-weight: 540; transition: color .25s; }
.alt-line a:hover { color: var(--blue-ink); }
.alt-line .alt-meta { color: var(--muted); }
.alt-line svg { flex: none; color: var(--faint); }

/* --------------------------------------------------------------------------
   19 · FOOTER
   -------------------------------------------------------------------------- */
/* The footer as an engineering title block: hairline grid, numbered sections,
   the drawing's own metadata along the bottom rule. */
.foot { border-block-start: 1px solid var(--line); padding-block: 56px 40px; position: relative; z-index: 1; background: var(--void-950); }
.foot-grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr 1fr; gap: 34px; }
/* The title block's hairline grid only makes sense while the four columns sit
   on one row; below that the columns wrap and a rule would land mid-row. */
@media (min-width: 1101px) {
  .foot-grid { gap: 0; }
  .foot-grid > * + * { border-inline-start: 1px solid var(--line); padding-inline-start: clamp(18px, 2.6vw, 34px); }
  .foot-grid > *:not(:last-child) { padding-inline-end: clamp(18px, 2.6vw, 34px); }
}
/* the section marker is a numeral, so it takes the numeral colour the rest of
   the page uses (--faint would be 3.14:1 — decoration only) */
.foot-h .sec { color: var(--blue-ink); margin-inline-end: 8px; }
.foot-brand img { block-size: 32px; inline-size: auto; }
.foot-brand p { color: var(--muted); font-size: .92rem; max-width: 34ch; margin-block-start: 16px; }
/* labelled with <p class="foot-h">, not a heading: they are column labels, and
   an <h5> after the page's h2s is a heading-order violation */
.foot .foot-h { font-family: var(--f-mono); font-weight: 400; font-size: .72rem; letter-spacing: .22em; text-transform: uppercase; color: var(--muted); margin: 0 0 16px; }
.foot-col { display: grid; gap: 10px; align-content: start; justify-items: start; }
.foot-col a { color: var(--muted); font-size: .95rem; transition: color .25s; }
.foot-col a:hover { color: var(--fg-strong); }
.foot-soon { color: var(--muted); font-size: .95rem; }
.foot-bottom { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px; margin-block-start: 48px; padding-block-start: 24px; border-block-start: 1px solid var(--line); font-size: .84rem; color: var(--muted); }
.socials { display: flex; gap: 8px; }
.socials a { inline-size: 38px; block-size: 38px; display: grid; place-items: center; border-radius: 50%; color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); transition: color .25s, box-shadow .25s, transform .25s var(--spring); }
.socials a:hover { color: #fff; box-shadow: inset 0 0 0 1px var(--line-strong); transform: translateY(-2px); }
.cookie-btn { color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.cookie-btn:hover { color: var(--fg); }

/* --------------------------------------------------------------------------
   20 · FLOATING DOCK
   -------------------------------------------------------------------------- */
.dock {
  /* inline-end offset leaves room for the HubSpot chat launcher (via GTM) */
  position: fixed; inset-block-end: 22px; inset-inline-end: 92px; z-index: 110;
  display: flex; gap: 10px; align-items: center;
  translate: 0 90px; opacity: 0; transition: translate .5s var(--spring), opacity .4s;
}
.dock.is-in { translate: 0 0; opacity: 1; }
.dock .btn-primary { box-shadow: 0 16px 44px -12px rgb(30 96 253 / .9); }
/* WhatsApp control: same function, inside the rationed palette (the brand
   green was the one off-system colour on the page) */
.dock-wa {
  inline-size: 52px; block-size: 52px; border-radius: 50%; display: grid; place-items: center;
  background: rgb(255 255 255 / .06); color: var(--fg); backdrop-filter: blur(10px);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: transform .3s var(--spring), background-color .3s, box-shadow .3s;
}
.dock-wa:hover { transform: translateY(-3px); background: rgb(255 255 255 / .1); box-shadow: inset 0 0 0 1px rgb(255 255 255 / .3); }
/* The consent banner owns the bottom of the viewport while it is up, at both
   breakpoints: banner inset + its measured height (set by consent.js) + gap. */
html.consent-open .dock { inset-block-end: calc(16px + var(--consent-h, 96px) + 18px); }

/* --------------------------------------------------------------------------
   21 · CONSENT BANNER (markup generated by consent.js)
   -------------------------------------------------------------------------- */
.consent-banner {
  position: fixed; inset-inline: 16px; inset-block-end: 16px; z-index: 140;
  margin-inline: auto; max-inline-size: 640px;
  display: flex; flex-wrap: wrap; gap: 14px; align-items: center; justify-content: space-between;
  background: rgb(13 16 30 / .92); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border-radius: var(--r-md); box-shadow: inset 0 0 0 1px var(--line-strong), 0 30px 70px -30px rgb(0 0 0 / .8);
  padding: 18px 20px;
  translate: 0 20px; opacity: 0; transition: translate .4s var(--out), opacity .4s;
}
.consent-banner.is-in { translate: 0 0; opacity: 1; }
.consent-text { font-size: .88rem; color: var(--muted); max-width: 46ch; }
.consent-actions { display: flex; gap: 10px; }
.consent-btn { padding: 10px 20px; font-size: .9rem; }

/* --------------------------------------------------------------------------
   22 · SECTION INDEX RAIL (desktop)
   -------------------------------------------------------------------------- */
.rail { position: fixed; inset-inline-end: 20px; inset-block-start: 50%; translate: 0 -50%; z-index: 100; display: grid; gap: 12px; }
.rail a { position: relative; inline-size: 10px; block-size: 10px; display: grid; place-items: center; }
.rail a i { inline-size: 5px; block-size: 5px; border-radius: 50%; background: var(--faint); transition: background-color .3s, transform .3s var(--spring); }
.rail a:hover i, .rail a.is-live i { background: var(--fg-strong); transform: scale(1.7); }
.rail a.is-live i { background: var(--blue); box-shadow: 0 0 10px rgb(30 96 253 / .9); }
.rail a span {
  position: absolute; inset-inline-end: 22px; inset-block-start: 50%; translate: 0 -50%;
  font-family: var(--f-mono); font-size: .66rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--fg); background: rgb(13 16 30 / .9); padding: 6px 10px; border-radius: 6px;
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .25s;
  box-shadow: inset 0 0 0 1px var(--line);
}
.rail a:hover span { opacity: 1; }

/* --------------------------------------------------------------------------
   23 · REVEALS — pure CSS transitions, toggled by an IntersectionObserver.
   The hidden states are gated behind html.js, which one.js adds as its FIRST
   statement. If the script fails to load, parse, or run, the class is never
   added and every element is simply visible — content can never get stuck
   hidden the way a JS "from" animation can.
   -------------------------------------------------------------------------- */
html.js [data-rv] {
  opacity: 0; transform: translateY(16px);
  transition: opacity .5s var(--out), transform .5s var(--out);
}
html.js [data-rv].is-in { opacity: 1; transform: none; }

/* Hero intro — same mechanism, staggered with transition delays.
   The hidden state is written as :not(.hero-in) rather than as a pair of
   competing rules. The pair looked equivalent but was not: `html.js .hero h1
   .ln` carries one more element selector than `.hero-in .hero h1 .ln`, so it
   won the cascade and the intro never actually landed — the badge stayed at
   opacity 0 and the headline stayed translated out of its clip mask for every
   visitor with JS and normal motion settings. Keep the negation. */
html.js .hero-badge, html.js .hero .lead,
html.js .hero-cta, html.js .hero-tert {
  transition: opacity .6s var(--out), transform .6s var(--out);
}
html.js body:not(.hero-in) .hero-badge, html.js body:not(.hero-in) .hero .lead,
html.js body:not(.hero-in) .hero-cta, html.js body:not(.hero-in) .hero-tert {
  opacity: 0; transform: translateY(14px);
}
html.js .hero h1 .ln { transition: transform .9s var(--out); }
html.js body:not(.hero-in) .hero h1 .ln { transform: translateY(108%); }
.hero-in .hero h1 .line:nth-child(2) .ln { transition-delay: .09s; }
.hero-in .hero h1 .line:nth-child(3) .ln { transition-delay: .18s; }
.hero-in .hero .lead   { transition-delay: .30s; }
.hero-in .hero-cta     { transition-delay: .40s; }
.hero-in .hero-tert    { transition-delay: .50s; }

/* --------------------------------------------------------------------------
   24 · RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1100px) {
  .rail { display: none; }
  .stats { grid-template-columns: repeat(3, 1fr); gap: 34px 20px; }
  .planner-grid, .process-grid, .sprint-grid, .contact-grid, .studio-grid { grid-template-columns: 1fr; }
  .process-stage .anim-layer,
  .process-stage .process-note,
  .process-stage .process-count { display: none; }
  .svc-grid { grid-template-columns: 1fr; }
  /* the path turns vertical: dashed hairline per row, node in the gutter */
  .path { grid-template-columns: 1fr; gap: 0; margin-block-start: 44px; padding-block-start: 0; }
  .path::before { display: none; }
  .tier { padding-block: 26px 30px; padding-inline-start: 26px; border-block-start: 1px dashed rgb(255 255 255 / .1); }
  .tier .node { inset-block-start: 33px; }
  .foot-grid { grid-template-columns: 1fr 1fr; }
}
/* The hero keeps its real two-column layout all the way down to 900. It used
   to collapse at 1100 and park the stage as a full-bleed absolute layer behind
   the copy at opacity .5 — which at 1024 painted the cube straight through the
   headline, with the yellow "AI" sitting on the cube's inner square. At 1024
   there is no free band to park it in either: the copy runs y197–691 of a 768
   viewport. So above 900 the cube keeps a column of its own (583px copy beside
   301px stage at 1024) and cannot overlap anything.

   C2: below 900 there is no room for a second column, and only there does the
   stage go behind the copy — parked low and dim, because it used to sit centred
   behind the hero lead at 390px. */
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-stage { min-block-size: 0; position: absolute; inset: 0; z-index: -1; opacity: .3; }
  .gl-fallback { place-items: end center; padding-block-end: 4%; }
  .gl-fallback svg { inline-size: min(34%, 132px); filter: drop-shadow(0 16px 34px rgb(30 96 253 / .3)); }
}
/* The chat launcher is a fixed ~100×96 disc in the bottom corner, and it is the
   *physical* right corner in both directions — measured identical on /en/ and
   /ar/, because the widget is not direction-aware. The hero is exactly 100svh,
   so its last line lands on the viewport floor and the tertiary wrapped under
   the disc; worst in AR, where the tertiary was the longest string on the page
   and RTL wraps it into that same right corner. Reserve the corner, and lift
   the hero's floor a little so the line is not flush against it either. */
@media (max-width: 560px) {
  .hero { padding-block-end: 92px; }
  .hero-tert { padding-right: 96px; }
}
@media (max-width: 860px) {
  .nav { display: none; }
  .burger { display: block; }
  .hdr .btn-book { display: none; }
  .case, .case:nth-child(even) { grid-template-columns: 1fr; }
  .case:nth-child(even) .case-media { order: 0; }
  .dock { inset-inline: 14px; inset-block-end: 14px; justify-content: stretch; }
  .dock .btn-primary { flex: 1; justify-content: center; }
  .cform .row { grid-template-columns: 1fr; }
  .work { border-radius: clamp(18px, 3vw, 28px); }
  .ledger { grid-template-columns: 1fr; }
  .led { padding-inline-end: 0; }
  .led:nth-child(2n) { border-inline-start: 0; padding-inline-start: 0; }
  .stats { grid-template-columns: 1fr; gap: 26px; }
  .readout-in { grid-template-columns: 1fr; gap: 22px; }
  .ro { padding-inline-end: 0; }
  .ro + .ro { border-inline-start: 0; padding-inline-start: 0; border-block-start: 1px solid var(--line); padding-block-start: 20px; }
  .plan-mail-row { grid-template-columns: 1fr; }
  /* the sprint HUD is the one label long enough to break at card width: the
     nowrap <b> takes the whole line and the descriptive half stacks beside it.
     Let the line break instead. */
  .sprint .hud { flex-wrap: wrap; }
  .sprint .hud b { white-space: normal; }
}

/* --------------------------------------------------------------------------
   25 · ARABIC / RTL
   Layout mirrors on its own — this file is authored in logical properties. What
   is left is what logical properties cannot express: the type pairing, the
   direction of glyphs and gradients, and the sign of a few nudges.
   -------------------------------------------------------------------------- */
[dir="rtl"] {
  /* Tajawal first so Arabic never waits on a face that cannot render it; the
     Latin faces stay in the stack for Latin runs. Mono stays Latin: Arabic has
     no uppercase and letter-spacing breaks its joining. */
  --f-display: "Tajawal", "Tajawal Fallback", "Clash Display", system-ui, sans-serif;
  --f-body: "Tajawal", "Tajawal Fallback", "Satoshi", system-ui, sans-serif;
}

/* Type retune. Arabic ascenders and tashkeel need vertical room, and the hero
   lines clip their overflow, so the display line-height goes 0.98 → 1.3. */
[dir="rtl"] h1, [dir="rtl"] h2, [dir="rtl"] h3, [dir="rtl"] h4 { line-height: 1.3; letter-spacing: 0; }
[dir="rtl"] body { line-height: 1.8; }
[dir="rtl"] .hero h1 { line-height: 1.3; letter-spacing: 0; font-weight: 800; }
[dir="rtl"] .hero h1 .line { padding-block-end: .2em; margin-block-end: -.2em; }
[dir="rtl"] .sect-head h2, [dir="rtl"] .sprint h2, [dir="rtl"] .planner-shell h2,
[dir="rtl"] .studio-grid h2 { font-weight: 700; }
[dir="rtl"] .plan-name, [dir="rtl"] .city .nm, [dir="rtl"] .case h3,
[dir="rtl"] .chip b, [dir="rtl"] .ro-v { font-weight: 700; }
[dir="rtl"] .lead, [dir="rtl"] .step p, [dir="rtl"] .svc p, [dir="rtl"] .led p { letter-spacing: 0; }

/* HUD: the structural label stays Latin mono (SEC 01 · MODE / 01), the
   descriptive half is Arabic, so it drops the tracking and takes Tajawal. */
[dir="rtl"] .hud { letter-spacing: 0; font-family: var(--f-body); text-transform: none; }
[dir="rtl"] .hud b { font-family: var(--f-mono); letter-spacing: .22em; text-transform: uppercase; }
[dir="rtl"] .hud::before { background: linear-gradient(270deg, var(--blue), transparent); }
[dir="rtl"] .work .hud::before { background: linear-gradient(270deg, #B0331E, transparent); }
/* Latin digits and mono in instrument contexts, per the AR-tree convention */
[dir="rtl"] .ro-v, [dir="rtl"] .stat b, [dir="rtl"] .led-n, [dir="rtl"] .city .tm,
[dir="rtl"] .process-count b, [dir="rtl"] .plan-tl .lab, [dir="rtl"] .day .d,
[dir="rtl"] .svc .no, [dir="rtl"] .sheet a.big small { font-variant-numeric: tabular-nums; letter-spacing: 0; }
/* Latin-only instrument labels keep JetBrains mono… */
[dir="rtl"] .led-n, [dir="rtl"] .city .tm, [dir="rtl"] .svc .no,
[dir="rtl"] .sheet a.big small, [dir="rtl"] .process-count span { font-family: var(--f-mono); }
/* …while the descriptive labels are Arabic sentences, so they take Tajawal: in
   mono the Latin word-spaces are wider than the Arabic glyphs and the phrase
   falls apart. */
[dir="rtl"] .process-note, [dir="rtl"] .ro-k, [dir="rtl"] .hero-note,
[dir="rtl"] .plan-tl .lab, [dir="rtl"] .day .d { font-family: var(--f-body); }
/* …but any of those that carry Arabic words lose the tracking, which would
   otherwise break the joining. Latin-only labels (01-04, MODE / 01, clocks,
   / 04) keep theirs. */
[dir="rtl"] .ro-k, [dir="rtl"] .hero-note, [dir="rtl"] .process-note,
[dir="rtl"] .plan-tl .lab, [dir="rtl"] .day .d, [dir="rtl"] .tag,
[dir="rtl"] .city .soon-tag, [dir="rtl"] .opt .lbl,
[dir="rtl"] .foot .foot-h, [dir="rtl"] .rail a span,
[dir="rtl"] .hero-badge, [dir="rtl"] .plan-wa, [dir="rtl"] .plan-tert,
[dir="rtl"] .case-stat div b, [dir="rtl"] .stat b { letter-spacing: 0; text-transform: none; }

/* Arabic at line-height 1.3 needs the extra room somewhere: the display size
   steps down a notch so the hero still fits one screen. */
[dir="rtl"] .hero h1 { font-size: clamp(2.3rem, 5.2vw, 4.9rem); }
[dir="rtl"] .hero-badge { font-family: var(--f-body); font-size: .82rem; }

/* Directional glyphs and origins */
[dir="rtl"] .arr { display: inline-block; transform: scaleX(-1); }
[dir="rtl"] .btn:hover .arr, [dir="rtl"] .btn:focus-visible .arr,
[dir="rtl"] .link-arrow:hover .arr, [dir="rtl"] a.ro:hover .arr { transform: scaleX(-1) translateX(5px); }
[dir="rtl"] .progress { transform-origin: 100% 50%; }
[dir="rtl"] .nav a::after, [dir="rtl"] .foot-col a::after { transform-origin: 100% 50%; }
/* the data pulse travels with the reading direction */
[dir="rtl"] .nav a:hover::after, [dir="rtl"] .foot-col a:hover::after { animation-direction: reverse; }
[dir="rtl"] .step.is-live { transform: translateX(-8px); }
/* (the client wall's two rows carry their own RTL direction flip in §7) */
[dir="rtl"] .t-rail { mask: linear-gradient(270deg, #000 92%, transparent); -webkit-mask: linear-gradient(270deg, #000 92%, transparent); }
[dir="rtl"] .plan-tl .bar i { background: linear-gradient(270deg, var(--blue), var(--violet)); }
[dir="rtl"] .rail-line i { background: linear-gradient(180deg, var(--blue), var(--violet)); }
[dir="rtl"] .tier li::before { content: "←"; }

/* --------------------------------------------------------------------------
   26 · REDUCED MOTION — hard gate
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001s !important; animation-iteration-count: 1 !important; transition-duration: .15s !important; }
  .grain { animation: none; }
  html.js [data-rv], html.js body:not(.hero-in) .hero-badge, html.js body:not(.hero-in) .hero .lead,
  html.js body:not(.hero-in) .hero-cta, html.js body:not(.hero-in) .hero-tert { opacity: 1 !important; transform: none !important; }
  html.js body:not(.hero-in) .hero h1 .ln { transform: none !important; }
}

/* Print: just the content */
@media print {
  .stage, .aurora, .grain, .dock, .rail, .progress, .hdr { display: none !important; }
  body { background: #fff; color: #000; }
}

/* --------------------------------------------------------------------------
   26 · WORK CARDS CARRY THE DOSSIER SCENE (Wave B)
   The case cards swapped a screenshot for the hand-authored SVG scene, so the
   frame keeps its 16/10.6 box but takes the void field behind the drawing.
   direction: ltr because a scene is a diagram with Latin HUD labels — it must
   not mirror under dir="rtl" the way prose does.
   -------------------------------------------------------------------------- */
/* 16/9 rather than the photo frame's 16/10.6: six files now sit where five
   did, and the shallower frame keeps the section within a tenth of its
   previous height. The scene letterboxes into its own field, so the change
   is invisible. */
.case-media.scene-frame { background: #06080F; aspect-ratio: 16 / 9; direction: ltr; unicode-bidi: isolate; contain: layout paint; }
.case-media.scene-frame img { inline-size: 100%; block-size: 100%; object-fit: contain; }
