/* ============================================================================
   QRBrand Ltd — Design System
   Single source of truth. Mobile-first. Logical properties → RTL for free.
   ========================================================================= */

/* ------------------------------------------------------------------ FONTS --
   Markazi Text is SELF-HOSTED. Loading it from Google meant a third-party
   round trip, a CSP exception, and a dependency on a host we do not control —
   for a face the site cannot do without in Arabic. It is now one 40 KB file
   on our own origin, cached immutably for a year, with no external request
   and no way for it to silently fail to arrive.

   Only the Arabic subset is shipped: Latin comes free from the system stack,
   so nothing is downloaded that is not actually rendered. It is a variable
   font, so 400–700 is one file rather than four.                            */
@font-face {
  font-family: "Markazi Text";
  src: url("/assets/fonts/markazi-arabic.woff2") format("woff2-variations"),
       url("/assets/fonts/markazi-arabic.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+08A0-08FF,
                 U+200C-200E, U+2010-2011, U+FB50-FDFF, U+FE70-FEFF;
}

/* ---------------------------------------------------------------- (legacy) --
   The site runs on the system stack: SF Pro on iOS/macOS, Roboto on Android,
   Segoe UI on Windows. Nothing to download, nothing to block rendering.

   To upgrade to a self-hosted face later, drop the woff2 files into
   /assets/fonts/ and paste the @font-face block from deploy/DEPLOY.md back in
   here. The family names are already first in the stacks below, so they take
   effect the moment the rules exist — and until then nothing is requested.
   (An @font-face pointing at a missing file is NOT free: the browser still
   issues the request and logs a 404.)                                        */

/* ---------------------------------------------------------------- TOKENS -- */
:root {
  /* Space */
  --bg:            #05060a;
  --bg-deep:       #020306;
  --surface:       rgba(255, 255, 255, .045);
  --surface-2:     rgba(255, 255, 255, .075);
  --surface-solid: rgba(10, 12, 20, .88);
  --line:          rgba(255, 255, 255, .10);
  --line-strong:   rgba(255, 255, 255, .18);

  /* Ink */
  --ink:      #f2f5fa;
  --ink-soft: rgba(242, 245, 250, .72);
  --ink-mute: rgba(242, 245, 250, .48);
  --ink-faint:rgba(242, 245, 250, .28);

  /* Brand */
  --cyan:   #22d3ee;
  --violet: #8b5cf6;
  --ember:  #ff6b35;
  --mint:   #34d399;
  --amber:  #fbbf24;
  --rose:   #fb7185;
  --brand-grad: linear-gradient(120deg, var(--cyan) 0%, var(--violet) 100%);

  /* Type — system-first. "QRBrand Display"/"QRBrand Arabic" are optional
     self-hosted upgrades; when the files are absent the stack simply
     falls through to SF Pro / Roboto / Segoe UI with no layout shift. */
  --font-ui: "QRBrand Display", ui-sans-serif, system-ui, -apple-system,
             "SF Pro Display", "SF Pro Text", "Segoe UI Variable", "Segoe UI",
             Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-ar: "Markazi Text", "SF Arabic", "Geeza Pro",
             "Noto Naskh Arabic", "Segoe UI", Tahoma, serif;
  --font: var(--font-ui);

  /* ---------------------------------------------------------- TYPE SCALE --
     Every size interpolates linearly between its phone value at 375px and its
     full value at 1600px, so there is one continuous ramp with no breakpoint
     jumps anywhere in between.

                        375px      1600px
       t-xs              11          13     micro labels, chips, counters
       t-sm              13          15     secondary copy, sheet body
       t-base            15         17.5    body
       t-lg             17.5         21     lead paragraphs
       t-xl              21          28     panel titles
       t-2xl             26          40     display

     The steps widen towards the top (≈1.16 between the small sizes, ≈1.4 at
     the display end): a phone needs a compressed hierarchy to fit, a large
     display can afford the drama. Nothing is ever below 11px.               */
  --t-xs:   clamp(11px,   .649rem + .163vw, 13px);
  --t-sm:   clamp(13px,   .774rem + .163vw, 15px);
  --t-base: clamp(15px,   .890rem + .204vw, 17.5px);
  --t-lg:   clamp(17.5px, 1.027rem + .286vw, 21px);
  --t-xl:   clamp(21px,   1.179rem + .571vw, 28px);
  --t-2xl:  clamp(26px,   1.357rem + 1.143vw, 40px);

  /* Rhythm */
  /* Every control in the header is this size: the language button, the sound
     button, and the logo mark beside them. One number, so "the logo matches
     the buttons" is a fact about the stylesheet rather than two values that
     happen to agree today and drift apart at the next edit. */
  --hud-control: 36px;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  /* Form */
  --r-sm: 10px; --r-md: 14px; --r-lg: 20px; --r-xl: 28px; --r-full: 999px;

  /* Motion */
  --ease:     cubic-bezier(.32, .72, 0, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --spring:   cubic-bezier(.34, 1.46, .64, 1);
  --fast: .18s; --mid: .34s; --slow: .62s;

  /* Elevation */
  --sh-sm: 0 2px 10px rgba(0,0,0,.34);
  --sh-md: 0 10px 34px rgba(0,0,0,.44);
  --sh-lg: 0 26px 78px rgba(0,0,0,.58);
  --glow-cyan:   0 0 0 1px rgba(34,211,238,.28), 0 10px 40px rgba(34,211,238,.24);
  --glow-violet: 0 10px 44px rgba(139,92,246,.30);

  /* Chrome */
  --hud-h: 108px;
  --safe-t: env(safe-area-inset-top,   0px);
  --safe-b: env(safe-area-inset-bottom,0px);
  --safe-l: env(safe-area-inset-left,  0px);
  --safe-r: env(safe-area-inset-right, 0px);
}

/* ------------------------------------------------------------ ARABIC SCALE --
   Markazi Text is a naskh face: its letter bodies sit small inside the em, so
   at the same nominal size Arabic reads noticeably smaller than the Latin it
   replaces. The correction TAPERS rather than being a flat percentage:

       t-xs   ×1.23     small text is where a naskh face loses most legibility
       t-sm   ×1.19
       t-base ×1.13
       t-lg   ×1.11
       t-xl   ×1.07
       t-2xl  ×1.06     large display text already reads clearly; more size
                        here only costs screen space, which phones cannot spare

   The previous scale applied roughly +20% at every level, which made Arabic
   headings and body copy visibly heavier than their English counterparts and
   pushed the mobile layout around.                                          */
:root[dir="rtl"] {
  --font: var(--font-ar);
  --t-xs:   clamp(13.5px, .796rem + .204vw, 16px);
  --t-sm:   clamp(15.5px, .921rem + .204vw, 18px);
  --t-base: clamp(17px,  1.005rem + .245vw, 20px);
  --t-lg:   clamp(19.5px, 1.152rem + .286vw, 23px);
  --t-xl:   clamp(22.5px, 1.263rem + .612vw, 30px);
  --t-2xl:  clamp(27.5px, 1.441rem + 1.184vw, 42px);
}

/* Naskh needs air between lines, but the earlier 1.8–1.9 was so open that
   Arabic paragraphs ran much taller than the same English ones. */
:root[dir="rtl"] body { line-height: 1.7; }
/* Sheet copy is not listed here: the panel owns its own leading through
   --sheet-lead, and a blanket 1.8 fought that token and won on specificity. */
:root[dir="rtl"] .tagline { line-height: 1.8; }
:root[dir="rtl"] .eyebrow,
:root[dir="rtl"] .field__label,
:root[dir="rtl"] .stat__label { line-height: 1.5; }

/* ---------------------------------------------------------------- RESET -- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  min-height: 100svh;
  min-height: 100dvh;
  font-family: var(--font);
  font-size: var(--t-base);
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  overflow-x: hidden;
  overscroll-behavior-y: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  -webkit-tap-highlight-color: transparent;
}

/* Chrome/UI is non-selectable; real content stays copyable. */
.hud, .dock, .stats, .cosmos, .btn, .chip, .stage-chip { user-select: none; -webkit-user-select: none; }

img, svg, canvas { display: block; max-width: 100%; }
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
[hidden] { display: none !important; }

:where(a, button, input, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
  border-radius: 6px;
}

.skip-link {
  position: fixed;
  inset-block-start: calc(var(--safe-t) + 8px);
  inset-inline-start: 8px;
  z-index: 999;
  padding: 10px 16px;
  background: var(--surface-solid);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--ink);
  font-size: var(--t-sm);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--fast) var(--ease);
}
.skip-link:focus { transform: translateY(0); }

/* ----------------------------------------------------------------- BOOT -- */
.boot {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center;
  background: var(--bg-deep);
  transition: opacity .5s var(--ease), visibility .5s;
}
.boot.is-done { opacity: 0; visibility: hidden; pointer-events: none; }
/* The first thing anyone sees should be the brand, not three anonymous dots.
   Visible by default; the breath is decoration on top of it. */
.boot__mark {
  inline-size: 56px; block-size: 56px;
  animation: bootBreathe 1.6s var(--ease) infinite;
}
@keyframes bootBreathe {
  0%, 100% { opacity: .55; transform: scale(.96); }
  50%      { opacity: 1;   transform: scale(1.02); }
}

/* --------------------------------------------------------------- COSMOS -- */
.cosmos {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;   /* input is hit-tested against the sphere in JS */
  touch-action: none;
}
.cosmos--bg { z-index: 0;  }   /* stars, nebula, singularities — behind the words */
.cosmos--fg { z-index: 25; }   /* the sphere, its trail, particles — in front  */

.cosmos-veil {
  position: fixed; inset: 0; z-index: 1;
  pointer-events: none;
  /* The lower wash is deliberately lighter than the upper one: the singularity
     now anchors in the free band near the bottom, and a heavy vignette there
     swallowed its glow. The dock stays readable on its own fill. */
  background:
    radial-gradient(120% 90% at 50% 0%,   rgba(0,0,0,.62) 0%, transparent 58%),
    radial-gradient(150% 100% at 50% 100%, rgba(0,0,0,.52) 0%, transparent 66%);
}

/* ------------------------------------------------------------------ HUD -- */
.hud {
  position: fixed;
  inset-block-start: 0; inset-inline: 0;
  z-index: 40;
  padding-block-start: var(--safe-t);
  pointer-events: none;
  background: linear-gradient(180deg, rgba(5,6,10,.86) 0%, rgba(5,6,10,.42) 62%, transparent 100%);
}
.hud__row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) calc(var(--s4) + var(--safe-r)) var(--s2) calc(var(--s4) + var(--safe-l));
}
.hud__center { flex: 1; display: flex; justify-content: center; min-width: 0; }
.hud__actions { display: flex; gap: var(--s2); }
.hud > *, .hud button, .hud a { pointer-events: auto; }

/* The mark and the logotype are one drawing split across two SVGs: the Q in
   the logotype is the mark. Both take their stroke from currentColor so the
   whole lockup dims as a single object on hover. */
.wordmark {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--ink); text-decoration: none;
  transition: opacity var(--fast) var(--ease);
  /* Always laid out left-to-right: it is a drawing, not text. */
  direction: ltr;
}
.wordmark:hover  { opacity: .72; }
.wordmark:active { opacity: .55; }

/* The name IS the mark now — one drawing, sized by height alone. Its
   viewBox includes the balancing B above the cap line and the bridge and
   ink below the baseline, so the visual letter height is ~24 of ~38.5
   units: a 28px box shows ~17px letters, sitting right in the header row.

   No ambient animation: a pulsing full name reads as a reflow, i.e. as
   breakage. The hover answer stays — transform and filter only, so it
   lives on the compositor and costs the main thread nothing. */
.wordmark__logo {
  height: 30px;
  width: auto;
  flex: 0 0 auto;
  transform-origin: 50% 50%;
}
.wordmark:hover .wordmark__logo,
.wordmark:focus-visible .wordmark__logo {
  transform: scale(1.04);
  filter: drop-shadow(0 0 8px rgba(34, 211, 238, .45));
  transition: transform var(--mid) var(--spring), filter var(--mid) var(--ease);
}

/* NOTE ON backdrop-filter
   Every blurred element must re-sample what is behind it on every frame, and
   behind all of this chrome is a canvas repainting at up to 120 Hz. Nine such
   layers on screen at once was the heaviest thing an iPhone had to do here.
   Blur is therefore reserved for modal surfaces — which cover a scene the
   visitor has stopped playing with — and the persistent chrome uses a slightly
   more opaque fill instead. Over a dark starfield the two are near identical. */
.icon-btn {
  inline-size: var(--hud-control); block-size: var(--hud-control);
  padding-inline: 2px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: rgba(18, 21, 33, .72);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  font-size: var(--t-sm); font-weight: 600;   /* holds the Arabic "ع" — must scale */
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--spring);
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }

/* The language pill: all three choices visible, the active one lit.
   A cycling button hid the third language well enough that even the site's
   owner never found French behind it — an option nobody can see is an
   option that does not exist. */
.lang {
  display: inline-flex; align-items: center; gap: 2px;
  block-size: var(--hud-control);
  padding: 3px;
  border-radius: var(--r-full);
  background: rgba(18, 21, 33, .72);
  border: 1px solid var(--line);
  /* language codes read left-to-right in every locale */
  direction: ltr;
}
.lang__opt {
  padding: 3px 8px;
  border-radius: var(--r-full);
  font-size: calc(var(--t-xs) * .92); font-weight: 600;
  letter-spacing: .03em;
  color: var(--ink-mute);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.lang__opt:hover { color: var(--ink); background: var(--surface-2); }
.lang__opt.is-active {
  color: var(--cyan);
  background: rgba(34, 211, 238, .14);
}
/* "AR"/"EN" are Latin codes whichever way the page is running. */
#langLabel { direction: ltr; font-size: .82em; letter-spacing: .04em; font-weight: 700; }
.icon-btn:active { transform: scale(.92); }
.icon-btn .mute { opacity: 1; transition: opacity var(--fast); }
.icon-btn .wave { opacity: 0;  transition: opacity var(--fast); }
.icon-btn[aria-pressed="true"] { color: var(--cyan); border-color: rgba(34,211,238,.35); }
.icon-btn[aria-pressed="true"] .mute { opacity: 0; }
.icon-btn[aria-pressed="true"] .wave { opacity: 1; }

/* Stage chip */
.stage-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 13px;
  border-radius: var(--r-full);
  background: rgba(12,15,24,.78);
  border: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .13em;
  color: var(--ink-soft);
  white-space: nowrap;
  transition: border-color var(--mid) var(--ease), box-shadow var(--mid) var(--ease);
}
.stage-chip__dot {
  inline-size: 6px; block-size: 6px; border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 10px var(--cyan);
  animation: dotBreathe 2.4s var(--ease) infinite;
}
@keyframes dotBreathe { 0%,100%{opacity:.45; transform:scale(.8)} 50%{opacity:1; transform:scale(1.15)} }
.stage-chip__num  { color: var(--ink); font-variant-numeric: tabular-nums; }
.stage-chip__sep  { opacity: .4; }
.stage-chip[data-stage="2"] { border-color: rgba(139,92,246,.4); box-shadow: var(--glow-violet); }
.stage-chip[data-stage="2"] .stage-chip__dot { background: var(--violet); box-shadow: 0 0 10px var(--violet); }
.stage-chip[data-stage="3"] { border-color: rgba(255,107,53,.45); box-shadow: 0 10px 44px rgba(255,107,53,.28); }
.stage-chip[data-stage="3"] .stage-chip__dot { background: var(--ember); box-shadow: 0 0 12px var(--ember); }
.stage-chip.is-levelup { animation: chipPop .7s var(--spring); }
@keyframes chipPop { 0%{transform:scale(1)} 35%{transform:scale(1.22)} 100%{transform:scale(1)} }

/* Ticker */
.ticker {
  padding: 4px calc(var(--s4) + var(--safe-r)) var(--s2) calc(var(--s4) + var(--safe-l));
  text-align: center;
}
.ticker__text {
  display: inline-block;
  font-size: var(--t-xs);
  color: var(--ink-mute);
  letter-spacing: .05em;
  transition: opacity var(--mid) var(--ease), transform var(--mid) var(--ease);
  will-change: transform, opacity;
}
.ticker__text.is-swap { opacity: 0; transform: translateY(-7px); }

/* ---------------------------------------------------------------- STAGE -- */
.stage {
  position: relative; z-index: 20;
  min-height: 100svh; min-height: 100dvh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--s4);
  padding:
    calc(var(--hud-h) + var(--safe-t) + var(--s5))
    calc(var(--s5) + var(--safe-r))
    calc(var(--s9) + var(--safe-b))
    calc(var(--s5) + var(--safe-l));
  text-align: center;
  pointer-events: none;
}
.stage > * { pointer-events: auto; }

.eyebrow {
  font-size: var(--t-xs);
  letter-spacing: .30em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
:root[dir="rtl"] .eyebrow { letter-spacing: .10em; }

/* ------------------------------------------------------------- HERO MARK --
   The mark is the centre of the screen. The name already sits in the header,
   so repeating it here spent the most valuable space on the page saying the
   same thing twice.

   IMPORTANT — the mark is VISIBLE BY DEFAULT and the entrance is decoration
   layered on top. The previous version hid it with stroke-dashoffset and
   relied on an animation to reveal it, so any interruption at all left an
   empty screen. Nothing below has a fill-mode that can strand it out of
   sight, and the markup carries width/height attributes so it still has a
   size even with no CSS at all.                                             */
/* The hero mark that used to live here has been removed: the same logo is in
   the header, at the size of the controls beside it, and one mark on a page is
   enough.

   What went with it: .title, .title__mark, their entrance and struck-by-the-
   sphere animations, and the one rule that reached inside the glyph
   (.title .mk-core). The header mark draws itself from the SVG's own fill
   attributes and its own .wordmark__mark rules above — it never depended on
   anything here. */

.tagline {
  max-inline-size: 46ch;
  font-size: var(--t-lg);
  /* This element is the page's <h1>. Every visual property it needs is stated
     here rather than inherited, so the heading looks the same as the
     paragraph it used to be — a browser would otherwise make it bold. */
  font-weight: 400;
  /* Latin renders optically larger than Markazi at the same nominal size, so
     this one line — and only this one — is trimmed in English to sit level
     with its Arabic counterpart. Applied by direction, not by breakpoint. */
}
:root[dir="ltr"] .tagline { font-size: calc(var(--t-lg) * .85); }
.tagline {
  color: var(--ink-soft);
  text-wrap: balance;
}

.cta-row {
  display: flex; flex-wrap: wrap; gap: var(--s3);
  justify-content: center;
  margin-block-start: var(--s2);
}


/* --------------------------------------------------------------- BUTTON -- */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  min-block-size: 48px;
  padding: 13px 24px;
  border-radius: var(--r-full);
  font-size: var(--t-sm); font-weight: 600;
  letter-spacing: .02em;
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
  touch-action: manipulation;
  transition: transform var(--fast) var(--spring), box-shadow var(--mid) var(--ease),
              background var(--mid) var(--ease), border-color var(--mid) var(--ease), opacity var(--fast);
}
.btn:active { transform: scale(.965); }
.btn:disabled { opacity: .55; pointer-events: none; }

.btn--primary {
  background: var(--brand-grad);
  color: #04060c;
  box-shadow: 0 8px 30px rgba(34,211,238,.28), 0 2px 0 rgba(255,255,255,.22) inset;
}
.btn--primary:hover { box-shadow: 0 12px 42px rgba(34,211,238,.42), 0 2px 0 rgba(255,255,255,.3) inset; }
/* Content sits above the sheen; the sheen sits above the gradient fill. */
.btn > span:not(.btn__glow), .btn > svg { position: relative; z-index: 2; }
.btn__glow {
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: linear-gradient(120deg, transparent 20%, rgba(255,255,255,.5) 50%, transparent 80%);
  transform: translateX(-130%);
  animation: sheen 4.2s var(--ease) infinite;
}
:root[dir="rtl"] .btn__glow { animation-name: sheenRtl; }
@keyframes sheenRtl { 0%,62%{transform:translateX(130%)} 100%{transform:translateX(-130%)} }

/* Earned after mastering the final stage. */
.btn.is-earned {
  box-shadow: 0 8px 30px rgba(34,211,238,.45), 0 0 0 2px rgba(251,191,36,.55),
              0 2px 0 rgba(255,255,255,.22) inset;
}
@keyframes sheen { 0%,62%{transform:translateX(-130%)} 100%{transform:translateX(130%)} }
.btn__arrow { transition: transform var(--mid) var(--spring); }
:root[dir="rtl"] .btn__arrow { transform: scaleX(-1); }
.btn--primary:hover .btn__arrow { transform: translateX(4px); }
:root[dir="rtl"] .btn--primary:hover .btn__arrow { transform: scaleX(-1) translateX(4px); }

.btn--ghost {
  background: rgba(20, 24, 37, .68);
  border: 1px solid var(--line-strong);
  color: var(--ink);
}
.btn--ghost:hover { background: var(--surface-2); border-color: rgba(255,255,255,.3); }

.btn--sm    { min-block-size: 42px; padding: 10px 18px; font-size: var(--t-xs); }
.btn--block { inline-size: 100%; }

.btn__spinner {
  position: absolute; inset-block-start: 50%; inset-inline-start: 50%;
  inline-size: 18px; block-size: 18px;
  margin: -9px 0 0 -9px;
  border: 2px solid rgba(4,6,12,.28);
  border-block-start-color: #04060c;
  border-radius: 50%;
  opacity: 0;
  animation: spin .68s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.btn.is-busy .btn__label,
.btn.is-busy .btn__arrow { opacity: 0; }
.btn.is-busy .btn__spinner { opacity: 1; }

/* ---------------------------------------------------------------- STATS -- */
.stats {
  position: fixed;
  inset-block-end: calc(var(--safe-b) + var(--s4));
  inset-inline-end: calc(var(--safe-r) + var(--s4));
  z-index: 30;
  display: none;
  gap: var(--s6);
  padding: var(--s4) var(--s5);
  border-radius: var(--r-lg);
  background: rgba(10,12,20,.5);
  border: 1px solid var(--line);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat__num {
  font-size: var(--t-xl); font-weight: 700; line-height: 1.05;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}
.stat__num[data-accent="a"] { color: var(--ember); }
.stat__num[data-accent="b"] { color: var(--cyan);  white-space: nowrap; }
.stat__num[data-accent="c"] { color: var(--mint);  }
.stat__label {
  font-size: var(--t-xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: .13em;
}
:root[dir="rtl"] .stat__label { letter-spacing: .04em; }

/* ----------------------------------------------------------------- DOCK -- */
/* ------------------------------------------------------------------ DOCK --
   Five coloured tabs that rise out of a single control in the bottom-left
   corner, one after another. Collapsed, the screen is clean; expanded, the
   sections are one tap away and the sphere can bounce off them. */
.dock {
  position: fixed;
  inset-block-end: calc(var(--safe-b) + var(--s3));
  inset-inline-start: calc(var(--s4) + var(--safe-l));
  z-index: 35;
  display: block;
}

/* Absolutely positioned above the control, so a collapsed stack reserves no
   height at all: the sky reaches down to the toggle, and opening the tabs
   never nudges the layout — or the world — by a pixel. */
.dock__stack {
  position: absolute;
  inset-block-end: 100%;
  inset-inline-start: 0;
  margin-block-end: var(--s2);
  display: flex;
  flex-direction: column;
  /* stretch, not flex-start: every tab takes the width of the longest label,
     so the column is a clean edge — without any of them carrying a fixed
     width full of empty colour. */
  align-items: stretch;
  /* --dock-w is measured across BOTH languages by ui.js, so the block is the
     same size whichever one is showing. max-content is the fallback for the
     instant before that measurement lands. */
  inline-size: var(--dock-w, max-content);
  gap: 5px;
}

/* Off-screen ruler used for that measurement. */
.dock-probe {
  position: absolute;
  visibility: hidden;
  white-space: nowrap;
  inset-block-start: -9999px;
  inset-inline-start: -9999px;
  pointer-events: none;
}

/* The stagger index used to be an inline style attribute on each button —
   and the CSP (style-src 'self', deliberately without 'unsafe-inline')
   BLOCKS inline style attributes, so in production every --i read as its
   fallback 0 and the cascade collapsed into one simultaneous pop. The index
   comes from the stylesheet now, which the CSP has no opinion about, and the
   entrance stagger actually happens on the live site. Top button first. */
.dock__stack .dock__btn:nth-child(1) { --i: 5; }
.dock__stack .dock__btn:nth-child(2) { --i: 4; }
.dock__stack .dock__btn:nth-child(3) { --i: 3; }
.dock__stack .dock__btn:nth-child(4) { --i: 2; }
.dock__stack .dock__btn:nth-child(5) { --i: 1; }
.dock__stack .dock__btn:nth-child(6) { --i: 0; }

.dock__btn {
  --tint: 139, 92, 246;
  flex: 0 0 auto;
  position: relative;
  min-block-size: 27px;
  /* Tight to the word. These are one- and two-word labels, so the box is
     sized to the text rather than the text floating inside a box. */
  padding: 5px 9px;
  border-radius: var(--r-full);
  /* Translucent rather than solid: the tabs sit in the sky instead of on it,
     and the stars stay faintly visible through them. */
  background: linear-gradient(135deg, rgba(var(--tint), .40) 0%, rgba(var(--tint), .24) 100%);
  border: 1px solid rgba(var(--tint), .55);
  backdrop-filter: blur(11px) saturate(1.3);
  -webkit-backdrop-filter: blur(11px) saturate(1.3);
  color: #fff;
  font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .04em;
  text-align: center;
  white-space: nowrap;
  box-shadow: 0 4px 16px rgba(var(--tint), .18), inset 0 1px 0 rgba(255,255,255,.16);
  touch-action: manipulation;

  /* Collapsed state. Transform + opacity only, so the reveal runs on the
     compositor and never disturbs layout or the physics loop. */
  opacity: 0;
  transform: translateY(14px) scale(.86);
  pointer-events: none;
  transition:
    opacity .34s var(--ease) calc(var(--i, 0) * 45ms),
    transform .44s var(--spring) calc(var(--i, 0) * 45ms),
    box-shadow var(--fast) var(--ease);
}
:root[dir="rtl"] .dock__btn { letter-spacing: 0; }

.dock.is-open .dock__btn {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}
/* Closing runs in reverse: the furthest tab folds away first. */
.dock:not(.is-open) .dock__btn {
  transition-delay: calc((5 - var(--i, 0)) * 28ms);
}

/* The visible pill is 32px, but the target you can hit is 44 — the size a
   thumb actually needs. Shrinking a control should never mean shrinking the
   area that responds to it. The pseudo-element is invisible and is not what
   the sphere collides with, so the physics still matches what is drawn. */
.dock__btn::before {
  content: "";
  position: absolute;
  /* The pill is 27px; what answers a thumb is still 44. */
  inset: -8.5px -5px;
}
.dock__btn:active { transform: translateY(0) scale(.94); }
.dock__btn.is-active {
  background: linear-gradient(135deg, rgba(var(--tint), .70) 0%, rgba(var(--tint), .48) 100%);
  border-color: rgba(255,255,255,.55);
  box-shadow: 0 6px 24px rgba(var(--tint), .45);
}

.dock__btn:nth-child(1) { --tint: 139, 92, 246; }   /* violet  */
.dock__btn:nth-child(2) { --tint:  34, 211, 238; }   /* cyan    */
.dock__btn:nth-child(3) { --tint:  52, 211, 153; }   /* emerald */
.dock__btn:nth-child(4) { --tint: 251, 191,  36; }   /* amber   */
.dock__btn:nth-child(5) { --tint: 251, 113, 133; }   /* rose    */
.dock__btn:nth-child(6) { --tint: 129, 140, 248; }   /* indigo  */

/* ---- the control that opens them ---- */
.dock__toggle {
  position: relative;
  inline-size: 35px; block-size: 35px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: rgba(12,15,24,.82);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  box-shadow: var(--sh-md);
  touch-action: manipulation;
  transition: transform var(--fast) var(--spring), border-color var(--mid) var(--ease),
              box-shadow var(--mid) var(--ease);
}
.dock__toggle::before {
  content: "";
  position: absolute;
  inset: -5px;                     /* 45px target around a 35px control */
  border-radius: inherit;
}
.dock__toggle:active { transform: scale(.92); }
.dock.is-open .dock__toggle {
  border-color: rgba(34,211,238,.5);
  box-shadow: var(--sh-md), 0 0 22px rgba(34,211,238,.35);
}

/* Three lines fold into a cross. */
.dock__toggle svg > path {
  transform-origin: 50% 50%;
  transition: transform .38s var(--spring), opacity .2s var(--ease);
}
.dock.is-open .dock__l1 { transform: translateY(5px) rotate(45deg); }
.dock.is-open .dock__l2 { opacity: 0; transform: scaleX(.3); }
.dock.is-open .dock__l3 { transform: translateY(-5px) rotate(-45deg); }

/* A ring that leaves the button on every press. */
.dock__pulse {
  position: absolute; inset: 0;
  border-radius: inherit;
  border: 1.5px solid rgba(34,211,238,.75);
  opacity: 0;
  pointer-events: none;
}
.dock__toggle.is-pulsing .dock__pulse { animation: dockPulse .62s var(--ease-out); }
@keyframes dockPulse {
  0%   { opacity: .9; transform: scale(1); }
  100% { opacity: 0;  transform: scale(1.85); }
}

/* --------------------------------------------------------- MOTION GATE -- */
.motion-gate {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: var(--s5);
  background: rgba(2,3,6,.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: fadeIn var(--mid) var(--ease);
}
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
.motion-gate__card {
  inline-size: min(340px, 100%);
  padding: var(--s6) var(--s5);
  border-radius: var(--r-xl);
  background: var(--surface-solid);
  border: 1px solid var(--line-strong);
  box-shadow: var(--sh-lg);
  text-align: center;
  animation: riseIn .5s var(--ease-out);
}
@keyframes riseIn { from { opacity: 0; transform: translateY(22px) scale(.96) } to { opacity: 1; transform: none } }
.motion-gate__icon {
  inline-size: 58px; block-size: 58px;
  margin-inline: auto;
  margin-block-end: var(--s4);
  display: grid; place-items: center;
  border-radius: var(--r-lg);
  background: rgba(34,211,238,.12);
  border: 1px solid rgba(34,211,238,.28);
  color: var(--cyan);
  animation: tiltNudge 2.6s var(--ease) infinite;
}
@keyframes tiltNudge { 0%,100%{transform:rotate(-9deg)} 50%{transform:rotate(9deg)} }
.motion-gate__title { font-size: var(--t-lg); font-weight: 700; margin-block-end: var(--s2); }
.motion-gate__body  { font-size: var(--t-sm); color: var(--ink-soft); margin-block-end: var(--s5); }
.motion-gate__actions { display: flex; flex-direction: column; gap: var(--s2); }

/* ---------------------------------------------------------------- SHEET -- */
.sheet, .portal { position: fixed; inset: 0; z-index: 200; }
.sheet__scrim, .portal__scrim {
  position: absolute; inset: 0;
  background: rgba(2,3,6,.66);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity var(--mid) var(--ease);
}
.sheet.is-open .sheet__scrim,
.portal.is-open .portal__scrim { opacity: 1; }

.sheet__panel {
  position: absolute;
  inset-block-end: 0; inset-inline: 0;
  max-block-size: 86svh; max-block-size: 86dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding:
    var(--s4) calc(var(--s5) + var(--safe-r))
    calc(var(--s6) + var(--safe-b)) calc(var(--s5) + var(--safe-l));
  background: linear-gradient(180deg, rgba(14,16,26,.97) 0%, rgba(6,8,14,.99) 100%);
  border-start-start-radius: var(--r-xl);
  border-start-end-radius: var(--r-xl);
  border-block-start: 1px solid var(--line-strong);
  box-shadow: var(--sh-lg);
  transform: translateY(102%);
  transition: transform var(--slow) var(--ease-out);
}
.sheet.is-open .sheet__panel { transform: translateY(0); }

.sheet__grip {
  inline-size: 38px; block-size: 4px;
  margin: 0 auto var(--s5);
  border-radius: var(--r-full);
  background: var(--line-strong);
}
.sheet__close, .portal__close {
  position: absolute;
  inset-block-start: var(--s4);
  inset-inline-end: calc(var(--s4) + var(--safe-r));
  inline-size: 36px; block-size: 36px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  transition: background var(--fast), color var(--fast), transform var(--fast) var(--spring);
}
.sheet__close:hover, .portal__close:hover { background: var(--surface-2); color: var(--ink); }
.sheet__close:active, .portal__close:active { transform: scale(.9); }

/* Panel typography is scoped to the panel.
 *
 * A sheet is read differently from a page: it arrives over the content, it is
 * short, and on a phone it is read at arm's length in one sitting. Page-level
 * sizes are tuned for a headline competing with a starfield, and carried into
 * this dense box they read loud. These tokens step the panel down a notch and
 * cap the measure, without touching a single size elsewhere. */
.sheet__panel {
  --sheet-title: clamp(19px, 1.06rem + .49vw, 25px);
  --sheet-body:  clamp(12.5px, .747rem + .143vw, 14.5px);
  --sheet-lead:  1.62;
}

/* Markazi already carries a naskh size correction at page level (see the RTL
   scale above). Inside the panel that correction over-serves: the same words
   in Arabic ran visibly heavier and taller than the English, which is what
   made the panel feel crowded. Here the two languages are brought to parity —
   Arabic keeps a little extra height, which naskh genuinely needs, but not the
   extra bulk. */
:root[dir="rtl"] .sheet__panel {
  --sheet-title: clamp(20.5px, 1.14rem + .53vw, 27px);
  /* Exactly 1.10 × the English body at every width — the same clamp with every
     term multiplied, so the ratio holds across the whole range instead of
     drifting. The page scale carries 1.19 here; a panel of solid text does not
     need that much, and the extra leading above buys back the legibility. */
  --sheet-body:  clamp(13.75px, .822rem + .157vw, 15.95px);
  --sheet-lead:  1.72;
}

.sheet__title {
  font-size: var(--sheet-title); font-weight: 700;
  letter-spacing: -.02em;
  margin-block-end: var(--s3);
  padding-inline-end: 44px;
}
.sheet__body {
  font-size: var(--sheet-body);
  line-height: var(--sheet-lead);
  color: var(--ink-soft);
  /* Never let a line run the full width of a desktop monitor. */
  max-inline-size: 62ch;
}
.sheet__title, .sheet__foot { max-inline-size: 62ch; }
.sheet__body p { margin-block-end: var(--s3); }
.sheet__body ul { list-style: none; display: grid; gap: 7px; margin-block: var(--s3); }
.sheet__body li {
  position: relative;
  padding-inline-start: 22px;
  line-height: var(--sheet-lead);
}
.sheet__body li::before {
  content: "";
  position: absolute;
  inset-block-start: .62em;
  inset-inline-start: 4px;
  inline-size: 6px; block-size: 6px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 8px rgba(34,211,238,.7);
}
.sheet__body strong { color: var(--ink); font-weight: 600; }
.sheet__body .kv {
  display: grid; gap: 3px;
  padding: var(--s3) 0;
  border-block-end: 1px solid var(--line);
}
.sheet__body .kv:last-child { border-block-end: 0; }
.sheet__body .kv b { color: var(--ink-mute); font-size: calc(var(--sheet-body) * .84); text-transform: uppercase; letter-spacing: .12em; font-weight: 600; }
.sheet__foot { margin-block-start: var(--s5); }
.sheet__foot .btn { inline-size: 100%; }

/* Accent per section */
.sheet[data-tone="about"]    .sheet__title { color: var(--violet); }
.sheet[data-tone="why"]      .sheet__title { color: var(--cyan);   }
.sheet[data-tone="services"] .sheet__title { color: var(--mint);   }
.sheet[data-tone="tech"]     .sheet__title { color: var(--amber);  }
.sheet[data-tone="offices"]  .sheet__title { color: var(--rose);   }
.sheet[data-tone="work"]     .sheet__title { color: #818cf8;        }
.sheet[data-tone="about"]    .sheet__body li::before { background: var(--violet); box-shadow: 0 0 8px rgba(139,92,246,.7); }
.sheet[data-tone="services"] .sheet__body li::before { background: var(--mint);   box-shadow: 0 0 8px rgba(52,211,153,.7); }
.sheet[data-tone="tech"]     .sheet__body li::before { background: var(--amber);  box-shadow: 0 0 8px rgba(251,191,36,.7); }
.sheet[data-tone="offices"]  .sheet__body li::before { background: var(--rose);   box-shadow: 0 0 8px rgba(251,113,133,.7); }
.sheet[data-tone="work"]     .sheet__body li::before { background: #818cf8;        box-shadow: 0 0 8px rgba(129,140,248,.7); }

/* --------------------------------------------------------------- PORTAL -- */
.portal__panel {
  position: absolute;
  inset-block-end: 0; inset-inline: 0;
  max-block-size: 94svh; max-block-size: 94dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding:
    var(--s6) calc(var(--s5) + var(--safe-r))
    calc(var(--s6) + var(--safe-b)) calc(var(--s5) + var(--safe-l));
  background:
    radial-gradient(120% 62% at 50% 0%, rgba(34,211,238,.10) 0%, transparent 62%),
    linear-gradient(180deg, rgba(14,16,26,.98) 0%, rgba(5,7,12,1) 100%);
  border-start-start-radius: var(--r-xl);
  border-start-end-radius: var(--r-xl);
  border-block-start: 1px solid rgba(34,211,238,.28);
  box-shadow: 0 -20px 70px rgba(0,0,0,.6), 0 -1px 0 rgba(34,211,238,.2) inset;
  transform: translateY(102%);
  transition: transform var(--slow) var(--ease-out);
}
.portal.is-open .portal__panel { transform: translateY(0); }

.portal__head { text-align: center; margin-block-end: var(--s6); }
.portal__eyebrow {
  font-size: var(--t-xs);
  letter-spacing: .28em; text-transform: uppercase;
  color: var(--cyan);
  margin-block-end: var(--s2);
}
:root[dir="rtl"] .portal__eyebrow { letter-spacing: .08em; }
.portal__title {
  font-size: var(--t-2xl); font-weight: 700;
  letter-spacing: -.03em; line-height: 1.12;
  text-wrap: balance;
  margin-block-end: var(--s2);
}
:root[dir="rtl"] .portal__title { letter-spacing: 0; line-height: 1.35; }
.portal__sub { font-size: var(--t-sm); color: var(--ink-mute); text-wrap: balance; }

/* ----------------------------------------------------------------- FORM -- */
.form { display: grid; gap: var(--s5); max-inline-size: 620px; margin-inline: auto; }
.hp { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.field { display: grid; gap: 8px; min-inline-size: 0; }
.field-row { display: grid; gap: var(--s5); }

.field__label {
  font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-mute);
}
:root[dir="rtl"] .field__label { letter-spacing: .04em; }
.opt { text-transform: none; letter-spacing: 0; color: var(--ink-faint); font-weight: 400; }

.field__input {
  inline-size: 100%;
  min-block-size: 52px;
  padding: 14px 16px;
  border-radius: var(--r-md);
  background: rgba(255,255,255,.045);
  border: 1px solid var(--line);
  color: var(--ink);
  font-size: max(16px, var(--t-base)); /* ≥16px stops iOS zoom-on-focus */
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.field__input::placeholder {
  color: var(--ink-faint);
  /* The one surviving placeholder states accepted formats and runs long;
     let it shrink rather than truncate on a narrow phone. */
  font-size: .92em;
  opacity: 1;
}
.field__input:hover { border-color: var(--line-strong); }
.field__input:focus {
  outline: none;
  background: rgba(255,255,255,.07);
  border-color: rgba(34,211,238,.6);
  box-shadow: 0 0 0 4px rgba(34,211,238,.12);
}
.field__input--area { min-block-size: 118px; resize: vertical; line-height: 1.6; }
.field.is-invalid .field__input { border-color: rgba(251,113,133,.7); box-shadow: 0 0 0 4px rgba(251,113,133,.1); }

.field__meta { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.field__err {
  font-size: var(--t-xs); color: var(--rose);
  min-block-size: 1em;
  opacity: 0; transform: translateY(-3px);
  transition: opacity var(--fast), transform var(--fast);
}
.field.is-invalid .field__err { opacity: 1; transform: none; }
.counter { font-size: var(--t-xs); color: var(--ink-faint); font-variant-numeric: tabular-nums; margin-inline-start: auto; }

.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  min-block-size: 40px;
  padding: 9px 15px;
  border-radius: var(--r-full);
  background: rgba(255,255,255,.04);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  font-size: var(--t-xs); font-weight: 600;
  touch-action: manipulation;
  transition: transform var(--fast) var(--spring), background var(--fast), border-color var(--fast), color var(--fast);
}
.chip:hover  { background: var(--surface-2); color: var(--ink); }
.chip:active { transform: scale(.94); }
.chip[aria-checked="true"] {
  background: rgba(34,211,238,.14);
  border-color: rgba(34,211,238,.55);
  color: var(--cyan);
  box-shadow: 0 0 0 1px rgba(34,211,238,.2);
}

.form__note { font-size: var(--t-xs); color: var(--ink-faint); text-align: center; }

/* Success */
.portal__done { text-align: center; padding: var(--s7) 0 var(--s5); animation: riseIn .55s var(--ease-out); }
.portal__done-ring {
  inline-size: 82px; block-size: 82px;
  margin-inline: auto; margin-block-end: var(--s5);
  display: grid; place-items: center;
  border-radius: 50%;
  background: rgba(52,211,153,.12);
  border: 1px solid rgba(52,211,153,.4);
  color: var(--mint);
  box-shadow: 0 0 44px rgba(52,211,153,.28);
}
.portal__done-ring .tick { stroke-dasharray: 40; stroke-dashoffset: 40; animation: drawTick .55s .18s var(--ease-out) forwards; }
@keyframes drawTick { to { stroke-dashoffset: 0; } }
.portal__done h3 { font-size: var(--t-xl); font-weight: 700; margin-block-end: var(--s2); }
.portal__done p  { font-size: var(--t-sm); color: var(--ink-soft); margin-block-end: var(--s5); }

/* --------------------------------------------------------- REVEAL/TOAST -- */
.reveal {
  position: fixed;
  inset-block-start: 42%;
  inset-inline: 0;
  z-index: 60;
  display: grid; place-items: center;
  pointer-events: none;
  opacity: 0;
}
.reveal.is-on { animation: revealPop 2.1s var(--ease-out); }
.reveal span {
  padding: 11px 22px;
  border-radius: var(--r-full);
  background: rgba(11,14,23,.92);
  border: 1px solid rgba(34,211,238,.45);
  font-size: var(--t-sm); font-weight: 700;
  letter-spacing: .08em;
  box-shadow: var(--glow-cyan);
  text-align: center;
}
@keyframes revealPop {
  0%   { opacity: 0; transform: translateY(18px) scale(.86); }
  16%  { opacity: 1; transform: translateY(0) scale(1); }
  78%  { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-22px) scale(.96); }
}

.toast {
  position: fixed;
  inset-block-end: calc(var(--safe-b) + 86px);
  inset-inline: var(--s5);
  z-index: 300;
  margin-inline: auto;
  max-inline-size: 420px;
  padding: 13px 18px;
  border-radius: var(--r-md);
  background: rgba(10,12,20,.96);
  border: 1px solid var(--line-strong);
  box-shadow: var(--sh-md);
  font-size: var(--t-sm);
  text-align: center;
  transform: translateY(18px);
  opacity: 0;
  transition: transform var(--mid) var(--ease-out), opacity var(--mid) var(--ease);
}
.toast.is-on { transform: none; opacity: 1; }
.toast[data-kind="error"] { border-color: rgba(251,113,133,.55); color: #ffd9de; }
.toast[data-kind="ok"]    { border-color: rgba(52,211,153,.5);  color: #cdfbe8; }

.noscript {
  position: relative; z-index: 500;
  max-inline-size: 640px; margin: 20vh auto; padding: var(--s6);
  text-align: center;
}
.noscript h2 { font-size: var(--t-2xl); margin-block-end: var(--s3); }
.noscript p  { color: var(--ink-soft); margin-block-end: var(--s3); }

/* ================================================== PHONE LAYOUT (<720) ====
   A phone is not a small desktop. Below 720px the screen is reorganised so the
   centre of the display belongs to the experience and nothing else:

     · the primary action moves into the top bar as a compact pill
     · the five section buttons collapse into one menu
     · the stage indicator moves to the foot, out of the wordmark's way
     · the hero keeps only what earns its place

   Everything from 720px up is untouched.
   ========================================================================= */
@media (max-width: 719.98px) {

  /* The centre CTA pair is replaced by the pill in the top bar. */
  .cta-row { display: none; }

  .hud__center { display: none; }        /* stage chip is repositioned below */

  .hud__row { gap: var(--s2); }
  .hud__actions { margin-inline-start: auto; gap: 6px; }

  .hud__cta {
    min-block-size: 34px;
    padding: 8px 15px;
    font-size: var(--t-xs);
    letter-spacing: .03em;
  }
  .hud__cta .btn__glow { display: none; }   /* no sheen at this size — just noise */

  /* Arabic sets wider than Latin at the same nominal size, so the same pill
     reads noticeably heavier in the header. The PILL is trimmed 10% for Arabic
     only, so the two languages carry equal visual weight up there.
     The WORD inside it is a separate decision: shrinking the pill had dragged
     the label down with it until it read small for its box, so the label is
     given back 5% while the box — min-block-size and padding — is untouched.
     At 13.5px base that is 12.15px → 12.76px, which still clears the 31px
     height with room to spare, so the pill does not grow. */
  :root[dir="rtl"] .hud__cta {
    min-block-size: 31px;
    padding: 7px 13px;
    font-size: calc(var(--t-xs) * .945);
  }

  :root { --hud-control: 34px; }   /* the button AND the mark, together */
  .lang__opt { padding: 3px 6px; }

  .wordmark { gap: 8px; }
  .wordmark__logo { height: 25px; }

  /* The stage indicator sits at the foot of the screen, where the section
     buttons used to be: visible, but no longer competing with the logo. */
  .stage-chip {
    position: fixed;
    inset-block-end: calc(var(--safe-b) + 20px);
    inset-inline-end: calc(var(--s4) + var(--safe-r));
    z-index: 36;
    inline-size: fit-content;
    padding: 6px 13px;
  }

  /* Give the hero less bulk so the play field below it can breathe. */
  .stage {
    justify-content: flex-start;
    gap: var(--s3);
    padding-block-start: calc(var(--hud-h) + var(--safe-t) + var(--s9));
  }
  .eyebrow { font-size: var(--t-xs); letter-spacing: .18em; }
  .tagline { font-size: var(--t-base); max-inline-size: 34ch; }
  :root[dir="ltr"] .tagline { font-size: calc(var(--t-base) * .85); }

  :root { --hud-h: 92px; }
}

/* Very short phones (SE, landscape-ish): drop the ticker and eyebrow too. */
@media (max-width: 719.98px) and (max-height: 700px) {
  .ticker, .eyebrow { display: none; }
  :root { --hud-h: 64px; }
}

/* ====================================================== RESPONSIVE UP ==== */

/* Tablet and up: stats appear, dock centers, tighter hero rhythm */
@media (min-width: 720px) {
  :root { --hud-h: 116px; }
  .stats { display: flex; }
  /* Desktop keeps the full centre composition, so the phone-only affordances
     stand down: the action lives in the hero, the sections in the dock. */
  .hud__cta, .hud__menu { display: none; }
  .stage { gap: var(--s5); }
  .field-row { grid-template-columns: 1fr 1fr; }
  .motion-gate__actions { flex-direction: row; justify-content: center; }
  /* Centre with auto margins, never translateX — a -50% shift lands in the
     wrong place once the document direction flips to RTL. */
  .sheet__panel, .portal__panel {
    inset-inline: 0;
    margin-inline: auto;
    inline-size: min(680px, 94vw);
    inset-block-end: 3vh;
    transform: translateY(102%);
    border-radius: var(--r-xl);
    border: 1px solid var(--line-strong);
    max-block-size: 88dvh;
  }
  .sheet.is-open .sheet__panel,
  .portal.is-open .portal__panel { transform: translateY(0); }
  .portal__panel { inline-size: min(760px, 94vw); border-color: rgba(34,211,238,.28); }
  .sheet__grip { display: none; }
}

/* Desktop: side-anchored dock, bigger canvas presence */
@media (min-width: 1080px) {
  .stage { gap: var(--s5); padding-block-end: calc(var(--s9) + var(--s6)); }
  .tagline { font-size: var(--t-lg); }
  :root[dir="ltr"] .tagline { font-size: calc(var(--t-lg) * .85); }
  .dock__btn { min-block-size: 31px; padding: 7px 13px; font-size: var(--t-sm); }
  .dock__toggle { inline-size: 37px; block-size: 37px; }
}

/* Ultra-wide / large displays: cap the measure, let the cosmos breathe */
@media (min-width: 1800px) {
  /* A very large display can carry a bigger wordmark than the shared ramp
     tops out at — but not the runaway 190px the old rule allowed. */
  .tagline { max-inline-size: 52ch; }
  .stats { gap: var(--s8); padding: var(--s5) var(--s6); }
}

/* Short landscape phones: collapse the hero, keep the game playable */
@media (max-height: 560px) and (orientation: landscape) {
  :root { --hud-h: 76px; }
  .stage { justify-content: flex-start; gap: var(--s2); padding-block-start: calc(var(--hud-h) + var(--s3)); }
  .eyebrow, .ticker { display: none; }
  .tagline { font-size: var(--t-sm); max-inline-size: 60ch; }
  .stats   { display: none; }
}

/* ------------------------------------------------------------ A11Y PREFS -- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .cosmos { opacity: .35; }

}

@media (prefers-contrast: more) {
  :root {
    --ink-soft: rgba(255,255,255,.92);
    --ink-mute: rgba(255,255,255,.78);
    --ink-faint: rgba(255,255,255,.6);
    --line: rgba(255,255,255,.3);
    --line-strong: rgba(255,255,255,.5);
    --surface-solid: rgba(0,0,0,.96);
  }
  .cosmos { opacity: .3; }
}

/* Devices that cannot hover: no hover-only affordances */
@media (hover: none) {
  .btn:hover, .chip:hover, .dock__btn:hover, .icon-btn:hover { background: inherit; }
  .btn--primary:hover { background: var(--brand-grad); }
}



/* --------------------------------------------------- SHEET CONTENT REVEAL --
   The panel slides, then its contents arrive a beat later, in order. Cheap
   (transform + opacity only) and it makes the sheet feel authored rather
   than simply toggled. */
/* The content is VISIBLE BY DEFAULT and the stagger is layered on top.
   It previously started at opacity 0 and depended on the animation running
   to become readable — so anywhere animations are suspended (iOS Low Power
   Mode is the common one) the panel opened completely blank. A decorative
   entrance must never be the thing that makes content exist.

   The animation now runs FROM the hidden state TO the natural one with no
   fill-mode, so if it never plays, nothing was ever hidden. */
.sheet.is-open .sheet__title,
.sheet.is-open .sheet__body > *,
.sheet.is-open .sheet__foot {
  animation: sheetIn .5s var(--ease-out) backwards;
}
.sheet.is-open .sheet__title                   { animation-delay: .10s; }
.sheet.is-open .sheet__body > *:nth-child(1)   { animation-delay: .16s; }
.sheet.is-open .sheet__body > *:nth-child(2)   { animation-delay: .21s; }
.sheet.is-open .sheet__body > *:nth-child(3)   { animation-delay: .26s; }
.sheet.is-open .sheet__body > *:nth-child(4)   { animation-delay: .31s; }
.sheet.is-open .sheet__body > *:nth-child(n+5) { animation-delay: .36s; }
.sheet.is-open .sheet__foot                    { animation-delay: .40s; }
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------- PORTAL NOTICES --
   Two quiet lines that appear only when they have something to say: one
   acknowledging a previous message, one confirming that a failed send did
   not lose the visitor's words. Neither ever prints an address. */
.portal__note {
  margin-block-start: var(--s3);
  padding: 10px 14px;
  border-radius: var(--r-md);
  font-size: var(--t-xs);
  line-height: 1.6;
  text-align: center;
  animation: noteIn .45s var(--ease-out);
}
@keyframes noteIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

.portal__note--warm {
  background: rgba(52, 211, 153, .10);
  border: 1px solid rgba(52, 211, 153, .30);
  color: #a9f2d4;
}
.portal__note--kept {
  max-inline-size: 620px;
  margin-inline: auto;
  background: rgba(251, 191, 36, .10);
  border: 1px solid rgba(251, 191, 36, .32);
  color: #ffe6a6;
}

/* ------------------------------------------------------------- PRINTING --
   A page built for a black sky is unreadable on paper. Printing gives the
   mark, the name and the essentials in ink — nothing that only makes sense
   lit up, and no address anywhere. */
@media print {
  .cosmos, .cosmos-veil, .dock, .stage-chip, .hud__actions,
  .ticker, .boot, .toast, .reveal, .motion-gate, .stats { display: none !important; }

  html, body { background: #fff !important; color: #111 !important; }
  body { min-height: auto; }

  .hud { position: static; background: none; padding: 0; }
  .stage {
    min-height: auto;
    padding: 0;
    display: block;
    text-align: start;
  }
  .wordmark { color: #111; }
  .eyebrow { color: #555; }
  .tagline { color: #111; max-inline-size: none; }
  .cta-row { display: none; }

  .sheet, .portal { position: static; }
  .sheet__panel, .portal__panel {
    position: static; transform: none; box-shadow: none;
    background: none; border: 0; max-block-size: none; padding: 0;
  }
  .sheet__scrim, .portal__scrim, .sheet__close, .portal__close { display: none; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; color: #555; }
}

/* ------------------------------------------------------- ADAPTIVE QUALITY --
   cosmos.js measures real frame times and stamps the verdict on <html>.
   When a device is struggling, the blurred surfaces — by far the most
   expensive thing on the page — become plain translucent fills instead.
   Visually near-identical over a dark sky; dramatically cheaper to compose. */
:root[data-quality="low"] .sheet__scrim,
:root[data-quality="low"] .portal__scrim,
:root[data-quality="low"] .motion-gate,
:root[data-quality="low"] .dock__btn,
:root[data-quality="low"] .stats {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
:root[data-quality="low"] .dock__btn {
  background: linear-gradient(135deg, rgba(var(--tint), .72) 0%, rgba(var(--tint), .52) 100%);
}
:root[data-quality="low"] .sheet__scrim,
:root[data-quality="low"] .portal__scrim { background: rgba(2, 3, 6, .86); }

/* ------------------------------------------------------------- OUR WORK --
   A list of live sites, each carrying a small badge that states exactly why
   it appears here. The badge is a fact, not a disclaimer — so it is styled
   with the same care as everything else and never looks like ad furniture. */
/* Every size in this panel is derived from --sheet-body, so the panel scales
   as one family and the Arabic and English versions stay in step. */
.work-note {
  margin-block-end: var(--s4);
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .035);
  border-inline-start: 2px solid var(--cyan);
  font-size: var(--sheet-body);
  line-height: var(--sheet-lead);
  color: var(--ink-soft);
}

.work-list { display: grid; gap: 8px; }

.work-item {
  display: block;
  padding: 12px 14px;
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .035);
  border: 1px solid var(--line);
  color: var(--ink);
  text-decoration: none;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              transform var(--fast) var(--spring);
}
.work-item:hover {
  background: rgba(255, 255, 255, .07);
  border-color: rgba(255, 255, 255, .28);
}
.work-item:active { transform: scale(.985); }

.work-item__top {
  display: flex; align-items: center; gap: var(--s3);
  flex-wrap: wrap;
  margin-block-end: 4px;
}
.work-item__name {
  font-size: calc(var(--sheet-body) * 1.1);
  font-weight: 600;
  letter-spacing: .01em;
  /* Domains are Latin whichever way the page runs. */
  direction: ltr;
  unicode-bidi: isolate;
}
.work-item__go {
  margin-inline-start: auto;
  color: var(--ink-faint);
  font-size: calc(var(--sheet-body) * 1.3);
  line-height: 1;
  transition: transform var(--mid) var(--spring), color var(--fast);
}
:root[dir="rtl"] .work-item__go { transform: scaleX(-1); }
.work-item:hover .work-item__go { color: var(--cyan); transform: translateX(3px); }
:root[dir="rtl"] .work-item:hover .work-item__go { transform: scaleX(-1) translateX(3px); }

.work-item__desc { font-size: calc(var(--sheet-body) * .9); color: var(--ink-mute); line-height: 1.55; }

/* ---- badges ---- */
.work-badge {
  --tone: 148, 163, 184;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  border-radius: var(--r-full);
  background: rgba(var(--tone), .14);
  border: 1px solid rgba(var(--tone), .40);
  color: rgb(var(--tone));
  font-size: calc(var(--sheet-body) * .82);
  font-weight: 600;
  letter-spacing: .06em;
  white-space: nowrap;
  cursor: help;
}
:root[dir="rtl"] .work-badge { letter-spacing: 0; }
.work-badge::before {
  content: "";
  inline-size: 5px; block-size: 5px;
  border-radius: 50%;
  background: currentColor;
}
.work-badge[data-kind="sponsored"] { --tone: 251, 191, 36; }
.work-badge[data-kind="partner"]   { --tone: 34, 211, 238; }
.work-badge[data-kind="probono"]   { --tone: 52, 211, 153; }

.work-legend {
  margin-block-start: var(--s4);
  padding-block-start: var(--s3);
  border-block-start: 1px solid var(--line);
  display: grid; gap: 8px;
}
.work-legend__row {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: calc(var(--sheet-body) * .88);
  color: var(--ink-mute);
  line-height: 1.6;
}
.work-legend__row .work-badge { flex: 0 0 auto; cursor: default; }
