/* uvu.css — GENERATED by _backend/build/build.py (v18, 2026-09-19).
   Do not hand-edit this file: the next rebuild overwrites it.
   Edit STYLESHEET in the generator instead. */
/* Self-hosted, subset to the characters this site actually sets.
   GENERATED by _backend/tools/fonts_build.py — do not hand-edit.
   272128 bytes fetched from Google, 171384 bytes shipped. */
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/plexsans-400.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/plexsans-500.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/plexsans-600.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"IBM Plex Sans";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/plexsans-700.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/plexmono-400.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"IBM Plex Mono";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/plexmono-500.woff2") format("woff2");unicode-range:U+0000-058F,U+2000-206F,U+2190-21BB}
@font-face{font-family:"Readex Pro";font-style:normal;font-weight:400;font-display:swap;src:url("/assets/fonts/readex-400.woff2") format("woff2");unicode-range:U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF,U+060C,U+061B,U+061F}
@font-face{font-family:"Readex Pro";font-style:normal;font-weight:500;font-display:swap;src:url("/assets/fonts/readex-500.woff2") format("woff2");unicode-range:U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF,U+060C,U+061B,U+061F}
@font-face{font-family:"Readex Pro";font-style:normal;font-weight:600;font-display:swap;src:url("/assets/fonts/readex-600.woff2") format("woff2");unicode-range:U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF,U+060C,U+061B,U+061F}
@font-face{font-family:"Readex Pro";font-style:normal;font-weight:700;font-display:swap;src:url("/assets/fonts/readex-700.woff2") format("woff2");unicode-range:U+0600-06FF,U+0750-077F,U+FB50-FDFF,U+FE70-FEFF,U+060C,U+061B,U+061F}
/* UVU — industrial edge sensing-compute. Trilingual, Arabic-first (RTL), mobile-first.
   Authored fresh for v10. Not ported from any prior site's stylesheet.
   Light palette lives on bare :root; dark tokens are redefined under the two dark guards.
   Every colour has a light definition on :root so nothing is defined only inside a media query.

   v10.1 visual pass — no page copy changed. Everything here is presentation:
   a Saudi-institutional geometric motif (eight-point khatim lattice), an instrument-grade
   surface language (hairline brackets, measured gradients, a sweep figure in the hero),
   and motion that is calm, short and fully switched off under prefers-reduced-motion. */

/* Saudi-institutional palette: national green as the primary, gold as the secondary
   accent, on a clean near-white ground. --accent = green (primary), --teal = gold
   (secondary), --sand = soft green tint. Variable names are kept from the first build so
   every rule still resolves; only the role colours changed. */
:root{
  color-scheme: light;
  /* ---- light palette (default) ---- */
  --ink:#12241b;            /* primary text — deep green-ink */
  --ink-soft:#3d4f45;       /* secondary text */
  --ink-faint:#68796f;      /* captions, meta */
  --bg:#f4f7f4;             /* page ground — clean near-white, green hint */
  --bg-deep:#e8eee9;        /* the ground one step down, for layered fades */
  --panel:#ffffff;          /* cards, tables */
  --panel-2:#eef2ee;        /* alt panel, code, placeholders */
  --panel-glass:rgba(255,255,255,.78);
  --line:#d7e0d9;           /* hairlines, borders */
  --line-strong:#b4c3b8;
  --accent:#006C35;         /* Saudi green — primary: links, CTAs, marks */
  --accent-ink:#0a5a32;     /* deeper green for text/hover on light */
  --accent-lift:#00894a;    /* brighter green — gradient head, hover fill */
  --accent-deep:#014e27;    /* darkest green — gradient foot */
  /* The CTA fill is its own token set, not --accent-lift etc., because those are also the
     hover fills for links and chips where the requirement is different. v10 put white on
     a gradient whose head measured 4.49:1 in light and 1.92:1 in dark, against 4.5:1.
     Measured now — light, white on fill: 6.13 / 6.57 / 9.90 : 1. */
  --cta-from:#00713c; --cta-mid:#006C35; --cta-to:#014e27; --cta-ink:#ffffff;
  --line-ui:#74897c;        /* borders on controls — 1.4.11 wants 3:1, not --line's 1.35 */
  --teal:#9a6a1c;           /* gold — secondary: kickers, status, accents */
  --teal-soft:#f2e9d6;
  --teal-lift:#c08b2c;
  --sand:#e3efe7;           /* soft green fill — active nav, chips */
  --open:#a3341f;           /* "open" / defect / today-zero emphasis */
  --open-soft:#f6e3dd;
  --focus:#006C35;
  --glow:rgba(0,108,53,.20);
  --glow-soft:rgba(0,108,53,.10);
  --motif-opacity:.055;     /* the khatim lattice, barely there */
  --hero-veil:74%;          /* how much ground is held over the hero video */
  --sweep-opacity:.085;
  --shadow:0 1px 2px rgba(18,36,27,.05), 0 8px 24px rgba(18,36,27,.06);
  --shadow-lift:0 2px 4px rgba(18,36,27,.06), 0 18px 44px rgba(18,36,27,.12);
  --radius:16px;
  --radius-s:10px;
  --maxw:1080px;
  --fs-arabic:1.02em;       /* Arabic parity with Readex Pro's larger x-height */
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ff-latin:"IBM Plex Sans","Segoe UI",system-ui,-apple-system,Roboto,Helvetica,Arial,sans-serif;
  /* Readex Pro is subset to Arabic only, so Latin runs inside Arabic sentences — UVU,
     FMCW, CFAR, uvu.com.tr — fall through to Plex Sans by unicode-range rather than to
     whatever the OS happens to offer. This is also what removes the 31,392-byte Readex
     latin face that v10 downloaded to set about seventy characters. */
  --ff-arabic:"Readex Pro","IBM Plex Sans","Tajawal","Noto Naskh Arabic","Segoe UI",Tahoma,system-ui,sans-serif;
  --ff-mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* eight-point khatim lattice, drawn once and painted through a mask so a single tile
     serves both themes and takes its colour from --accent. */
  --motif:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='80' height='80' viewBox='0 0 80 80'%3E%3Cg fill='none' stroke='%23000' stroke-width='1.15'%3E%3Crect x='22' y='22' width='36' height='36' rx='2'/%3E%3Crect x='22' y='22' width='36' height='36' rx='2' transform='rotate(45 40 40)'/%3E%3Crect x='-18' y='-18' width='36' height='36' rx='2'/%3E%3Crect x='-18' y='-18' width='36' height='36' rx='2' transform='rotate(45 0 0)'/%3E%3Crect x='62' y='-18' width='36' height='36' rx='2'/%3E%3Crect x='62' y='-18' width='36' height='36' rx='2' transform='rotate(45 80 0)'/%3E%3Crect x='-18' y='62' width='36' height='36' rx='2'/%3E%3Crect x='-18' y='62' width='36' height='36' rx='2' transform='rotate(45 0 80)'/%3E%3Crect x='62' y='62' width='36' height='36' rx='2'/%3E%3Crect x='62' y='62' width='36' height='36' rx='2' transform='rotate(45 80 80)'/%3E%3C/g%3E%3C/svg%3E");
}
:root:not([data-theme="light"]){
  @media (prefers-color-scheme: dark){
    color-scheme: dark;
    --ink:#e9f1ec; --ink-soft:#aec1b5; --ink-faint:#85988c;
    --bg:#08130e; --bg-deep:#050d09; --panel:#10201a; --panel-2:#0c1a14;
    --panel-glass:rgba(12,26,20,.72);
    --line:#213329; --line-strong:#31493a;
    --accent:#3fb779; --accent-ink:#5fca90; --accent-lift:#57d195; --accent-deep:#1d7a4d;
    /* dark ground: dark ink ON the bright fill rather than white through it.
       Measured 9.87 / 7.44 / 5.51 : 1. */
    --cta-from:#57d195; --cta-mid:#3fb779; --cta-to:#2b9d66; --cta-ink:#08130e;
    --line-ui:#5a7265;
    /* dark ground: dark ink ON the bright fill rather than white through it.
     Measured 9.87 / 7.44 / 5.51 : 1. */
  --cta-from:#57d195; --cta-mid:#3fb779; --cta-to:#2b9d66; --cta-ink:#08130e;
  --line-ui:#5a7265;
  --teal:#e0b364; --teal-soft:#221a0c; --teal-lift:#f0c87f;
    --sand:#12291d; --open:#f08a6f; --open-soft:#2a1712;
    --focus:#3fb779;
    --glow:rgba(63,183,121,.26);
    --glow-soft:rgba(63,183,121,.12);
    --motif-opacity:.075;
    --sweep-opacity:.12;
    --hero-veil:68%;
    --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px rgba(0,0,0,.4);
    --shadow-lift:0 2px 6px rgba(0,0,0,.45), 0 22px 52px rgba(0,0,0,.55);
  }
}
:root[data-theme="dark"]{
  color-scheme: dark;
  --ink:#e9f1ec; --ink-soft:#aec1b5; --ink-faint:#85988c;
  --bg:#08130e; --bg-deep:#050d09; --panel:#10201a; --panel-2:#0c1a14;
  --panel-glass:rgba(12,26,20,.72);
  --line:#213329; --line-strong:#31493a;
  --accent:#3fb779; --accent-ink:#5fca90; --accent-lift:#57d195; --accent-deep:#1d7a4d;
  /* Same CTA / control tokens as the prefers-color-scheme dark branch. Without
     these, an explicit dark toggle kept the light-theme fill (white type on a
     near-white button). C3 v14. */
  --cta-from:#57d195; --cta-mid:#3fb779; --cta-to:#2b9d66; --cta-ink:#08130e;
  --line-ui:#5a7265;
  --teal:#e0b364; --teal-soft:#221a0c; --teal-lift:#f0c87f;
  --sand:#12291d; --open:#f08a6f; --open-soft:#2a1712;
  --focus:#3fb779;
  --glow:rgba(63,183,121,.26);
  --glow-soft:rgba(63,183,121,.12);
  --motif-opacity:.075;
  --sweep-opacity:.12;
  --hero-veil:68%;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px rgba(0,0,0,.4);
  --shadow-lift:0 2px 6px rgba(0,0,0,.45), 0 22px 52px rgba(0,0,0,.55);
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;overflow-x:clip;max-width:100%;scroll-behavior:smooth;
  scroll-padding-block-start:124px}
body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--ff-latin);
  font-size:17px; line-height:1.65;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  font-feature-settings:"kern" 1,"liga" 1;
  overflow-x:clip;                   /* the page body never scrolls horizontally */
  max-width:100%;
}
/* a whole-page tint swap should read as one smooth move, not a flash of repainting parts */
html.theme-shift *,html.theme-shift *::before,html.theme-shift *::after{
  transition:background-color .32s var(--ease),border-color .32s var(--ease),
             color .32s var(--ease),fill .32s var(--ease) !important}
/* Arabic typography.
   In v10 every Arabic string sat inside <span lang="ar">, so a bare [lang="ar"] selector
   matched the text directly. v11 declares the language once, on <html> — and `body` sets
   font-family explicitly, which blocks inheritance from the root. So the Arabic face has
   to be applied AT body, not above it. Without this the Arabic pages set Arabic text in a
   Latin-subset face and the hero renders as empty space.
   The bare [lang="ar"] rule is kept for a genuinely inline Arabic phrase inside an LTR
   page, which is what that selector is actually for. */
html[lang="ar"] body{font-family:var(--ff-arabic); font-size:var(--fs-arabic); line-height:1.9;}
[lang="ar"]{font-family:var(--ff-arabic);}

/* Positive tracking pulls a cursive script apart — the connecting strokes stretch and the
   word stops reading as one. Measured on v10 at the two smallest sizes: +1.10px at 9.3px
   on the brand line and +1.41px at 12.0px on .tag, about 12% tracking. Latin keeps its
   tracking; Arabic does not get any. */
html[lang="ar"] .brand small,
html[lang="ar"] .tag,
html[lang="ar"] .kicker{letter-spacing:normal}
html[lang="ar"] h1,html[lang="ar"] h2{letter-spacing:normal}
[dir="rtl"] body,[dir="rtl"]{text-align:start}
::selection{background:var(--glow);color:var(--ink)}

img{max-width:100%;height:auto;display:block}
a{color:var(--accent-ink);text-underline-offset:.18em;text-decoration-thickness:.06em;
  transition:color .18s var(--ease)}
a:hover{color:var(--accent)}
:focus-visible{outline:3px solid var(--focus);outline-offset:3px;border-radius:6px}

/* ---------- language ---------- */
/* v10 kept all three languages in every document and hid two of them here. v11 renders
   one language per URL, so there is nothing left to hide — the rules are gone rather than
   left inert, because an inert display:none on span[lang] is a trap for the next editor
   who adds a legitimately-tagged foreign phrase. */
/* v10 crossfaded the body on a language swap. There is no in-page swap any more — a
   language change is a navigation — so the transition and its .lang-shift class are gone. */

/* ---------- layout ---------- */
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:20px;width:100%;min-width:0}
main{display:block}
section{padding-block:clamp(38px,7vw,76px);position:relative;--band:var(--bg)}
/* Most pages here are long runs of prose with almost no objects in them. A faint alternating
   ground gives the reader the shape of the page — where one part ends and the next begins —
   without adding a single word. The tint is a few units off the page ground, deliberately. */
main section:not(.hero):not(.pagehero){background:var(--band)}
main section:not(.hero):not(.pagehero):nth-of-type(even){
  --band:color-mix(in srgb,var(--panel-2) 60%,var(--bg))}
/* hairline rule between sections: it fades at both ends and carries a small khatim node,
   which reads far softer than a full-bleed border. */
section+section{border-top:0}
section+section::before{content:"";position:absolute;inset-inline:0;inset-block-start:0;height:1px;
  background:linear-gradient(90deg,transparent,var(--line-strong) 16%,var(--line-strong) 84%,transparent);
  opacity:.85;pointer-events:none}
section+section::after{content:"";position:absolute;inset-block-start:-5px;inset-inline-start:50%;
  width:10px;height:10px;transform:translateX(-50%) rotate(45deg);background:var(--band);
  border:1px solid var(--line-strong);border-radius:2px;pointer-events:none}
/* Skip link: move it vertically off-screen (not to an inline edge, which flips in RTL
   and causes horizontal overflow). Stays within horizontal bounds in both directions. */
.skip{position:absolute;top:-64px;inset-inline-start:12px;background:var(--accent);color:#fff;
  padding:10px 16px;z-index:80;border-radius:0 0 10px 10px;transition:top .18s var(--ease-out);
  box-shadow:var(--shadow-lift)}
.skip:focus{top:0}

/* ---------- reading-progress rail (built by the script; absent without JS) ---------- */
/* a flex row rather than a plain block: the fill then starts from the inline edge the page
   actually reads from, right in Arabic and left in the two Latin scripts, with no RTL branch. */
.progress-rail{position:fixed;inset-block-start:0;inset-inline:0;height:3px;z-index:70;
  pointer-events:none;background:transparent;display:flex}
.progress-rail i{height:100%;width:0;flex:0 0 auto;
  background:linear-gradient(to right,var(--accent),var(--accent-lift) 45%,var(--teal-lift));
  box-shadow:0 0 10px var(--glow)}

/* ---------- header ---------- */
/* No backdrop-filter below 900px: a filter on the header would become the containing block
   for the position:fixed nav drawer and trap it inside the bar. Opaque bg there instead. */
header{position:sticky;top:0;z-index:50;background:var(--bg);border-bottom:1px solid var(--line);
  transition:box-shadow .3s var(--ease),border-color .3s var(--ease),background-color .3s var(--ease)}
header.is-stuck{box-shadow:0 1px 0 var(--line),0 10px 30px -18px rgba(0,0,0,.45);border-bottom-color:transparent}
@media(min-width:900px){
  header{background:var(--panel-glass);backdrop-filter:saturate(1.6) blur(14px);
    -webkit-backdrop-filter:saturate(1.6) blur(14px)}
}
.bar{display:flex;align-items:center;gap:14px;min-height:62px;transition:min-height .3s var(--ease)}
header.is-stuck .bar{min-height:56px}
.brand{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--ink);font-weight:700;
  flex:0 0 auto;border-radius:12px}
/* Logo badge: crop the tagline strip (object-position top), contain the dark ground as a
   rounded badge so it sits cleanly on light or dark headers. */
.brand .logo{width:56px;height:44px;flex:0 0 auto;border-radius:10px;object-fit:cover;
  object-position:top;background:#0a1611;border:1px solid var(--line);
  transition:transform .4s var(--ease-out),box-shadow .4s var(--ease-out)}
.brand:hover .logo{transform:translateY(-1px) scale(1.03);box-shadow:0 6px 18px -6px var(--glow)}
.brand .word{font-size:1.22rem;letter-spacing:.14em}
.brand small{display:block;font-size:.56rem;letter-spacing:.12em;color:var(--ink-faint);font-weight:500;
  max-width:40vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media(max-width:560px){.brand small{display:none}}
nav.main{display:none;flex:1 1 auto;gap:2px;flex-wrap:wrap}
nav.main a{display:inline-block;padding:8px 10px;border-radius:9px;text-decoration:none;color:var(--ink-soft);
  font-size:.92rem;font-weight:500;position:relative;transition:color .2s var(--ease),background-color .2s var(--ease)}
nav.main a:hover{background:var(--panel-2);color:var(--ink)}
nav.main a.active{color:var(--accent-ink);background:var(--sand)}
/* the active page keeps a short accent underline; on wide screens it grows out of the centre */
@media(min-width:900px){
  nav.main a::after{content:"";position:absolute;inset-block-end:2px;inset-inline-start:50%;
    width:0;height:2px;border-radius:2px;background:var(--accent);opacity:0;
    transform:translateX(-50%);transition:width .28s var(--ease-out),opacity .28s var(--ease-out)}
  nav.main a:hover::after{width:42%;opacity:.5}
  nav.main a.active::after{width:58%;opacity:1}
}
.controls{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex:0 0 auto}
.btn{background:var(--panel);border:1px solid var(--line-ui);color:var(--ink);border-radius:9px;
  min-width:38px;height:38px;padding:0 10px;font:inherit;font-size:.85rem;cursor:pointer;line-height:1;
  transition:border-color .2s var(--ease),background-color .2s var(--ease),transform .16s var(--ease),
             box-shadow .2s var(--ease)}
.btn:hover{border-color:var(--line-strong);transform:translateY(-1px);box-shadow:0 6px 16px -10px rgba(0,0,0,.5)}
.btn:active{transform:translateY(0) scale(.97)}
.lang-switch{display:flex;gap:2px;border:1px solid var(--line-ui);border-radius:10px;padding:2px;
  background:var(--panel);position:relative}
/* The switcher is three links now, and the current language is a <span> — there is nowhere
   to navigate to for the page you are already on. Sized to 40px so the row clears the 44px
   guidance once its 2px padding and border are counted; v10 measured 34x32. */
.lang-switch .lang{border:0;min-width:40px;height:40px;background:transparent;box-shadow:none;
  display:inline-flex;align-items:center;justify-content:center;text-decoration:none;
  color:var(--ink);font-size:.85rem;font-weight:600;border-radius:8px}
.lang-switch a.lang:hover{transform:none;box-shadow:none;background:var(--panel-2);color:var(--accent-ink)}
.lang-switch .lang[aria-current="true"]{background:var(--sand);color:var(--accent-ink);font-weight:700;
  box-shadow:inset 0 0 0 1px var(--line-strong)}
#theme{position:relative;overflow:hidden}
#theme:hover{color:var(--accent-ink)}
#navtoggle{display:inline-flex;align-items:center;justify-content:center;font-size:1.15rem}
.nav-backdrop{position:fixed;inset:0;background:rgba(3,12,8,.46);z-index:40;
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);animation:fade-in .22s var(--ease) both}

/* Twelve primary destinations will not sit on one line beside the brand at any laptop
   width — squeezed into the leftover ~600px they wrap three deep and strand a single link
   on its own row. Given the full width of the bar instead, they settle into two balanced,
   centred rows under a hairline: a navigation band, which is the honest shape for a site
   with this many sections. Grid places the band (no reordering involved). */
@media(min-width:900px){
  .bar{display:grid;grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"brand controls" "nav nav";
    align-items:center;gap:3px 14px;min-height:0;padding-block:7px}
  header.is-stuck .bar{min-height:0;padding-block:5px}
  .brand{grid-area:brand}
  .controls{grid-area:controls;margin-inline-start:0}
  nav.main{grid-area:nav;display:flex;justify-content:center;flex-wrap:wrap;row-gap:3px;
    padding-block-start:6px;border-block-start:1px solid var(--line)}
  /* the band sets its own line-height: inheriting Arabic's 1.9 made each row half again
     as tall as it needed to be, and the band is a control strip, not running text */
  /* the line-height has to land on the inner language span too: the global Arabic rule
     ([lang="ar"] line-height 1.9) sets the line box inside each link, not the link itself */
  nav.main a{font-size:.86rem;line-height:1.35}
  nav.main a{padding:6px 9px}
  #navtoggle{display:none}
}
/* mobile drawer
   The drawer is a child of <header>, so being positioned it paints above the bar's own
   brand and controls no matter what z-index those carry — which put it over the language
   and theme buttons. It hangs from under the bar instead: --header-h is measured by the
   script (with a sane fallback if the script never runs), the panel starts there, and the
   bar above it stays visible and operable while the drawer is open. */
@media(max-width:899px){
  /* visibility:hidden is not decoration here. v10 moved the drawer off-screen with
     transform alone, which hides it from the eye and from nobody else: a Tab sweep at
     375px produced twelve consecutive focus stops at x=392, outside the viewport, before
     reaching the toggle. transform does not remove an element from the tab sequence;
     visibility does. It is transitioned so the slide still animates. */
  nav.main{display:flex;position:fixed;inset-block-start:var(--header-h,62px);inset-block-end:0;
    inset-inline-end:0;width:min(82vw,340px);z-index:45;
    background:var(--panel);border-inline-start:1px solid var(--line);flex-direction:column;flex-wrap:nowrap;
    padding:14px 16px 24px;gap:4px;transform:translateX(100%);
    visibility:hidden;
    transition:transform .3s var(--ease-out),inset-block-start .3s var(--ease-out),
               visibility 0s linear .3s;
    overflow-y:auto;box-shadow:-24px 0 60px -30px rgba(0,0,0,.6)}
  [dir="rtl"] nav.main{transform:translateX(-100%);box-shadow:24px 0 60px -30px rgba(0,0,0,.6)}
  nav.main.open{transform:translateX(0);visibility:visible;transition-delay:0s}
  /* the dimmed ground stops at the bar for the same reason: the bar is still live */
  .nav-backdrop{inset-block-start:var(--header-h,62px)}
  nav.main a{font-size:1.05rem;padding:11px 12px}
  /* the links settle in one after another as the drawer arrives */
  nav.main.open a{animation:drawer-item .4s var(--ease-out) both;animation-delay:calc(var(--i,0) * 26ms + 90ms)}
}
@keyframes drawer-item{from{opacity:0;transform:translateY(9px)}to{opacity:1;transform:none}}
@keyframes fade-in{from{opacity:0}to{opacity:1}}

/* ---------- hero ---------- */
/* 404 opens on a bare <section> rather than a page hero; treated as one, it stops being
   the single page on the site with a flat, unmarked top. */
.hero,.pagehero,main>section:first-of-type:not(.hero):not(.pagehero){
  position:relative;overflow:hidden;isolation:isolate}
.hero{background:
   radial-gradient(120% 120% at 85% -10%,var(--sand) 0%,transparent 55%),
   linear-gradient(180deg,color-mix(in srgb,var(--teal-soft) 60%,var(--bg)),var(--bg))}
.pagehero,main>section:first-of-type:not(.hero):not(.pagehero){background:
   radial-gradient(100% 120% at 88% -20%,var(--sand) 0%,transparent 58%),
   linear-gradient(180deg,color-mix(in srgb,var(--teal-soft) 45%,var(--bg)),var(--bg))}
/* ---------- hero background video (index only, and only once the asset exists) ----------
   It sits behind the khatim lattice and the sweep, which is why it takes z-index -3 while
   they hold -2 and -1. The scrim is not decoration: the hero type is dark ink on a light
   ground in the light theme, and no video can be relied on to stay out of its way, so the
   veil is what keeps the headline legible whatever the footage does. It is painted from
   --bg, so it veils towards white in the light theme and towards black in the dark one. */
.hero-media{position:absolute;inset:0;z-index:-3;overflow:hidden;pointer-events:none}
.hero-media video{width:100%;height:100%;object-fit:cover;display:block}
/* --hero-veil is the one number to turn once there is real footage to look at. Lower lets
   more of the picture through; too low and the headline starts fighting it. It is set
   cautiously here because no footage exists yet to judge against. */
.hero-scrim{position:absolute;inset:0;
  background:
    linear-gradient(to bottom,
      color-mix(in srgb,var(--bg) var(--hero-veil),transparent) 0%,
      color-mix(in srgb,var(--bg) calc(var(--hero-veil) + 8%),transparent) 55%,
      color-mix(in srgb,var(--bg) calc(var(--hero-veil) + 18%),transparent) 100%)}
/* with footage behind it the opening earns a little more height; without it nothing changes.
   Interior pageheroes reuse the same class — isolation is already on .pagehero. */
.hero:has(.hero-media) .wrap,.pagehero:has(.hero-media) .wrap{padding-block:clamp(56px,10vw,116px)}
.pagehero:has(.hero-media){--hero-veil:56%}
section:has(.hero-media){overflow:hidden;isolation:isolate}
section:has(.hero-media)>.wrap{position:relative;z-index:1}
/* Interior bands crop toward the lower third so a spectrum/field clip reads behind
   the prose rather than as empty sky above a figure. The home hero is unchanged. */
section.ambient{--hero-veil:48%}
/* Spectrum-style clips keep their energy in the lower third; full-frame
   close-ups stay centred. */
section.ambient.low .hero-media video{object-position:center 78%}
@media (prefers-reduced-motion: reduce){
  /* the script also pauses it; this keeps the frame still even if that never runs */
  .hero-media video{animation:none}
}

/* layer 1 — the khatim lattice, cut from a single tile by a mask so one asset serves both
   themes and takes its colour from --accent. The fade lives in the painted gradient, not in a
   second mask layer, so nothing here depends on mask-composite being understood. */
.hero::before,.pagehero::before,footer::before,
main>section:first-of-type:not(.hero):not(.pagehero)::before{content:"";position:absolute;inset:0;z-index:-2;
  opacity:var(--motif-opacity);pointer-events:none;
  -webkit-mask-image:var(--motif);mask-image:var(--motif);
  -webkit-mask-size:80px 80px;mask-size:80px 80px;
  -webkit-mask-repeat:repeat;mask-repeat:repeat;
  background:var(--accent)}
.hero::before{background:radial-gradient(115% 95% at 82% 4%,var(--accent) 0%,transparent 68%)}
.pagehero::before,main>section:first-of-type:not(.hero):not(.pagehero)::before{
  background:linear-gradient(180deg,var(--accent),transparent 86%)}

/* Interior pages get the same instrument figure as the home hero, held one step quieter:
   the wavefront rings without the rotating wedge, breathing rather than sweeping. It gives
   a page that opens on nothing but a heading and a paragraph something to open onto. */
.pagehero::after,main>section:first-of-type:not(.hero):not(.pagehero)::after{
  content:"";position:absolute;z-index:-1;inset-block-start:-44%;inset-inline-end:-10%;
  width:min(70vw,560px);aspect-ratio:1;border-radius:50%;pointer-events:none;
  opacity:calc(var(--sweep-opacity) * .9);
  background:repeating-radial-gradient(circle,transparent 0 37px,
    color-mix(in srgb,var(--accent) 48%,transparent) 37px 38px);
  -webkit-mask-image:radial-gradient(circle,transparent 15%,#000 40%,transparent 74%);
  mask-image:radial-gradient(circle,transparent 15%,#000 40%,transparent 74%);
  animation:breathe 30s var(--ease) infinite alternate}
@keyframes breathe{from{transform:scale(1);opacity:.55}to{transform:scale(1.07);opacity:1}}
/* layer 2 — a slow sweep figure, the one nod to what the instrument actually does */
.hero::after{content:"";position:absolute;z-index:-1;inset-block-start:-38%;inset-inline-end:-14%;
  width:min(78vw,660px);aspect-ratio:1;border-radius:50%;pointer-events:none;
  opacity:var(--sweep-opacity);
  background:
    conic-gradient(from 0deg,transparent 0deg,
      color-mix(in srgb,var(--accent) 18%,transparent) 14deg,
      color-mix(in srgb,var(--accent) 62%,transparent) 30deg,
      color-mix(in srgb,var(--accent) 16%,transparent) 46deg,transparent 62deg),
    repeating-radial-gradient(circle,transparent 0 43px,
      color-mix(in srgb,var(--accent) 45%,transparent) 43px 44px);
  -webkit-mask-image:radial-gradient(circle,transparent 14%,#000 34%,transparent 72%);
  mask-image:radial-gradient(circle,transparent 14%,#000 34%,transparent 72%);
  animation:sweep 26s linear infinite}
@keyframes sweep{to{transform:rotate(360deg)}}
.hero .wrap,.pagehero .wrap{padding-block:clamp(40px,8vw,90px);position:relative;z-index:1}
.kicker{font-size:.8rem;letter-spacing:.2em;text-transform:uppercase;color:var(--teal);
  font-weight:600;margin:0 0 .5rem;display:grid;grid-template-columns:auto minmax(0,1fr);
  align-items:center;column-gap:.65em;row-gap:.15em}
/* a short gold rule ahead of the kicker — the standing mark of a section opening */
.kicker::before{content:"";width:34px;height:2px;border-radius:2px;
  background:linear-gradient(90deg,var(--teal),transparent)}
[dir="rtl"] .kicker::before{background:linear-gradient(270deg,var(--teal),transparent)}
[lang="ar"].kicker,.kicker [lang="ar"]{letter-spacing:.06em}
h1{font-size:clamp(1.95rem,5.4vw,3.2rem);line-height:1.14;margin:.1rem 0 .6rem;letter-spacing:-.018em;
  font-weight:700;text-wrap:balance;color:var(--ink)}
/* Narrow: .2em tracking on the kicker and text-wrap:balance on the headline inflate
   min-content past the column; html{overflow-x:clip} then crops the line (EN kicker,
   AR/TR h1, chips). Grid + quieter tracking lets the kicker wrap; balance waits for
   a width that can actually balance. */
@media (max-width: 640px){
  .kicker{letter-spacing:.08em;font-size:.74rem}
  .kicker::before{width:22px}
  html[lang="ar"] .kicker{letter-spacing:normal}
  h1{font-size:clamp(1.45rem,7vw,1.8rem);line-height:1.22;text-wrap:wrap;max-width:100%}
  h2,.lede{text-wrap:wrap;max-width:100%}
  .status,.row{max-width:100%}
}
/* The heading used to carry a slight green cast towards its foot, painted as a gradient
   clipped to the glyphs with background-clip:text and color:transparent. It is gone, and
   deliberately so.

   That technique makes the text invisible whenever the clipped background fails to paint,
   and the heading had no colour of its own to fall back to — transparent is the real value
   of `color`, not a placeholder. It failed in exactly that way on the live v10 site:
   reproduced here at 1280px, where the h1 rendered as blank space with opacity 1, an
   identity transform, the element in the viewport and every computed value correct.

   The cause is the entrance animation, not the browser. `html.is-ready .hero h1` applies
   `animation: rise .58s both`, which animates opacity and transform; that promotes the
   heading to its own compositing layer, and the glyph-clipped background is lost on the
   promoted layer. Setting `animation:none` on the element repaints it immediately, which
   is what isolates it. The @supports guard never covered this — the property IS supported,
   it simply does not survive compositing.

   A decorative gradient is not worth a headline that sometimes does not exist, so the
   heading is painted in --ink and the animation is kept. */
/* Forced colours (Windows high contrast). The @supports guard above protects an engine
   that cannot clip a background to text — it does not protect this mode, where the painted
   background-image is dropped and a transparent colour would take the whole heading with
   it. Same for the controls: box-shadow elevation and tinted borders are discarded here,
   so the system's own border keywords have to carry the shape. */
@media (forced-colors: active){
  .btn,.chip,.card,.cta{border:1px solid ButtonBorder;forced-color-adjust:auto}
  .cta{background:ButtonFace;color:ButtonText}
  .lang-switch .lang[aria-current="true"]{border:2px solid Highlight}
}

/* The root language chooser. Served only if mod_rewrite is unavailable or a host's
   AllowOverride refuses the .htaccess rules, so it is deliberately plain. */
body.langpick{display:flex;min-height:100vh;align-items:center}
body.langpick .wrap{padding-block:48px}
.langpick-list{display:flex;flex-wrap:wrap;gap:12px;margin-top:22px;padding:0;list-style:none}

/* the page does not scroll behind the open drawer */
body.nav-locked{overflow:hidden}

/* the closing next-step block. Set on the soft ground rather than a panel so it reads as
   the end of the page rather than another card competing with the content above it. */
section.closer{border-block-start:1px solid var(--line);margin-block-start:clamp(28px,4vw,46px);
  background:var(--panel-2)}
section.closer .row{display:flex;flex-wrap:wrap;gap:12px;
  padding-block:clamp(26px,3.6vw,38px)}

/* required / optional markers on the briefing form */
.req,.opt{font-weight:500;font-size:.82em;color:var(--ink-faint)}
.req{color:var(--open)}
form.brief .check{display:flex;gap:10px;align-items:flex-start;margin-top:.2rem}
form.brief .check label{margin:0}
.lede{font-size:clamp(1.05rem,2.4vw,1.32rem);color:var(--ink-soft);max-width:60ch;margin:.4rem 0 0;
  text-wrap:pretty}
.hero .lede{max-width:64ch}

/* ---------- the page figure, now inside the hero ----------
   On an interior page the diagram is what the reader came to look at, so it opens the page
   instead of closing it. It sits on the hero's own ground rather than on a panel, so the
   khatim lattice and the wavefront read behind it and the hero stays one block. */
.pagehero figure.infographic{margin-block-start:clamp(22px,3.4vw,34px);
  background:color-mix(in srgb,var(--panel) 82%,transparent);
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px)}
.pagehero figure.infographic figcaption{
  background:color-mix(in srgb,var(--panel-2) 40%,transparent)}
/* the hero's own padding already separates it from the section below */
.pagehero .wrap>figure.infographic:last-child{margin-block-end:0}

/* The hero entrance animation is GONE. Not softened — removed.

   It was a staggered `rise` on the kicker, headline, status chips, lede and buttons, run
   once when the script added html.is-ready. It hid its own content, repeatedly and in
   three different forms:

     * as an opacity fade, it parked the whole hero at opacity:0 until JS ran, so a slow
       connection opened on an empty band where the headline goes;
     * with background-clip:text on the h1, the clipped gradient did not survive the
       compositing layer the animation created, and the headline rendered as blank space
       with every computed value correct (reproduced on the live v10 site, and here);
     * and reduced to transform-only — no opacity anywhere — the elements STILL failed to
       paint on the promoted layer. Checked at 375px in dark theme: computed colour
       rgb(233,241,236) on rgb(8,19,14), opacity 1, identity transform, in the viewport,
       and nothing on screen. Setting animation:none on those five elements repaints the
       whole hero instantly. That isolates the animation itself, not any property it
       animates, as the cause.

   A first-load flourish is not worth a headline that sometimes does not exist. The hero is
   the first thing every reader sees and it now simply renders. Scroll-reveal below the
   fold is kept — it is a transform transition, not an animation, and it was confirmed
   rendering correctly after the same opacity removal.

   If an entrance is ever wanted back, it must not be the only thing standing between the
   reader and the words: animate a wrapper, never the text itself, and check it at 375px
   in dark theme in both scripts before it ships. */
/* Transform only — no opacity. The entrance used to fade from opacity:0, with
   animation-fill-mode:both holding that start state until the animation ran. Two
   consequences, both observed rather than theorised:

     * the hero is parked invisible until JS adds html.is-ready, so the first thing a
       reader sees on a slow connection is an empty band where the headline goes; and
     * an element left on its own compositing layer by a finished opacity animation was
       rendering ghosted or not at all — reproduced on the live v10 site (the English
       headline) and again here at 1280px (the whole Arabic hero). Setting animation:none
       on those elements repaints them solid immediately, which is what isolates it.

   Sliding the block up a few pixels gives the same arrival without ever making the
   content's visibility depend on an animation completing. */
@keyframes rise{from{transform:translateY(16px)}to{transform:none}}

/* ---------- scroll reveal (classes are attached by the script, so no-JS shows everything) -- */
/* Transform only, for the same reason as @keyframes rise above: content must never be
   invisible while it waits for something to run. opacity:0 here meant every block below
   the fold was blank until the IntersectionObserver reached it — the script has a
   three-second failsafe precisely because that state is dangerous, which is the tell that
   it was the wrong property to animate. A block now slides the last few pixels into place
   and is legible the entire time, including with the script blocked. */
.reveal{transform:translateY(16px);
  transition:transform .5s var(--ease-out);
  transition-delay:var(--d,0ms)}
.reveal.in{transform:none}

/* ---------- headings / text ---------- */
h2{font-size:clamp(1.45rem,3.2vw,2.05rem);line-height:1.22;margin:0 0 .6rem;letter-spacing:-.015em;
  text-wrap:balance}
h3{font-size:1.13rem;margin:0 0 .35rem;letter-spacing:-.008em}
p{margin:.7rem 0;text-wrap:pretty}
.sub{color:var(--ink-soft);max-width:70ch;font-size:1.02rem}
/* Unclassed paragraphs are the running text of the site, and at the full 1080px column they
   were setting lines of ninety-odd characters — too long to track back to comfortably in any
   of the three scripts. Every paragraph role the pages actually use is named and left on the
   width its own rule gives it; testing for an absent class would not work, since the reveal
   script puts a class on these very paragraphs. */
main section>.wrap>p:not(.lede):not(.kicker):not(.statusbar):not(.note):not(.sub):not(.row):not(.pull):not(.disclaim):not(.form-status){max-width:74ch}

/* Each section heading carries a short green-to-gold rule. On a page that is otherwise all
   prose it is the thing the eye finds when scanning for where a part begins. */
main section>.wrap>h2{position:relative;padding-block-end:.45rem}
main section>.wrap>h2::after{content:"";position:absolute;inset-block-end:0;inset-inline-start:0;
  width:46px;height:3px;border-radius:3px;
  background:linear-gradient(90deg,var(--accent),var(--teal))}
[dir="rtl"] main section>.wrap>h2::after{background:linear-gradient(270deg,var(--accent),var(--teal))}
.note{background:linear-gradient(180deg,var(--panel-2),color-mix(in srgb,var(--panel-2) 55%,var(--panel)));
  border-inline-start:3px solid var(--line-strong);
  padding:13px 17px;border-radius:0 var(--radius-s) var(--radius-s) 0;color:var(--ink-soft);font-size:.96rem}
[dir="rtl"] .note{border-radius:var(--radius-s) 0 0 var(--radius-s)}
.lead-emph{color:var(--ink);font-weight:600}
strong{font-weight:700;color:var(--ink)}

/* ---------- status banner ---------- */
.statusbar{position:relative;overflow:hidden;
  background:linear-gradient(135deg,color-mix(in srgb,var(--teal-soft) 42%,var(--panel)),var(--panel) 62%);
  border:1px solid var(--line);border-inline-start:4px solid var(--teal);
  border-radius:var(--radius-s);padding:15px 19px;color:var(--ink-soft);font-size:.98rem;
  box-shadow:var(--shadow)}
[dir="rtl"] .statusbar{border-inline-start-width:4px}
.statusbar strong{color:var(--teal)}
/* No passing-sheen. A moving highlight is promotional grammar; this block is a
   disclosure. B3 v14. Reduced-motion used to hide the sheen; with the sheen gone
   the overflow:hidden on .statusbar is just a clip, harmless. */

/* ---------- chips ---------- */
.status{display:flex;flex-wrap:wrap;gap:8px;margin:1.1rem 0 .2rem}
.chip{display:inline-flex;align-items:center;gap:.5em;padding:6px 13px;border-radius:999px;
  font-size:.82rem;font-weight:600;background:var(--panel);border:1px solid var(--line);color:var(--ink-soft);
  transition:transform .22s var(--ease),box-shadow .22s var(--ease),border-color .22s var(--ease)}
.chip::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;
  flex:0 0 auto;opacity:.62}
.chip:hover{transform:translateY(-2px);box-shadow:0 8px 18px -12px rgba(0,0,0,.55)}
.chip.gold{background:var(--sand);border-color:var(--line-strong);color:var(--accent-ink)}
.chip.teal{background:var(--teal-soft);border-color:var(--teal);color:var(--teal)}
/* the live-status chip breathes; it is the one element on the page that is never still */
.chip.teal::before{animation:pulse 2.6s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:.35;box-shadow:0 0 0 0 currentColor}
  50%{opacity:1;box-shadow:0 0 0 4px color-mix(in srgb,currentColor 18%,transparent)}}
.chip.open{background:var(--open-soft);border-color:var(--open);color:var(--open)}

/* ---------- buttons / CTAs ---------- */
.row{display:flex;flex-wrap:wrap;gap:12px;margin-top:1.5rem}
.cta{position:relative;overflow:hidden;display:inline-block;color:var(--cta-ink);text-decoration:none;font-weight:600;
  padding:13px 24px;border-radius:11px;border:1px solid transparent;isolation:isolate;
  background:linear-gradient(135deg,var(--cta-from),var(--cta-mid) 52%,var(--cta-to));
  box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 24px -14px var(--glow);
  transition:transform .22s var(--ease-out),box-shadow .28s var(--ease-out),filter .22s var(--ease)}
.cta:hover{color:var(--cta-ink);transform:translateY(-2px);filter:saturate(1.08);
  box-shadow:0 2px 4px rgba(0,0,0,.14),0 18px 36px -16px var(--glow)}
.cta:active{transform:translateY(0) scale(.985)}
/* a sheen crosses the face on hover — one pass, then it is gone */
.cta::after{content:"";position:absolute;inset-block:-40%;inset-inline-start:-70%;width:44%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.30),transparent);
  transform:skewX(-18deg);opacity:0;transition:transform .62s var(--ease-out),opacity .2s var(--ease)}
.cta:hover::after{opacity:1;transform:skewX(-18deg) translateX(420%)}
[dir="rtl"] .cta:hover::after{transform:skewX(-18deg) translateX(-420%)}
.cta.ghost{background:transparent;color:var(--accent-ink);border-color:var(--line-ui);box-shadow:none}
.cta.ghost:hover{background:var(--sand);color:var(--accent-ink);border-color:var(--accent);
  box-shadow:0 12px 28px -18px var(--glow)}
.cta.ghost::after{background:linear-gradient(100deg,transparent,var(--glow-soft),transparent)}
.more{display:inline-flex;align-items:center;gap:.35em;margin-top:.6rem;font-weight:600;text-decoration:none;
  color:var(--accent-ink)}
.more::after{content:"\2190";display:inline-block;transition:transform .28s var(--ease-out)}
html[dir="ltr"] .more::after{content:"\2192"}
.more:hover::after{transform:translateX(-4px)}
html[dir="ltr"] .more:hover::after{transform:translateX(4px)}

/* ---------- grids / cards ---------- */
.grid{display:grid;gap:16px;grid-template-columns:1fr}
@media(min-width:640px){.grid.g2,.grid.g3{grid-template-columns:repeat(2,1fr)}}
@media(min-width:900px){.grid.g3{grid-template-columns:repeat(3,1fr)}}
.card{position:relative;overflow:hidden;isolation:isolate;background:var(--panel);
  border:1px solid var(--line);border-radius:var(--radius);padding:22px;box-shadow:var(--shadow);
  transition:transform .34s var(--ease-out),box-shadow .34s var(--ease-out),border-color .34s var(--ease)}
.card:hover{transform:translateY(-4px);box-shadow:var(--shadow-lift);
  border-color:color-mix(in srgb,var(--accent) 34%,var(--line))}
/* a soft light follows the pointer across the face; the script feeds it --mx / --my */
.card::before{content:"";position:absolute;inset:0;z-index:-1;opacity:0;pointer-events:none;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,0%),var(--glow-soft),transparent 62%);
  transition:opacity .38s var(--ease)}
.card:hover::before{opacity:1}
/* instrument corner bracket — a hairline register mark, not an ornament */
.card::after{content:"";position:absolute;inset-block-start:12px;inset-inline-end:12px;width:14px;height:14px;
  border-block-start:1px solid var(--line-strong);border-inline-end:1px solid var(--line-strong);
  border-start-end-radius:5px;opacity:.6;pointer-events:none;
  transition:width .34s var(--ease-out),height .34s var(--ease-out),border-color .34s var(--ease),opacity .34s var(--ease)}
.card:hover::after{width:20px;height:20px;border-color:var(--accent);opacity:1}
.card h3{color:var(--ink)}
.card.gold{border-inline-start:4px solid var(--accent)}
.card.teal{border-inline-start:4px solid var(--teal)}
.card.open{border-inline-start:4px solid var(--open)}
.card.gold:hover{border-inline-start-color:var(--accent-lift)}
.card.teal:hover{border-inline-start-color:var(--teal-lift);border-color:color-mix(in srgb,var(--teal) 34%,var(--line))}
.card.teal:hover::after{border-color:var(--teal)}
.tag{display:inline-block;font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--teal);font-weight:700;margin-bottom:.4rem}
.card p{margin:.35rem 0 0;color:var(--ink-soft);font-size:.98rem}
/* the standing pull-quote card carries the motif rather than a flat fill */
section .card.teal h2{margin-bottom:.2rem}

/* three-part / feature list */
.trust{display:grid;gap:14px;grid-template-columns:1fr;margin-top:1rem}
@media(min-width:760px){.trust{grid-template-columns:repeat(2,1fr)}}
.trust>div{position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-s);padding:17px;
  transition:transform .3s var(--ease-out),border-color .3s var(--ease),box-shadow .3s var(--ease-out)}
.trust>div:hover{transform:translateY(-3px);border-color:color-mix(in srgb,var(--accent) 30%,var(--line));
  box-shadow:var(--shadow)}
.trust strong{display:block;margin-bottom:.25rem}

ul.clean{list-style:none;padding:0;margin:1rem 0 0}
ul.clean li{position:relative;padding-inline-start:1.5em;margin:.55rem 0;color:var(--ink-soft)}
ul.clean li::before{content:"\25B8";position:absolute;inset-inline-start:0;color:var(--teal);
  transition:transform .24s var(--ease-out)}
ul.clean li:hover::before{transform:translateX(3px)}
[dir="rtl"] ul.clean li::before{content:"\25C2"}
[dir="rtl"] ul.clean li:hover::before{transform:translateX(-3px)}

ol.steps{list-style:none;counter-reset:s;padding:0;margin:1rem 0 0;display:grid;gap:12px}
ol.steps li{counter-increment:s;position:relative;background:var(--panel);border:1px solid var(--line);
  border-radius:var(--radius-s);padding:15px 17px;display:flex;gap:15px;align-items:flex-start;
  transition:transform .3s var(--ease-out),border-color .3s var(--ease),box-shadow .3s var(--ease-out)}
ol.steps li:hover{transform:translateY(-2px);border-color:color-mix(in srgb,var(--accent) 32%,var(--line));
  box-shadow:var(--shadow)}
ol.steps li::before{content:counter(s);flex:0 0 auto;width:32px;height:32px;border-radius:50%;
  background:linear-gradient(140deg,var(--sand),color-mix(in srgb,var(--sand) 55%,var(--panel)));
  color:var(--accent-ink);font-weight:700;display:flex;align-items:center;justify-content:center;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 26%,transparent);
  transition:box-shadow .3s var(--ease),transform .3s var(--ease-out)}
ol.steps li:hover::before{transform:scale(1.06);
  box-shadow:inset 0 0 0 1px var(--accent),0 0 0 5px var(--glow-soft)}
ol.steps strong{display:block}
ol.steps .sub{margin:.15rem 0 0;font-size:.95rem}

/* ---------- tables (wide tables scroll inside their own container) ---------- */
.tablewrap{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--line);
  border-radius:var(--radius);margin-top:1rem;background:var(--panel);box-shadow:var(--shadow);
  scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
.tablewrap::-webkit-scrollbar{height:8px}
.tablewrap::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:8px}
.tablewrap::-webkit-scrollbar-track{background:transparent}
table{border-collapse:collapse;width:100%;min-width:520px;font-size:.95rem}
caption{caption-side:top;text-align:start;padding:12px 16px;color:var(--ink-faint);font-size:.88rem;
  border-bottom:1px solid var(--line)}
th,td{padding:12px 15px;text-align:start;vertical-align:top;border-bottom:1px solid var(--line)}
thead th{background:linear-gradient(180deg,var(--panel-2),color-mix(in srgb,var(--panel-2) 70%,var(--panel)));
  font-weight:700;color:var(--ink);position:sticky;top:0;z-index:1;
  font-size:.82rem;letter-spacing:.06em;text-transform:uppercase}
tbody tr:last-child td{border-bottom:0}
tbody tr{transition:background-color .2s var(--ease)}
tbody tr:hover{background:color-mix(in srgb,var(--sand) 52%,transparent)}
td .flag-open{color:var(--open);font-weight:700}
td .flag-unver{color:var(--accent-ink);font-weight:600}
td .flag-done{color:var(--teal);font-weight:600}
/* was an inline style attribute on three paragraphs; lifted to a class so the
   Content-Security-Policy needs no style-src-attr exemption */
.tight{margin:.3rem 0 0}
code,.mono{font-family:var(--ff-mono);
  background:var(--panel-2);padding:.1em .42em;border-radius:6px;font-size:.9em;
  box-shadow:inset 0 0 0 1px var(--line)}

/* ---------- infographic figures (native trilingual SVG; see the builders in build/) ---------- */
figure.infographic{margin:1.5rem 0 0;border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;background:var(--panel);box-shadow:var(--shadow);
  transition:box-shadow .38s var(--ease-out),border-color .38s var(--ease),transform .38s var(--ease-out)}
figure.infographic:hover{box-shadow:var(--shadow-lift);transform:translateY(-3px);
  border-color:color-mix(in srgb,var(--accent) 28%,var(--line))}
figure.infographic img{width:100%;height:auto;transition:transform .7s var(--ease-out)}
figure.infographic:hover img{transform:scale(1.014)}
figure.infographic figcaption{padding:13px 17px;border-top:1px solid var(--line);
  color:var(--ink-soft);font-size:.9rem;
  background:linear-gradient(180deg,color-mix(in srgb,var(--panel-2) 45%,var(--panel)),var(--panel))}
/* native trilingual SVG infographics (built claim-safe, in place of the raster set) */
.infographic-svg{padding:14px 16px 6px;background:var(--panel)}
figure.infographic svg{display:block;width:100%;height:auto}
figure.infographic svg text{font-family:var(--ff-latin)}
html[lang="ar"] svg text,[lang="ar"] svg text{font-family:var(--ff-arabic)}
figure.infographic .ig-label{font-weight:700;font-size:14px;fill:var(--ink)}
figure.infographic .ig-sub{font-size:11px;fill:var(--ink-faint)}
figure.infographic .ig-band{font-weight:600;font-size:13.5px;fill:var(--accent-ink)}
figure.infographic .ig-band-open{font-weight:600;font-size:12.5px;fill:var(--open)}
figure.infographic .ig-kicker{font-size:11px;font-weight:600;letter-spacing:.04em;fill:var(--teal)}
figure.infographic .ig-value{font-weight:700;font-size:21px;fill:var(--accent-ink)}
figure.infographic .ig-mono{font-family:var(--ff-mono);font-size:12.5px;fill:var(--ink);font-weight:500}
figure.infographic .ig-tag{font-size:10.5px;font-weight:700}
figure.infographic .ig-open{fill:var(--open)}
/* ---------- #11 · raster figures in the dark theme ----------
   The supplied art is drawn on a light ground. Dropped unchanged into a dark page it
   glares — a white slab in a near-black column. It is held back a little and given a warm
   matte so it sits in the page instead of burning a hole in it, and it returns to full
   strength on hover or when opened, where the reader is looking at it deliberately. */
figure.infographic picture img{transition:filter .4s var(--ease),transform .7s var(--ease-out)}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) figure.infographic picture img{
    filter:brightness(.88) contrast(1.04) saturate(.96)}
  :root:not([data-theme="light"]) figure.infographic:hover picture img{filter:none}
  :root:not([data-theme="light"]) figure.infographic:has(picture){
    background:linear-gradient(180deg,var(--panel-2),var(--panel))}
}
:root[data-theme="dark"] figure.infographic picture img{
  filter:brightness(.88) contrast(1.04) saturate(.96)}
:root[data-theme="dark"] figure.infographic:hover picture img{filter:none}
:root[data-theme="dark"] figure.infographic:has(picture){
  background:linear-gradient(180deg,var(--panel-2),var(--panel))}

/* ---------- #10 · the figures open ----------
   These are dense diagrams shown at about 1040px, where their internal type is at the
   edge of legible. A click opens the frame at full size. The control is built by the
   script, so a reader without it simply gets the figure as before. */
figure.infographic:has(picture){cursor:zoom-in}
.ig-open-hint{position:absolute;inset-block-end:10px;inset-inline-end:10px;z-index:2;
  display:inline-flex;align-items:center;gap:.4em;padding:6px 11px;border-radius:999px;
  font-size:.76rem;font-weight:600;cursor:zoom-in;font-family:inherit;
  background:var(--panel-glass);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  border:1px solid var(--line-strong);color:var(--ink-soft);
  opacity:0;transform:translateY(6px);transition:opacity .28s var(--ease),transform .28s var(--ease-out)}
figure.infographic{position:relative}
figure.infographic:hover .ig-open-hint,figure.infographic:focus-within .ig-open-hint,
.ig-open-hint:focus-visible{opacity:1;transform:none}
@media (hover:none){.ig-open-hint{opacity:1;transform:none}}

/* display:grid would otherwise beat the hidden attribute's own display:none */
.ig-viewer[hidden]{display:none}
.ig-viewer{position:fixed;inset:0;z-index:90;display:grid;grid-template-rows:auto 1fr;
  gap:10px;padding:14px;background:color-mix(in srgb,var(--bg) 86%,#000);
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);
  opacity:0;visibility:hidden;transition:opacity .26s var(--ease),visibility .26s}
.ig-viewer.open{opacity:1;visibility:visible}
.ig-viewer-bar{display:flex;align-items:center;gap:10px;justify-content:flex-end}
.ig-viewer-cap{margin-inline-end:auto;font-size:.88rem;color:var(--ink-soft);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ig-viewer figure{margin:0;min-height:0;overflow:auto;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--panel);display:grid;place-items:center}
.ig-viewer img{max-width:none;width:auto;height:auto;filter:none;
  transition:transform .3s var(--ease-out)}
.ig-viewer.fit img{max-width:100%;max-height:100%;width:auto}
.ig-viewer:not(.fit){cursor:zoom-out}
.ig-viewer:not(.fit) figure{place-items:start}

/* ---------- #12 · the native diagrams draw themselves ----------
   Strokes trace in and type settles behind them, once, when the figure is first reached.
   The whole effect hangs off one class the script adds on reveal; without it the diagram
   is simply already drawn. */
figure.infographic .infographic-svg svg rect,
figure.infographic .infographic-svg svg circle,
figure.infographic .infographic-svg svg line,
figure.infographic .infographic-svg svg path,
figure.infographic .infographic-svg svg ellipse{transition:none}
/* Everything traces EXCEPT the two kinds of element that carry their own continuous
   motion. They have to be excluded by selector, not left to the cascade: a later rule
   replaces `animation` but not the dasharray and dashoffset set here, which left the
   aperture rings permanently offset by their whole length — drawn, but invisible. */
figure.infographic.ig-draw .infographic-svg svg rect,
figure.infographic.ig-draw .infographic-svg svg path,
figure.infographic.ig-draw .infographic-svg svg ellipse,
figure.infographic.ig-draw .infographic-svg svg circle:not(.ig-ring),
figure.infographic.ig-draw .infographic-svg svg line:not(.ig-flow){
  stroke-dasharray:1400;stroke-dashoffset:1400;
  animation:ig-trace 1.5s var(--ease-out) forwards}
figure.infographic.ig-draw .infographic-svg svg text{animation:ig-settle .6s var(--ease-out) both;
  animation-delay:.5s}
figure.infographic.ig-draw .infographic-svg svg rect[fill="var(--sand)"]{animation-delay:.35s}
@keyframes ig-trace{to{stroke-dashoffset:0}}
@keyframes ig-settle{from{opacity:0}to{opacity:1}}
/* the signal runs along the chain: dashes march from stage to stage, continuously */
figure.infographic .infographic-svg svg line.ig-flow{
  stroke-dasharray:7 7;stroke-dashoffset:0;animation:ig-run 1.4s linear infinite}
@keyframes ig-run{from{stroke-dashoffset:0}to{stroke-dashoffset:-14}}
figure.infographic .infographic-svg svg circle.ig-ring{transform-box:fill-box;
  transform-origin:center;animation:ig-ping 3.2s var(--ease) infinite}
figure.infographic .infographic-svg svg circle.ig-ring-2{animation-delay:.45s}
@keyframes ig-ping{0%,72%,100%{opacity:1;transform:scale(1)}
  84%{opacity:.45;transform:scale(1.13)}}

/* ---------- #14 · status flags carry a mark, not only a colour ----------
   Colour alone is the one cue a reader with a colour vision difference may not receive,
   and these three states are the substance of the verification table. */
td .flag-open::before,td .flag-unver::before,td .flag-done::before{
  content:"";display:inline-block;width:9px;height:9px;margin-inline-end:.5em;
  vertical-align:baseline;border:1.5px solid currentColor;background:transparent}
td .flag-open::before{border-radius:50%}
td .flag-done::before{border-radius:2px;background:currentColor}
td .flag-unver::before{border-radius:2px;
  background:linear-gradient(135deg,currentColor 0 50%,transparent 50% 100%)}

/* the per-language wrapper spans render the SVG as a block when their language is active */


/* ---------- footer ---------- */
footer{position:relative;overflow:hidden;isolation:isolate;
  background:linear-gradient(180deg,var(--panel),color-mix(in srgb,var(--bg-deep) 55%,var(--panel)));
  border-top:1px solid var(--line);margin-top:0;padding-block:38px;color:var(--ink-soft)}
footer::before{z-index:-1;opacity:calc(var(--motif-opacity) * .9);
  background:linear-gradient(180deg,transparent,var(--accent) 78%)}
/* a gold-to-green thread across the top edge, the same rule the sections use */
footer::after{content:"";position:absolute;inset-block-start:0;inset-inline:0;height:2px;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--accent) 22%,var(--teal) 78%,transparent);opacity:.55}
.footnav{display:flex;flex-wrap:wrap;gap:2px 4px;align-items:center;margin-bottom:10px}
.footnav a{color:var(--ink-soft);text-decoration:none;font-size:.9rem;padding:4px 7px;border-radius:7px;
  transition:color .2s var(--ease),background-color .2s var(--ease)}
.footnav a:hover{color:var(--ink);background:var(--panel-2)}
.footnav.legal a{color:var(--accent-ink)}
.footnav .sep{color:var(--line-strong);padding:0 2px}
footer .brandline{font-size:.9rem;color:var(--ink);margin-top:4px}
.disclaim{font-size:.82rem;color:var(--ink-faint);max-width:none;margin-top:12px;line-height:1.7}

/* ---------- forms ---------- */
/* label and field are siblings now — <label for> + <input id> — rather than a label
   wrapping its input. Explicit association is what lets the required marker sit inside the
   label without being read as part of the field name, and it is what the check row needs
   so the box sits before its text. Row gap is small so a pair still reads as one
   unit; the larger gap comes back between pairs via the field's margin. */
form.brief{display:grid;gap:6px;max-width:560px;margin-top:1rem}
form.brief label{font-weight:600;font-size:.95rem}
form.brief input,form.brief textarea,form.brief select{margin-block-end:9px}
form.brief input,form.brief select,form.brief textarea{font:inherit;padding:12px 13px;
  border:1px solid var(--line-strong);border-radius:10px;background:var(--panel);color:var(--ink);width:100%;
  transition:border-color .22s var(--ease),box-shadow .22s var(--ease),background-color .22s var(--ease)}
form.brief input:hover,form.brief select:hover,form.brief textarea:hover{border-color:var(--accent-ink)}
form.brief input:focus,form.brief select:focus,form.brief textarea:focus{
  outline:none;border-color:var(--accent);box-shadow:0 0 0 4px var(--glow-soft)}
form.brief textarea{min-height:130px;resize:vertical}
form.brief .check{font-weight:400;font-size:.9rem;color:var(--ink-soft);margin-block:.3rem .2rem}
form.brief .check input{width:auto;margin-top:.2em;margin-block-end:0;accent-color:var(--accent);
  flex:0 0 auto}
form.brief .cta,form.brief button[type="submit"]{justify-self:start}
.form-status{min-height:1.2em;font-size:.9rem;color:var(--teal);font-weight:600}

/* ---------- return-to-top control (built by the script) ---------- */
.totop{position:fixed;inset-block-end:22px;inset-inline-end:22px;z-index:55;
  width:46px;height:46px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
  background:var(--panel-glass);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border:1px solid var(--line-strong);color:var(--accent-ink);font-size:1.05rem;line-height:1;
  box-shadow:var(--shadow);opacity:0;visibility:hidden;transform:translateY(14px) scale(.9);
  transition:opacity .3s var(--ease-out),transform .3s var(--ease-out),visibility .3s,
             border-color .22s var(--ease),box-shadow .22s var(--ease)}
.totop.show{opacity:1;visibility:visible;transform:none}
.totop:hover{border-color:var(--accent);box-shadow:0 10px 26px -12px var(--glow);transform:translateY(-2px)}
.totop:active{transform:scale(.94)}

/* ---------- misc ---------- */
hr.soft{border:0;height:1px;margin:1.8rem 0;
  background:linear-gradient(90deg,transparent,var(--line-strong),transparent)}
.center{text-align:center}
.pull{position:relative;font-size:clamp(1.15rem,2.6vw,1.52rem);line-height:1.42;color:var(--ink);
  font-weight:600;border-inline-start:4px solid var(--accent);padding-inline-start:20px;margin:1.5rem 0}
[dir="rtl"] .pull{border-inline-start-width:4px}

/* thin, quiet scrollbars on the page itself */
@supports (scrollbar-width: thin){
  html{scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
}

@media (prefers-reduced-motion: no-preference){
  nav.main{will-change:transform}
}
/* Everything above is decoration over a page that must read perfectly still.
   Under a reduced-motion preference the layout, colour and hierarchy stay; the movement goes. */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;scroll-behavior:auto !important}
  .reveal{opacity:1;transform:none}
  .hero::after,.pagehero::after,
  main>section:first-of-type:not(.hero):not(.pagehero)::after{animation:none}
  /* the diagram is simply already drawn, and the chain arrows are solid rather than
     marching — a dashed arrow left frozen would read as a different diagram */
  figure.infographic.ig-draw .infographic-svg svg *{
    stroke-dasharray:none;stroke-dashoffset:0;animation:none}
  figure.infographic .infographic-svg svg line.ig-flow{stroke-dasharray:none;animation:none}
  figure.infographic .infographic-svg svg circle.ig-ring{animation:none}
  figure.infographic picture img{transition:none}
}
