
:root {

  /* type scale */
  --t-d1: clamp(2.75rem, 1.816rem + 3.934vw, 5.75rem);
  --t-d2: clamp(2.125rem, 1.58rem + 2.295vw, 3.875rem);
  --t-d3: clamp(1.75rem, 1.439rem + 1.311vw, 2.75rem);
  --t-h4: clamp(1.25rem, 1.133rem + 0.492vw, 1.625rem);
  --t-h5: clamp(1.063rem, 1.024rem + 0.164vw, 1.188rem);
  --t-lede: clamp(1.125rem, 1.047rem + 0.328vw, 1.375rem);
  --t-body: clamp(1rem, 0.971rem + 0.123vw, 1.094rem);
  --t-sm: clamp(0.875rem, 0.856rem + 0.082vw, 0.938rem);
  --t-xs: clamp(0.75rem, 0.735rem + 0.061vw, 0.797rem);

  /* space scale */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.25rem;
  --s-6: 1.5rem;
  --s-8: 2rem;
  --s-10: 2.5rem;
  --s-12: 3rem;
  --s-16: 4rem;
  --s-20: 5rem;
  --s-24: 6rem;
  --s-32: 8rem;
  --s-40: 10rem;

  /* ---- the default section: the white page ---- */
  --bg:      #ffffff;
  --bg-1:    #f0f4f3;
  --bg-2:    #f0f4f3;
  --bg-3:    #e2eae9;
  --line:    #c8d4d3;
  --line-2:  #b4c4c3;
  --line-3:  #9db2b1;

  /* ---- the inverted section: deep green ---- */
  --alt:      #06262b;
  --alt-2:    #0d363c;
  --card:     #e2eae9;
  --card-2:   #d7e2e1;
  --card-ln:  #9db2b1;
  --card-ln-i:#78908f;
  --input-ln: #6e8f92;
  --alt-ln:   #1a474e;
  --on-alt:   #f0f5f4;
  --on-alt-2: #96aeae;

  /* ---- text on the white page ---- */
  --tx:    #06262b;
  --tx-2:  #445454;
  --tx-3:  #587171;

  /* ---- brand: teal. -----------------------------------------------------
     --brand carries white text at 5.4:1 and is both the text colour and the
     button fill. --brand-hi is the bright teal, which only reaches 3.3:1
     against white and therefore takes DARK text — the same call the live site
     makes on its primary button. --brand-deep is the value used for brand text
     inside the dark sections. Defined in src/palette.mjs. */
  --brand:       #0d5b5f;
  --brand-hi:    #3f9f96;
  --brand-solid: #0d5b5f;
  --brand-deep:  #ece3d2;
  --brand-ink:   #ffffff;

  /* ---- system: saffron. Reserved for live status, data and the diagrams, so
     a mark in this hue always means "the machine", never "the brand". */
  --sys:      #7a5606;   /* the white page */
  --sys-bright: #dda23f;     /* re-pointed inside .invert */
  --sys-deep: #7a5606;

  /* ---- attention ---- */
  --rose:      #ff9d8d;
  --rose-deep: #8f2f1c;
  --sky:       #7fd9ff;

  /* ---- geometry ----
     Tightened for the formal pass. 18/12/8 was a consumer-software radius set
     and it fought the plate the moment the mark stopped being round. The mark
     itself is a 3.5 radius on a 32 grid — about 3px at header size — so the
     page now sits in the same family.

     --r-pill is the one place a full radius survives: the live-status dot and
     the avatar, where a circle is the shape rather than a decision. Buttons,
     tags and filters used to be 999px; pills are the most informal shape in
     interface design and they were undoing the rest of this on every page. */
  --r-lg:   8px;
  --r:      6px;
  --r-sm:   4px;
  --r-pill: 999px;
  --wrap: 78rem;
  --wrap-n: 44rem;
  --border: 1px solid var(--line);

  /* ---- depth ---- */
  /* Retuned for a light page. Black at 40-65% alpha was built for a near-black
     background; on white it reads as grey smudge. These are the brand's own
     deep green at low alpha, which is what makes a light UI look considered
     rather than washed out. */
  --sh-1: 0 1px 2px rgba(5,32,25,.07);
  --sh-2: 0 4px 14px -4px rgba(5,32,25,.14), 0 2px 4px -2px rgba(5,32,25,.08);
  --sh-3: 0 30px 70px -30px rgba(5,32,25,.32), 0 6px 16px -8px rgba(5,32,25,.12);
  --glow: 0 0 0 1px rgba(12,120,98,.24), 0 14px 40px -14px rgba(12,120,98,.28);

  /* ---- motion ---- */
  --ease:     cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-in-out: cubic-bezier(.65,0,.35,1);
  --dur:  .28s;
  --dur-l: .62s;

  --sans: "Segoe UI Variable Display", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Inter var", Inter, ui-sans-serif, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --text: "Segoe UI Variable Text", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Inter var", Inter, ui-sans-serif, system-ui, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: "SF Mono", ui-monospace, SFMono-Regular, "JetBrains Mono", "Cascadia Code", Menlo, Consolas, "Liberation Mono", monospace;
}

/* ---- Tajawal, self-hosted -----------------------------------------------
   Tajawal (Boutros, SIL Open Font License), served from this origin. Pulling it
   from the Google Fonts CDN would put a third-party request on every Arabic
   page, which is the one thing this site does nowhere — no icon font, no
   embedded player, no third-party analytics — and it is the easiest question to
   answer in a PDPL review.

   Only 400 and 700. Tajawal ships 200/300/400/500/700/800/900 with no variable
   axis, and this site uses a dozen intermediate weights between 380 and 680.
   CSS font matching sends everything at or above 600 up to 700 and everything
   below down to 400, so two files cover the range. Adding 500 would sharpen the
   500-560 band; adding 300 would be actively WORSE, because 380-weight text
   would then resolve down to Light instead of up to Regular.

   No unicode-range restriction: Tajawal's Latin was drawn as a companion to its
   Arabic, so inline terms (WASL, GNSS, TPMS, RFID) stay with the Arabic around
   them instead of switching to an unrelated face mid-sentence. */
@font-face {
  font-family: "Tajawal";
  src: url("/tajawal-400.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Tajawal";
  src: url("/tajawal-700.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

html[lang="ar"] {
  --sans: "Tajawal", "IBM Plex Sans Arabic", "Noto Sans Arabic", "Segoe UI", Tahoma, "Geeza Pro", system-ui, sans-serif;
  --text: var(--sans);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scrollbar-color: var(--line-3) var(--bg);
  scrollbar-width: thin;
}
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 99px; border: 3px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--line-3); }

body {
  margin: 0;
  background: var(--bg);
  color: var(--tx);
  font-family: var(--text);
  font-size: var(--t-body);
  line-height: 1.6;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: proportional-nums;
  overflow-x: hidden;
  hanging-punctuation: first last;
}
html[lang="ar"] body { line-height: 1.95; font-size: calc(var(--t-body) * 1.06); }

/* Optical tracking: larger type gets tighter, small type gets looser. */
h1, h2, h3, h4, .display {
  font-family: var(--sans);
  font-weight: 680;
  line-height: 1.04;
  letter-spacing: -0.035em;
  margin: 0 0 0.45em;
  text-wrap: balance;
}
html[lang="ar"] :is(h1,h2,h3,h4,.display) { letter-spacing: 0; line-height: 1.38; font-weight: 700; }

h1 { font-size: var(--t-d2); }
h2 { font-size: var(--t-d3); letter-spacing: -0.03em; }
h3 { font-size: var(--t-h4); letter-spacing: -0.022em; line-height: 1.22; }
html[lang="ar"] h3 { line-height: 1.55; }
h4 { font-size: var(--t-h5); letter-spacing: -0.015em; }

p { margin: 0 0 1.1em; text-wrap: pretty; }
a { color: inherit; text-underline-offset: .2em; text-decoration-thickness: from-font; }
a:hover { color: var(--brand); }
img, svg { max-width: 100%; height: auto; }
ul, ol { margin: 0 0 1.1em; padding-inline-start: 1.1em; }
li { margin-bottom: .4em; text-wrap: pretty; }
strong { font-weight: 640; }

:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 4px; }
::selection { background: var(--brand-solid); color: var(--brand-ink); }

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

/* Cross-document view transitions where supported. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out .18s var(--ease-in-out) both; }
::view-transition-new(root) { animation: vt-in .34s var(--ease-out) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(8px); } }

/* Film grain: one inline SVG turbulence, fixed, very low opacity. It exists to
   stop large DARK areas banding on cheap panels, so on the light page it is
   dialled right back and only earns its keep in the inverted sections. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 999; pointer-events: none;
  opacity: .018; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------- primitives */
.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
@media (min-width: 48rem) { .wrap { width: min(100% - 4rem, var(--wrap)); } }
.narrow { max-width: var(--wrap-n); }
.lede { font-size: var(--t-lede); line-height: 1.5; color: var(--tx-2); font-weight: 380; text-wrap: pretty; }
html[lang="ar"] .lede { line-height: 1.85; font-weight: 400; }
.muted { color: var(--tx-2); }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.kicker {
  display: inline-flex; align-items: center; gap: .6rem;
  font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .18em;
  text-transform: uppercase; color: var(--brand); margin: 0 0 var(--s-6);
  font-weight: 500;
}
html[lang="ar"] .kicker { font-family: var(--text); letter-spacing: 0; text-transform: none; font-size: var(--t-sm); }
.kicker::before { content: ""; width: 1.75rem; height: 1px; background: currentColor; opacity: .55; }

.skip { position: absolute; inset-inline-start: -9999px; top: 0; z-index: 300; background: var(--brand-solid); color: var(--brand-ink); padding: .8rem 1.2rem; font-weight: 650; border-radius: 0 0 var(--r-sm) 0; }
.skip:focus { inset-inline-start: 0; }

section { position: relative; padding-block: clamp(3.75rem, 6.5vw, 6.5rem); }
.section-head { max-width: 46rem; margin-bottom: clamp(2.25rem, 4vw, 3.5rem); }
.hairline { border: 0; border-top: 1px solid var(--line); margin: 0; }

/* light inversion */
.invert { background: var(--alt); color: var(--on-alt); }
.invert .lede, .invert .muted { color: var(--on-alt-2); }
.invert .kicker { color: var(--brand-deep); }
.invert a:hover { color: var(--brand-deep); }
.invert .hairline { border-top-color: var(--alt-ln); }
/* The ambience blobs bloom on the dark sections and only tint the light ones. */
.invert .amb i { opacity: .3; }

/* POLARITY. --brand and --sys are declared for the WHITE page, where they must
   be dark enough to carry 4.5:1. Inside a dark section the same roles need the
   bright values, so they are re-pointed here rather than at every use site.
   Miss this and every kicker, tag and status dot on a dark section drops to
   around 2:1 — passing the audit on the page it was measured on and failing
   everywhere it was not. */
.invert {
  --brand: var(--brand-deep);
  --brand-hi: var(--brand-deep);
  --sys: #dda23f;
  --tx-2: var(--on-alt-2);
  --tx-3: var(--on-alt-2);
  --line: var(--alt-ln);
  --line-2: var(--alt-ln);
  --bg-1: #0d363c;
  --bg-2: #0d363c;
  --bg-3: #0d363c;
  --tx: var(--on-alt);
  --brand-solid: #3f9f96;
  --brand-ink: #06262b;
}

/* --------------------------------------------------------------- ambience */
.amb { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.amb i {
  position: absolute; display: block; border-radius: 50%;
  filter: blur(100px); opacity: .12; will-change: transform;
}
.amb i:nth-child(1) {
  width: 58vw; height: 58vw; inset-block-start: -24vw; inset-inline-start: -14vw;
  background: radial-gradient(circle at 32% 32%, #3f9f96, transparent 66%);
  animation: fl-a 28s var(--ease-in-out) infinite alternate;
}
.amb i:nth-child(2) {
  width: 52vw; height: 52vw; inset-block-start: -12vw; inset-inline-end: -18vw;
  background: radial-gradient(circle at 68% 62%, #0d5b5f, transparent 62%);
  animation: fl-b 36s var(--ease-in-out) infinite alternate;
}
.amb i:nth-child(3) {
  width: 40vw; height: 40vw; inset-block-end: -20vw; inset-inline-start: 28vw;
  background: radial-gradient(circle at 50% 50%, #dda23f, transparent 64%);
  opacity: .14;
  animation: fl-c 42s var(--ease-in-out) infinite alternate;
}
@keyframes fl-a { to { transform: translate3d(7vw, 5vw, 0) scale(1.14); } }
@keyframes fl-b { to { transform: translate3d(-6vw, 8vw, 0) scale(1.09); } }
@keyframes fl-c { to { transform: translate3d(5vw, -6vw, 0) scale(1.16); } }

.grid-bg {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(255,255,255,.026) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,.026) 1px, transparent 1px);
  background-size: 72px 72px;
  mask-image: radial-gradient(ellipse 92% 74% at 50% 0%, #000 26%, transparent 76%);
}
section > .wrap { position: relative; z-index: 1; }

/* ---------------------------------------------------------------- header */
.hdr {
  position: sticky; top: 0; z-index: 80;
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.hdr.scrolled { border-bottom-color: var(--line); background: color-mix(in srgb, var(--bg) 93%, transparent); }
.hdr-in { display: flex; align-items: center; gap: var(--s-6); min-height: 4.75rem; }

.brand { display: inline-flex; align-items: center; gap: .62rem; text-decoration: none; font-family: var(--sans); font-weight: 680; font-size: 1.18rem; letter-spacing: -0.035em; }
html[lang="ar"] .brand { letter-spacing: 0; }
.brand:hover { color: inherit; }
.brand svg { width: 1.7rem; height: 1.7rem; flex: none; }

/* The mark inside the submit button. It sits there quietly at rest and pulses
   while the request is in flight — the one place on this site where motion is
   doing a job rather than decorating. Faster than the brand's idle loop (1.25s
   against 2.6s) because a waiting indicator has to feel responsive, not calm.

   Under reduced motion it simply stops, and the button falls back to the label
   change that was there before. */
.btn-send { gap: .5rem; }
.btn-send .mark { inline-size: 1.05em; block-size: 1.05em; flex: none; opacity: .9; }
.btn-send.sending .mark { animation: send-pulse 1.25s var(--ease-in-out) infinite; }
@keyframes send-pulse {
  0%, 100% { opacity: .95; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.88); }
}
@media (prefers-reduced-motion: reduce) {
  .btn-send.sending .mark { animation: none; opacity: .6; }
}

/* The illustrative banner on worked examples. Deliberately loud: these pages
   contain numbers, and a reader who skims numbers and misses a footnote has
   been misled by us, not by their own carelessness. */
.illus {
  margin-block-start: var(--s-6);
  border-inline-start: 3px solid var(--sys);
  background: var(--bg-2);
  padding: .85rem 1.1rem;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--t-sm);
  color: var(--tx-2);
}
[dir="rtl"] .illus { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.invert .illus { background: var(--alt-2); border-inline-start-color: var(--sys); }

/* ------------------------------------------------ readiness assessment --
   Native radios, restyled. They keep keyboard navigation, screen-reader
   grouping and the no-JavaScript path for free; a custom widget would have
   thrown all three away for a nicer hover state. */
.assess { margin-block-start: var(--s-8); }
.qbox {
  border: var(--border); border-radius: var(--r); background: var(--bg);
  padding: clamp(1.1rem, 2.2vw, 1.6rem); margin-block-end: var(--s-4); min-inline-size: 0;
}
/* Floating the legend is the reliable way to keep <fieldset> semantics while
   drawing an unbroken border: left as-is, the browser notches the border so the
   legend can sit inside it, which reads as a rendering fault at this size. */
.qbox legend { float: left; inline-size: 100%; display: flex; gap: .75rem; align-items: baseline; padding: 0; margin-block-end: .9rem; }
.qn { color: var(--brand); font-size: var(--t-xs); letter-spacing: .12em; flex: none; }
.qt { font-family: var(--sans); font-weight: 620; font-size: var(--t-h6, 1.02rem); line-height: 1.35; letter-spacing: -0.012em; }
html[lang="ar"] .qt { letter-spacing: 0; line-height: 1.6; }
.opts { display: grid; gap: .4rem; clear: both; }
.opt {
  display: flex; gap: .7rem; align-items: flex-start; cursor: pointer;
  padding: .6rem .75rem; border: 1px solid transparent; border-radius: var(--r-sm);
  font-size: var(--t-sm); color: var(--tx-2); transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.opt:hover { background: var(--bg-2); color: var(--tx); }
.opt input { accent-color: var(--brand); margin: .28rem 0 0; flex: none; inline-size: 1rem; block-size: 1rem; }
.opt:has(input:checked) { border-color: var(--brand); background: var(--bg-2); color: var(--tx); }
.opt:has(input:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
.qbox.done { border-color: var(--line-2); }

/* The sticky area rail. Twelve identical question cards ran 5,500px with
   nothing saying which of the four areas you were in — a form with no spine.
   This fills as you answer. It shows PROGRESS, never score: telling someone
   they are doing badly when they are merely early would be the wrong reading
   of the same numbers. */
.axis-rail {
  position: sticky; top: 4.75rem; z-index: 40;
  display: grid; gap: .55rem 1.1rem; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  padding: .85rem 1rem; margin-block-end: var(--s-6);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border: 1px solid var(--card-ln); border-radius: var(--r-sm);
}
.ar-a span { display: block; font-size: var(--t-xs); color: var(--tx-3); margin-block-end: .3rem; }
.ar-m { block-size: 4px; background: var(--bg-3); border-radius: 2px; overflow: hidden; }
.ar-m i { display: block; block-size: 100%; inline-size: 0; background: var(--card-ln); border-radius: 2px; transition: inline-size .4s var(--ease-out), background-color .3s var(--ease); }
.ar-a.full .ar-m i { background: var(--brand); }
.ar-a.full span { color: var(--tx-2); }
@media (max-width: 40rem) { .axis-rail { grid-template-columns: repeat(2, 1fr); } }

/* The four areas, listed as a rail so the homepage block shows what the
   assessment actually asks about rather than just asserting that it does. */
/* Light register by default; the .invert block below restates it, so the same
   component works on both grounds instead of only the dark one. */
.axis-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; background: var(--card-ln); border: 1px solid var(--card-ln); border-radius: var(--r-lg); overflow: hidden; }
.axis-list li { background: var(--card); padding: 1.05rem 1.2rem; display: flex; gap: 1rem; align-items: baseline; font-size: 1.02rem; font-weight: 560; color: var(--tx); }
.axis-list li .mono { font-size: var(--t-xs); letter-spacing: .12em; color: var(--brand); flex: none; min-inline-size: 6.5rem; }
.invert .axis-list { background: #33717a; border-color: #33717a; }
.invert .axis-list li { background: #124850; color: var(--on-alt); }
.invert .axis-list li .mono { color: var(--brand-deep); }
html[lang="ar"] .axis-list li .mono { font-family: var(--text); letter-spacing: 0; }

.assess-bar {
  display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; justify-content: space-between;
  border-block-start: var(--border); padding-block-start: var(--s-6); margin-block-start: var(--s-6);
}
.assess-count { color: var(--tx-3); font-size: var(--t-xs); letter-spacing: .1em; }
.assess-actions { display: flex; gap: .6rem; flex-wrap: wrap; }

/* Results */
.result { margin-block-start: var(--s-10); scroll-margin-block-start: 6rem; }
.result h3 { margin-block: var(--s-8) var(--s-4); font-size: var(--t-h5); }
.band { display: flex; flex-wrap: wrap; gap: .8rem; align-items: baseline; margin-block-end: .4rem; }
.band b { font-family: var(--sans); font-size: var(--t-h4); letter-spacing: -0.02em; }
html[lang="ar"] .band b { letter-spacing: 0; }
.rscore { color: var(--tx-3); font-size: var(--t-sm); }
.axes { display: grid; gap: var(--s-5); margin-block-start: var(--s-7); }
.axis-h { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; font-size: var(--t-sm); margin-block-end: .4rem; }
.axis-v { color: var(--brand); font-variant-numeric: tabular-nums; }
.meter { block-size: 8px; background: var(--bg-3); border-radius: 2px; overflow: hidden; }
.meter i {
  display: block; block-size: 100%; background: var(--brand); border-radius: 2px;
  transition: inline-size .75s var(--ease-out);
}
.axis.low .meter i { background: var(--sys); }
.axis.low .axis-v { color: var(--sys); }

/* THE RESULT IS THE PRODUCT, SO IT IS DRAWN LIKE ONE.
   This was a plain bulleted list with the durations pushed right — on a site
   that owns a bespoke diagram language and uses it everywhere else. The steps
   are a sequence, so they are drawn as one: numbered nodes on a rail, exactly
   the vocabulary the mechanism diagrams use. Built in CSS rather than SVG
   because the content is generated in the browser, and because a CSS rail
   stays legible at any width instead of scaling its type down.
   Logical properties throughout: the rail mirrors itself in Arabic. */
.seq { margin: 0; padding: 0; list-style: none; counter-reset: sq; display: grid; }
.seq li {
  counter-increment: sq; position: relative;
  padding-inline-start: 3.1rem; padding-block: .2rem 1.35rem;
  display: flex; gap: 1rem; align-items: baseline; justify-content: space-between;
  font-size: var(--t-sm);
}
.seq li::before {
  content: counter(sq, decimal-leading-zero);
  position: absolute; inset-inline-start: 0; top: -.1rem;
  inline-size: 2.05rem; block-size: 2.05rem;
  display: grid; place-content: center;
  font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .06em;
  color: var(--brand); background: var(--card);
  border: 1px solid var(--card-ln-i); border-radius: 4px;
}
/* the rail between nodes */
.seq li::after {
  content: ""; position: absolute; inset-inline-start: 1.02rem; top: 2.15rem; bottom: -.1rem;
  inline-size: 1px; background: var(--card-ln);
}
.seq li:last-child { padding-block-end: 0; }
.seq li:last-child::after { display: none; }
.seq .sq-l { flex: 1; color: var(--tx); font-weight: 560; }
.seq .sq-d {
  color: var(--tx-2); font-family: var(--mono); font-size: var(--t-xs); white-space: nowrap;
  border: 1px solid var(--card-ln); border-radius: 3px; padding: .16rem .45rem;
}
html[lang="ar"] .seq li::before, html[lang="ar"] .seq .sq-d { font-family: var(--text); letter-spacing: 0; }
@media (max-width: 34rem) {
  .seq li { flex-wrap: wrap; gap: .45rem; }
  .seq .sq-d { flex: none; }
}
.holds { margin: 0; padding: 0; list-style: none; display: grid; gap: .5rem; }
.holds li {
  border-inline-start: 3px solid var(--sys); background: var(--bg-2);
  padding: .8rem 1.05rem; border-radius: 0 var(--r-sm) var(--r-sm) 0; font-size: var(--t-sm); color: var(--tx-2);
}
[dir="rtl"] .holds li { border-radius: var(--r-sm) 0 0 var(--r-sm); }
/* Arabic never gets the monospace face. It has no Arabic coverage, so the text
   falls back per-character and the letters stop joining — "أسابيع" renders as
   "أسا بيع". The same rule already exists for the diagram labels; these runs
   needed it too. */
html[lang="ar"] :is(.qn, .assess-count, .sq-d, .rscore, .axis-v) {
  font-family: var(--text); letter-spacing: 0;
}
.disclaim { margin-block-start: var(--s-7); padding-block-start: var(--s-5); border-block-start: var(--border); color: var(--tx-3); font-size: var(--t-sm); }

/* ------------------------------------------------------------- the mark --
   Motion belongs to the logo, not to a wrapper, so the same rules drive the
   header lockup, the footer lockup and anywhere else it is placed.

   Geometry note: the shared area is a second circle clipped to the square, so
   it must carry the identical transform to the visible circle or the join
   tears apart mid-animation. Both are driven by --mx/--my. */
.mark { overflow: visible; }
.mark .sq,
.mark .ci { transform-box: view-box; transform-origin: 16px 16px; }

/* Both the painted circle AND the circle inside the knockout mask carry .ci, so
   the hole travels with the shape. Animate one without the other and the mark
   visibly comes apart. */
.mark .ci {
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: transform .55s var(--ease-out);
}
.mark .m-sq {
  transform: scale(var(--sq, 1));
  transition: transform .55s var(--ease-out);
}
.mark .m-sheen { opacity: 0; }

/* Assembly on load: the circle arrives and locks in. Short enough to read as
   confidence rather than a splash screen. Only the circle-bearing variants use
   these; the formal marks are governed by .mk below. */
@keyframes mk-in-ci { from { transform: translate(7px, 7px) scale(.72); opacity: 0; } }
@keyframes mk-in-sq { from { transform: scale(.78); opacity: 0; } }
.mark .ci { animation: mk-in-ci .62s var(--ease-out); }
.mark .sq { animation: mk-in-sq .5s var(--ease-out); }

/* Hover: the join tightens and the shared area brightens — the two halves of
   the proposition pulling together, not apart. */
.brand:hover .mark { --mx: -1.9px; --my: -1.9px; --sq: 1.035; }

/* ---- the formal mark ---------------------------------------------------
   A seal does not bounce. The plate is one path, so there is no join to
   tighten and nothing to assemble; the whole motion budget is a short fade-up
   on load and a 2% acknowledgement on hover.

   Deliberately removed with the circle:
   - pointer magnetism (the lockup leaning toward the cursor)
   - the specular sweep across the square
   - the rotation on hover
   Each was defensible on a startup mark. On a seal they read as fidgeting, and
   restraint is the entire point of this pass. */
@keyframes mk-in { from { transform: scale(.94); opacity: 0; } }
.mark .mk {
  transform-box: view-box; transform-origin: 16px 16px;
  animation: mk-in .42s var(--ease-out);
  transition: transform .3s var(--ease-out);
}
.brand:hover .mark .mk { transform: scale(1.02); }
.brand:active .mark .mk { transform: scale(.985); transition-duration: .1s; }

@media (prefers-reduced-motion: reduce) {
  .mark .ci, .mark .sq, .mark .mk { animation: none; }
  .brand:hover .mark { --mx: 0px; --my: 0px; --sq: 1; }
  .brand:hover .mark .mk, .brand:active .mark .mk { transform: none; }
  .brand .mark { transform: none; }
}

.mainnav { display: flex; align-items: center; gap: 1.35rem; margin-inline-start: auto; }
.mainnav > a { white-space: nowrap; }
.mainnav > a {
  position: relative; text-decoration: none; font-size: var(--t-sm);
  color: var(--tx-2); transition: color .2s var(--ease); padding-block: .35rem;
}
.mainnav > a::after {
  content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 1px;
  background: var(--brand); transform: scaleX(0); transform-origin: var(--o, left);
  transition: transform .3s var(--ease-out);
}
html[dir="rtl"] .mainnav > a::after { --o: right; }
.mainnav > a:hover { color: var(--tx); }
.mainnav > a:hover::after { transform: scaleX(1); }
.mainnav > a[aria-current="page"] { color: var(--tx); font-weight: 600; }
.mainnav > a[aria-current="page"]::after { transform: scaleX(1); opacity: .5; }

.hdr-actions { display: flex; align-items: center; gap: 1rem; }
.lang { font-size: var(--t-sm); text-decoration: none; color: var(--tx-2); padding-inline-start: 1rem; border-inline-start: 1px solid var(--line-2); }
.lang:hover { color: var(--brand); }

.burger {
  display: none; align-items: center; gap: .5rem; margin-inline-start: auto;
  background: none; border: 1px solid var(--line-2); border-radius: var(--r-sm);
  color: var(--tx); font: inherit; font-size: var(--t-sm); padding: .45rem .95rem; cursor: pointer;
}
.burger:hover { border-color: var(--line-3); }
.burger .bars { display: grid; gap: 3px; }
.burger .bars i { display: block; width: 14px; height: 1.5px; background: currentColor; transition: transform .3s var(--ease-out), opacity .2s; }
.hdr.open .burger .bars i:nth-child(1) { transform: translateY(4.5px) rotate(45deg); }
.hdr.open .burger .bars i:nth-child(2) { opacity: 0; }
.hdr.open .burger .bars i:nth-child(3) { transform: translateY(-4.5px) rotate(-45deg); }

/* Platforms menu: a disclosure button with a small panel of links. */
.nav-group { position: relative; }
.nav-toggle { display: inline-flex; align-items: center; gap: .4rem; background: none; border: 0; padding: .35rem 0; font: inherit; font-size: var(--t-sm); color: var(--tx-2); cursor: pointer; white-space: nowrap; }
.nav-toggle:hover, .nav-group.is-open .nav-toggle { color: var(--tx); }
.nav-group.is-current .nav-toggle { color: var(--tx); font-weight: 600; }
.nav-caret { inline-size: .42rem; block-size: .42rem; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform .2s var(--ease); }
.nav-group.is-open .nav-caret { transform: translateY(1px) rotate(-135deg); }
.nav-sub { display: none; position: absolute; inset-inline-start: -1rem; top: calc(100% + .6rem); min-inline-size: 13rem; padding: .4rem; background: var(--bg-1); border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: var(--sh-3); z-index: 5; }
.nav-group.is-open .nav-sub { display: grid; }
.nav-sub a { padding: .6rem .8rem; border-radius: var(--r-sm); font-size: var(--t-sm); color: var(--tx-2); text-decoration: none; white-space: nowrap; }
.nav-sub a:hover { background: var(--bg-2); color: var(--tx); }
.nav-sub a[aria-current="page"] { color: var(--tx); font-weight: 600; }
@media (max-width: 78rem) {
  .nav-group { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; border-bottom: 1px solid var(--line); }
  .nav-toggle { inline-size: 100%; justify-content: space-between; padding: .85rem 0; font-size: 1rem; }
  .nav-sub { position: static; border: 0; box-shadow: none; background: none; padding: 0 0 .6rem; min-inline-size: 0; }
  .nav-sub a { padding: .65rem 1rem; font-size: 1rem; }
}

/* Eight nav items, a CTA and the language toggle is close to the limit here,
   and I cannot render a browser in this environment to measure it. So the
   burger appears a little earlier than strictly necessary: a menu at 1250px is
   a small loss, a nav that wraps into the logo is a broken header. */
@media (max-width: 78rem) {
  .burger { display: inline-flex; }
  .mainnav {
    display: none; position: absolute; inset-inline: 0; top: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--bg-1); border-block: 1px solid var(--line);
    padding-block: .5rem 1.25rem; box-shadow: var(--sh-3);
  }
  .hdr.open .mainnav { display: flex; animation: navdrop .32s var(--ease-out) both; }
  @keyframes navdrop { from { opacity: 0; transform: translateY(-8px); } }
  .mainnav > a { padding: .85rem 0; width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; border-bottom: 1px solid var(--line); font-size: 1rem; }
  .mainnav > a::after { display: none; }
  /* Two buttons plus the language link: side by side on tablets, stacked on
     phones, so neither button is squeezed until its label is clipped. */
  .hdr-actions { width: min(100% - 2.5rem, var(--wrap)); margin: 1.1rem auto 0; flex-wrap: wrap; gap: .75rem 1rem; }
  .hdr-actions .btn { flex: 1 1 13rem; justify-content: center; }
  .lang { border: 0; padding-inline-start: 0; margin-inline-start: auto; }
}

/* Scroll progress hairline */
.progress { position: fixed; inset-inline: 0; top: 0; height: 2px; z-index: 90; background: linear-gradient(90deg, var(--brand-solid), var(--sys)); transform-origin: 0 50%; transform: scaleX(0); }
html[dir="rtl"] .progress { transform-origin: 100% 50%; }

/* --------------------------------------------------------------- buttons */
.btn {
  position: relative; display: inline-flex; align-items: center; gap: .55rem;
  padding: .85rem 1.5rem; border-radius: var(--r-sm);
  font-family: var(--text); font-size: var(--t-sm); font-weight: 620;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  white-space: nowrap; overflow: hidden; isolation: isolate;
  transition: transform .2s var(--ease-out), box-shadow .25s var(--ease), background-color .2s var(--ease), border-color .2s var(--ease), color .2s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn-1 { background: var(--brand-solid); color: var(--brand-ink); box-shadow: var(--sh-2); }
.btn-1:hover { background: #0a4a4d; color: var(--brand-ink); box-shadow: var(--sh-2); }
/* The specular sheen that swept across this button on hover was removed in the
   formal pass, along with the violet glow it used to bloom. A button that
   glints is selling; this one is just a button. */
.btn-2 { border-color: var(--line-3); color: var(--tx); background: color-mix(in srgb, var(--bg-2) 60%, transparent); }
.btn-2:hover { border-color: var(--tx-3); background: var(--bg-2); }
.invert .btn-1 { background: #3f9f96; color: #06262b; }
.invert .btn-1:hover { background: #ece3d2; color: #06262b; box-shadow: 0 12px 34px -12px rgba(5,32,25,.5); }
.invert .btn-2 { border-color: #b9c9ce; color: var(--on-alt); background: transparent; }
.invert .btn-2:hover { border-color: var(--on-alt); background: #0d363c; }
.btn .ar { transition: transform .22s var(--ease-out); }
html[dir="rtl"] .btn .ar { transform: scaleX(-1); }
.btn:hover .ar { transform: translateX(4px); }
html[dir="rtl"] .btn:hover .ar { transform: scaleX(-1) translateX(4px); }

.txtlink { display: inline-flex; align-items: center; gap: .45rem; text-decoration: none; color: var(--brand); font-weight: 620; font-size: var(--t-sm); }
.invert .txtlink { color: var(--brand-deep); }
.txtlink .ar { transition: transform .22s var(--ease-out); }
html[dir="rtl"] .txtlink .ar { transform: scaleX(-1); }
.txtlink:hover .ar { transform: translateX(4px); }
html[dir="rtl"] .txtlink:hover .ar { transform: scaleX(-1) translateX(4px); }

/* ------------------------------------------------------------------ hero */
.hero { padding-block: clamp(4rem, 9vw, 7.5rem) clamp(3rem, 5vw, 4.5rem); }
.hero-grid { display: grid; gap: clamp(2.5rem, 5vw, 4.5rem); grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); align-items: center; }
@media (max-width: 62rem) { .hero-grid { grid-template-columns: 1fr; } }
.hero h1 { font-size: var(--t-d1); max-width: 15ch; font-weight: 700; }
/* The emphasis in the headline was a four-stop gradient shimmering on a 9s
   loop. It was the loudest thing on the page and the least formal — a headline
   that will not sit still is a headline nobody reads twice. Solid brand colour
   does the same job of marking the phrase, and it survives being printed,
   screenshotted into a tender document, or read by anyone with
   prefers-reduced-motion set. */
.hero h1 .grad { color: var(--brand); }
.hero .lede { max-width: 46ch; margin-top: 1.7rem; }
/* A hero with nothing beside it gets the full measure back — 46ch inside a
   1248px wrap is what left three column-bands of zero ink on 22 pages. */
.hero-solo h1 { max-width: 22ch; }
.hero .lede.wide { max-width: 68ch; }
.hero-cta { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: var(--s-10); }
.hero-note { margin-top: var(--s-8); font-size: var(--t-sm); color: var(--tx-3); display: flex; align-items: center; gap: .6rem; }
.pulse { width: .5rem; height: .5rem; border-radius: 50%; background: var(--sys); flex: none; position: relative; }
.pulse::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--sys); animation: ring 2.6s var(--ease-out) infinite; }
@keyframes ring { 0% { transform: scale(1); opacity: .7; } 100% { transform: scale(3.4); opacity: 0; } }

/* stat strip */
.strip {
  margin-top: clamp(3.5rem, 7vw, 5.5rem);
  border: var(--border); border-radius: var(--r-lg);
  background: linear-gradient(180deg, color-mix(in srgb, var(--bg-2) 88%, transparent), color-mix(in srgb, var(--bg-1) 88%, transparent));
  backdrop-filter: blur(8px);
  display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  overflow: hidden; box-shadow: var(--sh-2);
}
/* When the strip carries a label, the label takes the gap above and sits
   directly on the strip, instead of floating halfway between it and the CTA. */
.kicker + .strip { margin-top: 0; }
.hero-grid + .kicker { margin-top: clamp(3.5rem, 7vw, 5.5rem); }
.strip > div { padding: var(--s-6) var(--s-6); border-inline-end: 1px solid var(--line); }
.strip > div:last-child { border-inline-end: 0; }
@media (max-width: 52rem) { .strip > div { border-inline-end: 0; border-bottom: 1px solid var(--line); } .strip > div:last-child { border-bottom: 0; } }
.strip b { display: block; font-family: var(--sans); font-size: clamp(1.6rem, 2.6vw, 2.1rem); font-weight: 680; letter-spacing: -0.04em; font-variant-numeric: tabular-nums; line-height: 1.05; }
html[lang="ar"] .strip b { letter-spacing: 0; }
.strip span { display: block; margin-top: .45rem; font-size: var(--t-sm); color: var(--tx-2); }
.strip b i { font-style: normal; font-size: .48em; font-weight: 600; color: var(--tx-2); letter-spacing: 0; }
html[lang="ar"] .strip b i { font-size: .5em; }

/* -------------------------------------------------------------- surfaces
   The fill used to be bg-2 over bg-1 — two identical values, so the gradient
   did nothing and the card sat at 1.08:1 against the page with a 1.39:1
   hairline holding it together. It was not visible as an object. Fill and
   edge are now their own tokens (see src/palette.mjs) and the gradient has
   two ends. */
.card {
  position: relative; border: 1px solid var(--card-ln); border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--card), var(--card-2));
  padding: clamp(1.5rem, 2.4vw, 2rem); overflow: hidden; isolation: isolate;
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease);
}
/* top inner highlight — the detail that stops dark cards looking flat */
.card::before {
  content: ""; position: absolute; inset-inline: 0; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.14) 22%, rgba(255,255,255,.14) 78%, transparent);
}
/* cursor spotlight, driven by --mx/--my set in JS */
.card::after {
  content: ""; position: absolute; inset: -1px; z-index: -1; opacity: 0;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%), rgba(164,143,255,.14), transparent 62%);
  transition: opacity .35s var(--ease);
}
.card:hover::after, a.card:focus-visible::after { opacity: 1; }
/* A whole card that is one link is a user-interface component, and its edge
   is what says so — that boundary carries 3:1, where a static card's does not
   have to. */
a.card { text-decoration: none; display: flex; flex-direction: column; border-color: var(--card-ln-i); }
a.card:hover { border-color: var(--brand); transform: translateY(-4px); box-shadow: var(--sh-3); color: inherit; }
.card .idx { font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); letter-spacing: .14em; }
.card h3 { margin-top: .75rem; }
.card p { color: var(--tx-2); font-size: var(--t-sm); margin-bottom: 0; }
.card .txtlink { margin-top: auto; padding-top: var(--s-6); }

.invert .card { background: #124850; border-color: #33717a; box-shadow: none; }
.invert a.card { border-color: #3d848d; }
.invert a.card:hover { border-color: var(--brand-deep); }
.invert .card::before { background: none; }
.invert .card::after { background: radial-gradient(340px circle at var(--mx,50%) var(--my,0%), rgba(75,47,181,.08), transparent 62%); }
.invert .card p { color: var(--on-alt-2); }
.invert .card .idx { color: #62808f; }
.invert a.card:hover { border-color: #9db2b9; box-shadow: 0 18px 40px -18px rgba(6,18,26,.28); }

.bento { display: grid; gap: var(--s-4); grid-template-columns: repeat(6, 1fr); }
.bento > * { grid-column: span 6; }
@media (min-width: 54rem) {
  .bento > .b2 { grid-column: span 2; }
  .bento > .b3 { grid-column: span 3; }
  .bento > .b4 { grid-column: span 4; }
}

/* ------------------------------------------------------- platform preview */
/* The homepage tabbed mock-up of each platform. With scripting (html.js) only
   the selected panel shows; without it, the tabs are hidden and every panel
   is shown in turn. Status tones: teal = fine, saffron = due soon, brick red =
   urgent. */
.pv-tabs { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-6); }
html:not(.js) .pv-tabs { display: none; }
.pv-tab {
  font: inherit; font-size: var(--t-sm); font-weight: 620; cursor: pointer;
  padding: .6rem 1rem; border-radius: var(--r); border: 1px solid var(--card-ln-i);
  background: var(--bg); color: var(--tx-2);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.pv-tab:hover { border-color: var(--brand); color: var(--tx); }
.pv-tab[aria-selected="true"] { background: var(--brand); border-color: var(--brand); color: #fff; }
.pv-tab:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.pv-frame {
  border: 1px solid var(--card-ln); border-radius: var(--r-lg); overflow: hidden;
  background: linear-gradient(180deg, var(--card), var(--card-2)); box-shadow: var(--sh-3);
}
.js .pv-panel:not(.is-on) { display: none; }
html:not(.js) .pv-panel + .pv-panel { border-top: 1px solid var(--card-ln); }
.pv-panel:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.pv-bar { display: flex; align-items: center; gap: var(--s-3); padding: .7rem 1rem; border-bottom: 1px solid var(--card-ln); background: rgba(255,255,255,.45); }
.pv-dots { display: inline-flex; gap: .35rem; }
.pv-dots i { inline-size: .6rem; block-size: .6rem; border-radius: var(--r-pill); background: var(--card-ln-i); }
.pv-host { font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); }
.pv-bar .tag { margin-inline-start: auto; }
.pv-body { padding: clamp(1.25rem, 3vw, 2rem); }
.pv-grid { display: grid; gap: var(--s-6); }
@media (min-width: 54rem) { .pv-grid { grid-template-columns: 3fr 2fr; align-items: start; } }
.pv-sub { font-size: var(--t-xs); color: var(--tx-3); margin: 0 0 var(--s-2); }
.pv-title { font-size: var(--t-h5); line-height: 1.4; margin: 0 0 var(--s-4); }
.pv-meta { display: grid; gap: var(--s-2); margin: 0; }
.pv-meta div { display: flex; justify-content: space-between; gap: var(--s-4); padding-bottom: var(--s-2); border-bottom: 1px dashed var(--card-ln-i); }
.pv-meta dt { color: var(--tx-3); font-size: var(--t-sm); }
.pv-meta dd { margin: 0; font-weight: 620; font-size: var(--t-sm); text-align: end; }
.pv-side { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-4); }
.pv-checks, .pv-StatusList { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); inline-size: 100%; }
.pv-checks li { display: flex; align-items: center; gap: var(--s-2); font-size: var(--t-sm); }
.pv-checks .pv-ok { color: var(--brand); font-weight: 700; }
.pv-row {
  display: flex; justify-content: space-between; align-items: center; gap: var(--s-4);
  padding: .7rem .9rem; background: var(--bg); border: 1px solid var(--card-ln); border-inline-start: 3px solid var(--brand); border-radius: var(--r);
}
.pv-row div { display: grid; gap: .15rem; min-inline-size: 0; }
.pv-row strong { font-size: var(--t-sm); }
.pv-row span { font-size: var(--t-xs); color: var(--tx-3); overflow-wrap: anywhere; }
.pv-row b { font-size: var(--t-xs); white-space: nowrap; color: var(--brand); }
.pv-row.pv-warn { border-inline-start-color: var(--sys-deep); }
.pv-row.pv-warn b { color: var(--sys-deep); }
.pv-row.pv-urgent { border-inline-start-color: var(--rose-deep); }
.pv-row.pv-urgent b { color: var(--rose-deep); }

/* Pricing cards on the homepage. */
.card .pv-price { font-size: var(--t-h4); font-weight: 700; color: var(--tx); margin: var(--s-2) 0 0; }
.pv-tier-list { list-style: none; margin: var(--s-4) 0 0; padding: 0; display: grid; gap: var(--s-2); font-size: var(--t-sm); color: var(--tx-2); }
.pv-tier-list li { padding-inline-start: 1.25rem; position: relative; }
.pv-tier-list li::before { content: "✓"; position: absolute; inset-inline-start: 0; color: var(--brand); font-weight: 700; }
.card.pv-featured { border-color: var(--brand); box-shadow: var(--sh-3); }

/* ---------------------------------------------------------------- videos */
/* Homepage video cards: a 16:9 thumbnail flush with the card's top edge, then
   the title. The play mark sits in a corner, clear of the title YouTube draws on most
   thumbnails (on the left, in English, whatever the page direction), in the
   brand colour rather than YouTube red. */
.vid { padding-top: 0; }
.vid-th { position: relative; display: block; margin-inline: calc(-1 * clamp(1.5rem, 2.4vw, 2rem)); margin-bottom: .25rem; aspect-ratio: 16 / 9; overflow: hidden; background: #06262b; }
.vid-th img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; transition: transform .5s var(--ease-out); }
a.vid:hover .vid-th img { transform: scale(1.04); }
.vid-play { position: absolute; bottom: .75rem; right: .75rem; inline-size: 2.25rem; block-size: 2.25rem; filter: drop-shadow(0 3px 8px rgba(0,0,0,.4)); }
.vid-play circle { fill: var(--brand); opacity: .92; }
.vid-play path { fill: #fff; }
.vid h3 { font-size: var(--t-h5); line-height: 1.4; }
.videos-more { margin-top: var(--s-8); margin-bottom: 0; }

/* ------------------------------------------------------------------ tabs */
.tabs { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: var(--s-8); }
.tabs button {
  font: inherit; font-size: var(--t-sm); font-weight: 560;
  background: transparent; color: var(--tx-2);
  border: 1px solid var(--line-2); border-radius: var(--r-sm); padding: .58rem 1.15rem; cursor: pointer;
  transition: all .2s var(--ease);
}
.tabs button:hover { color: var(--tx); border-color: var(--line-3); }
.tabs button[aria-selected="true"] { background: var(--brand-solid); border-color: var(--brand-solid); color: var(--brand-ink); }
.invert .tabs button { border-color: #c3d2d7; color: var(--on-alt-2); }
.invert .tabs button[aria-selected="true"] { background: #3f9f96; border-color: #3f9f96; color: #06262b; }
.tabpanel[hidden] { display: none; }
.tabpanel { animation: fade .4s var(--ease-out) both; }
@keyframes fade { from { opacity: 0; transform: translateY(6px); } }

/* ----------------------------------------------------------------- steps */
.steps { display: grid; gap: var(--s-6); grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr)); counter-reset: s; }
.step { position: relative; border-top: 2px solid var(--line-2); padding-top: var(--s-5); counter-increment: s; transition: border-color .5s var(--ease); }
.step::before { content: counter(s, decimal-leading-zero); font-family: var(--mono); font-size: var(--t-xs); color: var(--brand); letter-spacing: .12em; }
.step h3 { margin: .6rem 0 .4rem; font-size: var(--t-h5); }
.step p { font-size: var(--t-sm); color: var(--tx-2); margin: 0; }
.step.on { border-top-color: var(--brand); }
.invert .step { border-top-color: var(--alt-ln); }
.invert .step::before { color: var(--brand-deep); }
.invert .step.on { border-top-color: var(--brand-deep); }
.invert .step p { color: var(--on-alt-2); }

/* --------------------------------------------------------------- metrics */
.metrics { display: grid; gap: var(--s-8); grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
.metric b { display: block; font-family: var(--sans); font-size: clamp(2.3rem, 4.6vw, 3.4rem); font-weight: 700; letter-spacing: -0.045em; line-height: 1; font-variant-numeric: tabular-nums; }
html[lang="ar"] .metric b { letter-spacing: 0; }
.metric b .u { color: var(--brand); }
.invert .metric b .u { color: var(--brand-deep); }
.metric span { display: block; margin-top: .65rem; font-size: var(--t-sm); color: var(--tx-2); }

/* ----------------------------------------------------------------- table */
.tbl { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.tbl th { text-align: start; font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .13em; text-transform: uppercase; color: var(--tx-3); font-weight: 500; padding-bottom: .85rem; border-bottom: 1px solid var(--line); }
html[lang="ar"] .tbl th { font-family: var(--text); letter-spacing: 0; text-transform: none; }
.tbl td { padding: .85rem 0; border-bottom: 1px solid var(--line); vertical-align: baseline; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl td.num { font-variant-numeric: tabular-nums; font-weight: 640; white-space: nowrap; }
.invert .tbl th, .invert .tbl td { border-bottom-color: var(--alt-ln); }
.tag { display: inline-block; font-size: var(--t-xs); padding: .18rem .55rem; border-radius: 3px; white-space: nowrap; font-weight: 550; }
.tag.ok { background: rgba(52,216,240,.14); color: var(--sys); }
.tag.warn { background: rgba(255,157,141,.14); color: var(--rose); }
.invert .tag.ok { background: #ddf1f6; color: var(--sys-deep); }
.invert .tag.warn { background: #fbe4de; color: var(--rose-deep); }

/* Product status badges. Logical properties only, so the badge sits at the
   start edge in both directions without an RTL override. */
.products .card .tag { align-self: flex-start; }
.products .card h3 .alt { font-weight: 400; color: var(--tx-3); font-size: .8em; margin-inline-start: .35em; }
.tag.st-live { background: rgba(13,91,95,.12); color: var(--brand); }
.tag.st-dev { background: rgba(122,86,6,.12); color: var(--sys); }
.tag.st-research { background: rgba(127,140,150,.16); color: var(--tx-2); }
.tag.st-beta { background: rgba(122,86,6,.12); color: var(--sys); }

/* Platform cards: what it does, its address, then "learn more" and a direct
   sign-in link pinned to the bottom so the three cards line up. */
.platform-card { display: flex; flex-direction: column; gap: var(--s-3); }
.platform-card p { margin: 0; }
.platform-host { font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); letter-spacing: .02em; }
.platform-links { display: flex; flex-wrap: wrap; gap: var(--s-5); margin-top: auto; padding-top: var(--s-3); border-top: 1px solid var(--line); }

/* ------------------------------------------------------------------ code */
.codewrap { border: var(--border); border-radius: var(--r-lg); overflow: hidden; background: #04070a; }
.codebar { display: flex; align-items: center; gap: .5rem; padding: .7rem 1rem; border-bottom: 1px solid var(--line); background: var(--bg-1); }
.codebar .dots { display: flex; gap: .35rem; }
.codebar .dots i { width: .55rem; height: .55rem; border-radius: 50%; background: var(--line-3); display: block; }
.codebar .fname { font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); direction: ltr; }
pre.code { margin: 0; padding: 1.4rem; overflow-x: auto; direction: ltr; text-align: left; font-family: var(--mono); font-size: .8rem; line-height: 1.78; color: #cfe0dc; }
pre.code .k { color: #6fd8c0; }
pre.code .n { color: #ffc258; }

/* --------------------------------------------------------------- marquee */
.plogo { display: inline-flex; align-items: center; gap: .55rem; direction: ltr; }
.plogo img { display: block; max-inline-size: none; }
.plogo-t { font-family: var(--sans); font-weight: 600; font-size: 1.2rem; letter-spacing: -0.01em; color: #5f6368; white-space: nowrap; }
.marquee .plogo { filter: grayscale(1); opacity: .6; transition: filter .25s var(--ease), opacity .25s var(--ease); }
.marquee li:hover .plogo { filter: none; opacity: 1; }
.marquee li { display: flex; align-items: center; }
.pcard-logo { block-size: 40px; display: flex; align-items: center; margin-block-end: 1rem; }
.tm-note { margin-block-start: var(--s-6, 1.5rem); font-size: .85rem; color: var(--tx-3); max-inline-size: 70ch; }
.marquee { overflow: hidden; mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
/* --run is the width of ONE laid copy, set by the script once it knows how
   many copies it took to overflow the container. Translating by exactly that
   distance is what puts the seam off-screen; translateX(-50%) only worked
   when a single copy happened to be wider than the viewport, which it was
   not. --marquee-dur is derived from the same distance so the speed is
   constant however long the list gets. */
.marquee ul { display: flex; gap: 3.75rem; list-style: none; padding: 0; margin: 0; width: max-content; animation: slide var(--marquee-dur, 38s) linear infinite; }
.marquee li { font-family: var(--sans); font-size: 1.35rem; font-weight: 640; letter-spacing: -0.03em; color: var(--tx-3); white-space: nowrap; margin: 0; transition: color .25s var(--ease); }
html[lang="ar"] .marquee li { letter-spacing: 0; }
.marquee li:hover { color: var(--tx); }
@keyframes slide { to { transform: translateX(calc(-1 * var(--run, 50%))); } }
html[dir="rtl"] .marquee ul { animation-name: slideR; }
@keyframes slideR { to { transform: translateX(var(--run, 50%)); } }
/* Pause on focus as well as hover: hover is not available to a keyboard or a
   screen-reader user, so hover-only pausing gives the pointer user a control
   that nobody else has. Still short of a real WCAG 2.2.2 mechanism — that
   needs a visible button — but it is parity rather than half a feature. */
.marquee:hover ul, .marquee:focus-within ul { animation-play-state: paused; }
.invert .marquee li { color: #7b8f99; }
.invert .marquee li:hover { color: var(--on-alt); }

/* ------------------------------------------------------------ briefing */
/* Each entry is somebody else's headline and our reading of it, so the two
   have to look different. The headline is set as reported speech — quieter
   than a heading we wrote — and our commentary sits on a brand rule, which is
   the same device the prose pull-quotes use. Nobody should have to work out
   which words are ours. */
.brief-updated { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .1em;
  text-transform: uppercase; color: var(--tx-3); margin-block-end: var(--s-6); }
html[lang="ar"] .brief-updated { font-family: var(--text); letter-spacing: 0; text-transform: none; font-size: var(--t-sm); }

.briefs { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-6); }
.brief { border-block-end: 1px solid var(--line); padding-block-end: var(--s-6); }
.briefs > li:last-child { border-block-end: 0; padding-block-end: 0; }

.brief-meta { display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline; margin-block-end: .7rem; }
.brief-meta time { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .08em; color: var(--tx-3); }
.brief-src { font-size: var(--t-xs); font-weight: 620; color: var(--tx-2); }
.brief-src::before { content: "·"; margin-inline-end: .5rem; color: var(--tx-3); }
.brief-tag { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .07em;
  color: var(--tx-3); border: 1px solid var(--card-ln); border-radius: 3px; padding: .1rem .42rem; }
html[lang="ar"] .brief-meta time, html[lang="ar"] .brief-tag { font-family: var(--text); letter-spacing: 0; }

.brief-h { font-size: var(--t-h4); line-height: 1.28; font-weight: 560; color: var(--tx-2);
  margin: 0 0 var(--s-5); max-inline-size: 46ch; }

.brief-take { border-inline-start: 2px solid var(--brand); padding-inline-start: 1.1rem;
  margin-block-end: var(--s-5); }
.brief-label { display: block; font-family: var(--mono); font-size: var(--t-xs);
  letter-spacing: .13em; text-transform: uppercase; color: var(--brand); margin-block-end: .35rem; }
html[lang="ar"] .brief-label { font-family: var(--text); letter-spacing: 0; text-transform: none; font-size: var(--t-sm); font-weight: 620; }
.brief-take p { margin: 0; color: var(--tx); max-inline-size: 64ch; }

/* --------------------------------------------------------- obligations */
/* Wide content scrolls inside its own container; the page body never does. */
.matrix { overflow-x: auto; border: 1px solid var(--card-ln); border-radius: var(--r-lg); background: var(--card); }
.matrix table { border-collapse: collapse; inline-size: 100%; min-inline-size: 46rem; font-size: var(--t-sm); }
.matrix caption { caption-side: top; text-align: start; padding: 1.15rem 1.3rem 0; color: var(--tx-2); font-size: var(--t-sm); max-inline-size: 62ch; }
.matrix th, .matrix td { text-align: start; padding: .95rem 1.1rem; border-block-end: 1px solid var(--card-ln); vertical-align: top; }
.matrix thead th { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .12em; text-transform: uppercase; color: var(--tx-3); font-weight: 500; }
html[lang="ar"] .matrix thead th { font-family: var(--text); letter-spacing: 0; text-transform: none; font-size: var(--t-sm); }
.matrix tbody th { font-weight: 640; color: var(--tx); inline-size: 14rem; }
.matrix td { color: var(--tx-2); }
.matrix td.who { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .06em; color: var(--brand); white-space: nowrap; }
html[lang="ar"] .matrix td.who { font-family: var(--text); letter-spacing: 0; font-size: var(--t-sm); white-space: normal; }
.matrix td.produce { color: var(--tx); }
.matrix tbody tr:last-child th, .matrix tbody tr:last-child td { border-block-end: 0; }

/* ------------------------------------------------------------ case cards */
.cases { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(18.5rem, 1fr)); }
.case { position: relative; border: var(--border); border-radius: var(--r-lg); padding: clamp(1.6rem, 2.4vw, 2.1rem); text-decoration: none; display: flex; flex-direction: column; overflow: hidden; isolation: isolate; background: linear-gradient(180deg, var(--card), var(--card-2)); border-color: var(--card-ln-i); transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease); }
.case::before { content: ""; position: absolute; inset-inline: 0; top: 0; height: 1px; background: linear-gradient(90deg, transparent, rgba(255,255,255,.14) 22%, rgba(255,255,255,.14) 78%, transparent); }
.case:hover { border-color: var(--brand); transform: translateY(-4px); box-shadow: var(--sh-3); color: inherit; }
.case .meta { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase; color: var(--brand); margin-bottom: var(--s-5); }
html[lang="ar"] .case .meta { font-family: var(--text); letter-spacing: 0; text-transform: none; font-size: var(--t-sm); }
.case .big { font-family: var(--sans); font-size: clamp(2.4rem, 4vw, 3rem); font-weight: 700; letter-spacing: -0.045em; line-height: 1; margin-bottom: .7rem; }
html[lang="ar"] .case .big { letter-spacing: 0; }
.case h3 { font-size: var(--t-h5); font-weight: 620; }
.case p { color: var(--tx-2); font-size: var(--t-sm); }
.invert .case { background: #124850; border-color: #3d848d; }
.invert .case:hover { border-color: var(--brand-deep); }
.invert .case::before { background: none; }
.invert .case .meta { color: var(--brand-deep); }
.invert .case p { color: var(--on-alt-2); }

/* ------------------------------------------------------------------- faq */
details.q { border-bottom: 1px solid var(--line); }
.invert details.q { border-bottom-color: var(--alt-ln); }
details.q summary { cursor: pointer; list-style: none; padding: 1.3rem 2.75rem 1.3rem 0; font-weight: 600; font-size: var(--t-h5); position: relative; transition: color .2s var(--ease); }
html[dir="rtl"] details.q summary { padding: 1.3rem 0 1.3rem 2.75rem; }
details.q summary:hover { color: var(--brand); }
.invert details.q summary:hover { color: var(--brand-deep); }
details.q summary::-webkit-details-marker { display: none; }
details.q summary::after { content: ""; position: absolute; inset-inline-end: .4rem; top: 1.7rem; width: .72rem; height: .72rem; border-inline-end: 2px solid var(--brand); border-block-end: 2px solid var(--brand); transform: rotate(45deg); transition: transform .28s var(--ease-out); }
.invert details.q summary::after { border-color: var(--brand-deep); }
details.q[open] summary::after { transform: rotate(-135deg); }
details.q .a { padding-bottom: 1.5rem; color: var(--tx-2); max-width: 64ch; }
.invert details.q .a { color: var(--on-alt-2); }

/* ------------------------------------------------------------------ form */
.fgrid { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.f { display: flex; flex-direction: column; gap: .4rem; }
.f.full { grid-column: 1 / -1; }
.f label { font-size: var(--t-sm); font-weight: 560; color: var(--tx-2); }
.f .rq { color: var(--brand); }
.f input, .f select, .f textarea { font: inherit; font-size: 1rem; padding: .78rem .9rem; width: 100%; background: var(--bg-1); color: var(--tx); border: 1px solid var(--input-ln); border-radius: var(--r-sm); transition: border-color .2s var(--ease), box-shadow .2s var(--ease); }
.f textarea { min-height: 10.5rem; resize: vertical; }
.f input:focus, .f select:focus, .f textarea:focus { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(164,143,255,.20); outline: none; }
.f input::placeholder, .f textarea::placeholder { color: var(--tx-3); }
.consent { display: flex; gap: .7rem; align-items: flex-start; font-size: var(--t-sm); color: var(--tx-2); }
.consent input { width: 1.05rem; height: 1.05rem; margin-top: .38rem; flex: none; accent-color: var(--brand); }
.hp { position: absolute; inset-inline-start: -9999px; opacity: 0; height: 0; overflow: hidden; }
.status { margin-top: var(--s-5); padding: 1rem 1.15rem; border-radius: var(--r-sm); font-size: var(--t-sm); }
.status[data-s="ok"] { background: rgba(62,224,193,.12); color: var(--sys); }
.status[data-s="err"] { background: rgba(255,157,141,.12); color: var(--rose-deep); }
/* --rose is the dark-section value; on the white page it is ~2:1 and unreadable. */
.invert .status[data-s="err"] { color: var(--rose); }
.status[hidden] { display: none; }

/* ------------------------------------------------------------- careers */
/* The application form needs JavaScript, because the CV is base64-encoded in
   the browser and posted as JSON — a native multipart POST would reach
   /api/apply in a format it does not parse. So the form is HIDDEN until the
   js class lands on <html>, and the email route is shown instead. Neither
   path is a dead end. */
.jsonly { display: none; }
html.js .jsonly { display: block; }
html.js .nojs { display: none; }

.roles { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr)); }
.role h3 { margin: 0 0 .35rem; font-size: var(--t-h4); }
.role p { color: var(--tx-2); font-size: var(--t-sm); }
.role .role-meta { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .1em;
  text-transform: uppercase; color: var(--tx-3); margin-bottom: .85rem; }
.role .role-cta { margin-top: var(--s-5); }

.empty-note { border: var(--border); border-radius: var(--r-lg); background: var(--bg-1);
  padding: clamp(1.4rem, 2.2vw, 1.9rem); max-width: 62ch; }
.empty-note p { color: var(--tx-2); }
.empty-note p:last-child { margin-bottom: 0; }

.f label .hint { font-weight: 400; color: var(--tx-3); }

/* File picker. The real <input type=file> stays in the document and in the tab
   order — it is only visually collapsed — so keyboard and screen-reader users
   get the native control, and the focus ring is forwarded to the label. */
.filepick { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }
.filepick input[type="file"] { position: absolute; inline-size: 1px; block-size: 1px;
  opacity: 0; overflow: hidden; clip-path: inset(50%); }
.fp-btn { display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
  font-size: var(--t-sm); font-weight: 620; color: var(--tx); background: var(--bg-2);
  border: 1px solid var(--input-ln); border-radius: var(--r-sm); padding: .62rem 1.05rem;
  transition: border-color .2s var(--ease), background .2s var(--ease); }
.fp-btn:hover { border-color: var(--brand); background: var(--bg-1); }
.filepick input[type="file"]:focus-visible + .fp-btn { outline: 2px solid var(--brand); outline-offset: 2px; }
.fp-name { font-size: var(--t-sm); color: var(--tx-3); word-break: break-all; }
.fp-name[data-has] { color: var(--tx); font-weight: 560; }
.fp-rm { background: none; border: 0; padding: .2rem .3rem; cursor: pointer;
  font: inherit; font-size: var(--t-xs); color: var(--tx-3); text-decoration: underline; }
.fp-rm:hover { color: var(--rose); }
.fp-hint { font-size: var(--t-xs); color: var(--tx-3); margin: .5rem 0 0; }

.split { display: grid; gap: clamp(2.5rem,5vw,4rem); grid-template-columns: minmax(0,1.55fr) minmax(0,1fr); align-items: start; }
@media (max-width: 58rem) { .split { grid-template-columns: 1fr; } }
.dl dt { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .13em; text-transform: uppercase; color: var(--tx-3); margin-top: var(--s-5); }
html[lang="ar"] .dl dt { font-family: var(--text); letter-spacing: 0; text-transform: none; }
.dl dd { margin: .35rem 0 0; }

/* ----------------------------------------------------------------- prose */
.prose { max-width: 40rem; }
.prose h2 { font-size: clamp(1.4rem, 2.5vw, 1.85rem); margin-top: var(--s-12); }
.prose h3 { margin-top: var(--s-8); font-size: var(--t-h5); }
.prose p, .prose li { color: var(--tx-2); }
.prose > p:first-of-type { font-size: var(--t-lede); line-height: 1.5; color: var(--tx); }
html[lang="ar"] .prose > p:first-of-type { line-height: 1.85; }
.invert .prose p, .invert .prose li { color: var(--on-alt-2); }
.invert .prose > p:first-of-type { color: var(--on-alt); }
.invert .prose strong { color: var(--on-alt); }
blockquote.pull { margin: var(--s-10) 0; padding-inline-start: var(--s-6); border-inline-start: 2px solid var(--brand); font-family: var(--sans); font-size: clamp(1.2rem,2.1vw,1.45rem); line-height: 1.4; letter-spacing: -0.02em; color: var(--tx); font-weight: 600; }
html[lang="ar"] blockquote.pull { line-height: 1.75; letter-spacing: 0; }
.invert blockquote.pull { border-inline-start-color: var(--brand-deep); color: var(--on-alt); }

.byline { display: flex; align-items: center; gap: .8rem; font-size: var(--t-sm); color: var(--tx-3); }
.avatar { width: 2.4rem; height: 2.4rem; border-radius: 50%; flex: none; display: grid; place-items: center; background: linear-gradient(140deg, #4b2fb5, #123642); color: #ede9ff; font-weight: 660; font-size: .88rem; letter-spacing: -0.02em; }
.invert .avatar { color: #ece3d2; }

/* ------------------------------------------------------------ breadcrumb */
.crumb { font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); padding-top: var(--s-8); letter-spacing: .07em; }
html[lang="ar"] .crumb { font-family: var(--text); letter-spacing: 0; font-size: var(--t-sm); }
.crumb a { text-decoration: none; }
.crumb a:hover { color: var(--brand); }
.crumb .s { padding-inline: .5rem; opacity: .45; }
.invert .crumb { color: #62808f; }

/* ------------------------------------------------------------------ todo */
.todo { border: 1px dashed var(--rose); background: rgba(255,157,141,.07); color: var(--rose); padding: .95rem 1.1rem; border-radius: var(--r-sm); font-size: var(--t-sm); margin: var(--s-5) 0; }
.invert .todo { color: var(--rose-deep); background: #fdf0ec; border-color: #d99b88; }
mark.todo-i { background: rgba(255,157,141,.16); color: var(--rose); padding: .06em .42em; border-radius: 3px; font-size: .94em; font-weight: 620; }
.invert mark.todo-i { background: #fbe4de; color: var(--rose-deep); }

/* ---------------------------------------------------------------- footer */
.ftr { border-top: 1px solid var(--line); padding-block: var(--s-16) var(--s-8); background: var(--bg-1); position: relative; }
/* Five tracks, declared explicitly.
   The previous rule was minmax(0,1.7fr) repeat(auto-fit, minmax(9rem,1fr)),
   which is a trap: auto-fit sizes its own tracks first and hands the leftover
   to the 1.7fr, so on a 430px phone the brand column computed to TWENTY-TWO
   PIXELS and the blurb rendered one word per line, overlapping the column
   beside it. Never pair a flexible track with auto-fit. Every track below is
   minmax(0,...) so long link labels cannot blow the grid out either. */
.ftr-grid { display: grid; gap: var(--s-10) var(--s-8); grid-template-columns: minmax(0,1.6fr) repeat(4, minmax(0,1fr)); }
@media (max-width: 68rem) { .ftr-grid { grid-template-columns: repeat(4, minmax(0,1fr)); } .ftr-brand { grid-column: 1 / -1; } }
@media (max-width: 46rem) { .ftr-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--s-8) var(--s-6); } }
@media (max-width: 22rem) { .ftr-grid { grid-template-columns: minmax(0,1fr); } }
.ftr h3 { font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .14em; text-transform: uppercase; color: var(--tx-3); font-weight: 500; margin-bottom: var(--s-4); }
html[lang="ar"] .ftr h3 { font-family: var(--text); letter-spacing: 0; text-transform: none; }
.ftr ul { list-style: none; padding: 0; margin: 0; }
.ftr li { margin-bottom: .25rem; }
/* inline-block + padding gives every footer link a hit area of at least 24px (WCAG 2.2 SC 2.5.8) */
.ftr a { display: inline-block; padding-block: .3rem; text-decoration: none; font-size: var(--t-sm); color: var(--tx-2); }
.ftr a:hover { color: var(--tx); }
/* plain text lines (the country) match the link lines beside them */
.ftr li > .muted { font-size: var(--t-sm); color: var(--tx-2); }
.ftr-brand p { color: var(--tx-2); font-size: var(--t-sm); max-width: 34ch; }
.ftr-btm { margin-top: var(--s-12); padding-top: var(--s-6); border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: .8rem 1.6rem; justify-content: space-between; font-size: var(--t-sm); color: var(--tx-3); }
.ftr-btm nav { display: flex; gap: 1.35rem; }
.ftr-legal { font-size: var(--t-xs); color: var(--tx-3); margin-top: .85rem; }

/* ---------------------------------------------------------- social links */
/* THE KUFIC BRICK RAIL.
   Not a copy of the pill rails everyone ships. The logo is four rectangles on
   a 12-unit square-Kufic grid — an architectural script, laid in brick rather
   than written with a pen — so the social rail is built the same way: square
   modules, stacked flush with a 3px mortar joint, bracketed top and bottom by
   a brand-green bar in the proportion of the mark's own crossbar.

   Three decisions, and the reasoning:

   1. PLATFORM COLOUR AT REST. The first version kept the tiles neutral until
      hovered, which is more tasteful and completely useless — nobody hovers
      something they have not noticed. Colour at rest is the entire job.
   2. NO RADIUS. Square Kufic is orthogonal; a pill would fight it. This is
      also why the rail does not undo the formality pass the way borrowed
      platform chrome would.
   3. THE COLUMN REACTS AS ONE OBJECT. Hover a module and its siblings
      desaturate and fade while the brackets extend — so the rail behaves like
      a piece of architecture rather than three unrelated buttons. That is the
      part that is ours.

   The colour comes from --soc, set inline per link by the build: adding a
   fourth platform is one line in src/site.mjs and no CSS at all. */
/* AT REST THE BRICKS ARE THE SITE'S OWN INK, NOT THE PLATFORMS'.
   They used to sit permanently in LinkedIn blue, WhatsApp green and YouTube
   red — three foreign palettes dragged into a two-hue system, floating over
   every page. A review put it plainly: a fixed, full-colour WhatsApp chip is
   a small-retail signal, and the buyer here is a bank CTO. The platform
   colour is still the right cue for WHICH link this is, so it arrives on
   hover and focus, where it reads as a response rather than as noise. */
.soc a { display: flex; align-items: center; text-decoration: none; background: var(--alt); color: var(--on-alt); border: 0; transition: background var(--dur) var(--ease); }
.soc a:hover, .soc a:focus-visible { background: var(--soc); color: #fff; }
.soc svg { inline-size: 1.3rem; block-size: 1.3rem; flex: none; fill: currentColor; }
.soc-t { font-size: var(--t-sm); font-weight: 650; white-space: nowrap; letter-spacing: -.005em; }

/* inset-inline-end pins the column to the trailing edge — right in English,
   left in Arabic, from this one declaration. Because that edge is pinned,
   a module growing its inline-size expands INTO the page in both directions,
   so the mirroring needs no second rule either. */
.soc-rail {
  position: fixed; inset-inline-end: 0; top: 50%; transform: translateY(-50%);
  z-index: 70; display: flex; flex-direction: column; gap: 3px;
  /* align-items: flex-end and NO Arabic override. In a column flex container
     the cross axis IS the inline axis, so flex-end already means "the
     inline-end side" — right in English, left in Arabic — which is the same
     edge inset-inline-end pins the rail to. An html[lang=ar] override was
     added here to "mirror" it and did the opposite: on hover the container
     grew to the width of the expanded brick and pushed every other brick
     inward, so the column detached from the edge and floated in the page.
     Logical properties mirror themselves; overriding them un-mirrors them. */
  align-items: flex-end;
}

/* The bracket bars. Same width as a module, the height of the mark's dots. */
.soc-rail::before, .soc-rail::after {
  content: ""; inline-size: 2.75rem; block-size: .5rem;
  /* --brand-hi, not --brand-solid: the rail is fixed, so it scrolls over the
     dark .invert sections too, and the darker green all but vanishes there.
     This value reads on both (3.3:1 on white, 5.3:1 on the dark register). */
  background: var(--brand-hi); box-shadow: 0 2px 10px -3px rgba(5,32,25,.3);
  transition: inline-size var(--dur) var(--ease-out), background var(--dur);
}
.soc-rail:has(a:hover)::before, .soc-rail:has(a:focus-visible)::before,
.soc-rail:has(a:hover)::after,  .soc-rail:has(a:focus-visible)::after { inline-size: 4.1rem; }

.soc-rail a {
  inline-size: 2.75rem; block-size: 2.75rem; gap: .55rem; overflow: hidden;
  padding-inline-start: .74rem;
  box-shadow: 0 2px 10px -3px rgba(5,32,25,.3);
  transition: inline-size var(--dur) var(--ease-out), filter var(--dur), box-shadow var(--dur);
}
.soc-rail .soc-t { opacity: 0; transition: opacity .16s var(--ease-out); }
.soc-rail a:hover, .soc-rail a:focus-visible {
  inline-size: 9.6rem; box-shadow: 0 8px 24px -6px rgba(5,32,25,.5);
}
.soc-rail a:hover .soc-t, .soc-rail a:focus-visible .soc-t { opacity: 1; transition-delay: .05s; }
/* The siblings step back so the colour reads as focus rather than as noise. */
.soc-rail:has(a:hover) a:not(:hover),
.soc-rail:has(a:focus-visible) a:not(:focus-visible) { opacity: .62; }

/* The column lays itself in once, on load — brick by brick, bracket last.
   Animating inline-size from 0 means it slides out of the viewport edge in
   whichever direction that edge is on, so RTL needs no separate keyframes.
   fill-mode is 'backwards', NOT 'both': with 'both' the animation keeps hold
   of inline-size after it finishes and silently swallows the hover expansion.
   That exact bug cost an afternoon on the logo animation; see the README. */
/* padding-inline goes to 0 too: with border-box the used inline-size cannot
   fall below the padding, so 'inline-size: 0' alone left a 12px sliver of
   colour on screen at frame 0 instead of nothing. */
@keyframes soc-lay { from { inline-size: 0; padding-inline: 0; opacity: 0; } }
@keyframes soc-bracket { from { inline-size: 0; opacity: 0; } }
.soc-rail::before { animation: soc-bracket .46s var(--ease-out) backwards; }
.soc-rail::after  { animation: soc-bracket .46s var(--ease-out) .28s backwards; }
.soc-rail a { animation: soc-lay .46s var(--ease-out) backwards; }
.soc-rail a:nth-of-type(1) { animation-delay: .07s; }
.soc-rail a:nth-of-type(2) { animation-delay: .14s; }
.soc-rail a:nth-of-type(3) { animation-delay: .21s; }
.soc-rail a:nth-of-type(4) { animation-delay: .28s; }
/* Hidden while the footer (which has the same links) is on screen. */
.soc-rail { transition: opacity var(--dur) var(--ease-out), visibility var(--dur); }
.soc-rail.is-away { opacity: 0; visibility: hidden; pointer-events: none; }

/* A fixed rail eats about 15% of a 390px viewport and sits on top of the
   content the whole time it is there. Below 48rem it goes away and the footer
   row carries the links instead — same markup, no JavaScript involved. */
@media (max-width: 48rem) { .soc-rail { display: none; } }

/* The footer row: the same brickwork laid horizontally, bracketed at the
   leading end so it is recognisably the same object as the rail. */
.soc-row { display: flex; align-items: center; gap: 3px; margin-top: var(--s-6); }
.soc-row::before {
  content: ""; inline-size: .5rem; block-size: 2.6rem; background: var(--brand-hi);
}
.soc-row a { inline-size: 2.6rem; block-size: 2.6rem; justify-content: center; transition: filter var(--dur), transform var(--dur) var(--ease-out); }
.soc-row a:hover, .soc-row a:focus-visible { transform: translateY(-3px); }
.soc-row:has(a:hover) a:not(:hover),
.soc-row:has(a:focus-visible) a:not(:focus-visible) { filter: saturate(.45) opacity(.62); }
.soc-row .soc-t { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

@media (prefers-reduced-motion: reduce) {
  .soc-rail a, .soc-rail .soc-t, .soc-rail::before, .soc-rail::after, .soc-row a { transition: none; }
  .soc-rail a, .soc-rail::before, .soc-rail::after { animation: none; }
  .soc-row a:hover, .soc-row a:focus-visible { transform: none; }
}

/* ----------------------------------------------------------------- video */
/* Poster-first and click-to-play. preload="none" on the element means no
   media bytes are fetched until someone asks, so the poster is the LCP
   candidate and the video costs nothing on load. Nothing autoplays, so WCAG
   2.2.2 never engages — and the controls attribute supplies a real pause. */
/* Watermark: faint Kufic squares, fixed behind light sections and faded out
   behind the content column (the clear area follows the text side in RTL). */
body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='560'%3E%3Crect x='6' y='246' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='7' y='447' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='46' y='86' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='47' y='287' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='47' y='367' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='47' y='447' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='46' y='486' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='86' y='6' width='28' height='28' fill='%23dda23f' fill-opacity='.12'/%3E%3Crect x='86' y='326' width='28' height='28' fill='%23dda23f' fill-opacity='.12'/%3E%3Crect x='127' y='47' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='127' y='87' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='126' y='126' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='126' y='246' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='166' y='46' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='166' y='126' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='166' y='246' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='166' y='406' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='166' y='446' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='167' y='487' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='207' y='7' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='207' y='327' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='206' y='486' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='247' y='7' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='247' y='367' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='246' y='406' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='247' y='447' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='287' y='7' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='286' y='46' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='286' y='406' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='286' y='486' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='327' y='47' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='327' y='247' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='327' y='327' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='367' y='287' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='406' y='6' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='446' y='246' width='28' height='28' fill='%230d5b5f' fill-opacity='.07'/%3E%3Crect x='487' y='487' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3Crect x='527' y='207' width='26' height='26' fill='none' stroke='%230d5b5f' stroke-opacity='.12' stroke-width='1.5'/%3E%3C/svg%3E"); background-size: 560px 560px;
  -webkit-mask-image: radial-gradient(ellipse 70% 75% at 42% 50%, transparent 30%, #000 90%);
          mask-image: radial-gradient(ellipse 70% 75% at 42% 50%, transparent 30%, #000 90%); }
[dir="rtl"] body::before { -webkit-mask-image: radial-gradient(ellipse 70% 75% at 58% 50%, transparent 30%, #000 90%); mask-image: radial-gradient(ellipse 70% 75% at 58% 50%, transparent 30%, #000 90%); }
@media print { body::before { display: none; } }
.hero-video { position: relative; isolation: isolate; overflow: hidden; background: #041a1e; }
.hero-video .hero-grid { grid-template-columns: minmax(0, 1fr); max-inline-size: 46rem; }
.hero-card { display: none; }
@media (min-width: 62.01rem) {
  .hero-video .hero-grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); max-inline-size: none; }
  .hero-card { display: block; margin-inline-end: 2.5rem; background: rgba(6,38,43,.88); border: 1px solid #33717a; border-radius: 1rem; padding: 1.4rem; box-shadow: 0 30px 60px rgba(0,0,0,.45); color: #f0f5f4; }
  .hero-card-bar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; font-size: .8rem; color: #96aeae; margin-bottom: 1rem; }
  .hero-card-sector { font-size: .78rem; color: #96aeae; }
  .hero-card-title { font-size: 1.15rem; font-weight: 700; line-height: 1.4; margin: .4rem 0 1rem; }
  .hero-card-meta > div { display: flex; justify-content: space-between; gap: 1rem; padding: .5rem 0; border-top: 1px solid #1a474e; font-size: .88rem; }
  .hero-card-meta dt { color: #96aeae; }
  .hero-card-meta dd { margin: 0; text-align: end; }
  .hero-card-rows { list-style: none; margin: .8rem 0 0; padding: 0; }
  .hero-card-rows li { display: flex; justify-content: space-between; gap: 1rem; padding: .45rem 0; border-top: 1px solid #1a474e; font-size: .84rem; }
  .hero-card-rows b { font-weight: 600; white-space: nowrap; }
  .hero-card-rows .tone-ok b { color: #3f9f96; } .hero-card-rows .tone-warn b { color: #dda23f; } .hero-card-rows .tone-urgent b { color: #ff9d8d; }
  .hero-card-note { margin-top: .8rem; font-size: .72rem; color: #96aeae; }
}
.hero-video { min-block-size: min(88vh, 52rem); display: flex; flex-direction: column; justify-content: center; }
.hero-bg { position: absolute; inset: 0; z-index: -1; background: #041a1e url(/hero-bg.jpg) center / cover no-repeat; }
.hero-bg video { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }
.hero-bg::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(4,26,30,.92) 0%, rgba(4,26,30,.72) 45%, rgba(4,26,30,.35) 100%); }
[dir="rtl"] .hero-bg::after { background: linear-gradient(270deg, rgba(4,26,30,.92) 0%, rgba(4,26,30,.72) 45%, rgba(4,26,30,.35) 100%); }
.hero-video h1, .hero-video h1 .grad { color: #f0f5f4; }
.hero-video h1 .grad { color: #3f9f96; }
.hero-pause { position: absolute; inset-block-end: var(--s-4); inset-inline-end: var(--s-4); z-index: 2; inline-size: 2.75rem; block-size: 2.75rem; border-radius: 50%; border: 1px solid rgba(240,245,244,.45); background: rgba(4,26,30,.55); color: #f0f5f4; cursor: pointer; display: grid; place-items: center; padding: 0; }
.hero-pause:hover { background: rgba(4,26,30,.8); }
.hero-pause-ico { inline-size: .8rem; block-size: .8rem; border-inline: .26rem solid currentColor; box-sizing: border-box; }
.hero-pause[aria-pressed="true"] .hero-pause-ico { border: 0; border-inline-start: .8rem solid currentColor; border-block: .45rem solid transparent; inline-size: 0; block-size: 0; }
[dir="rtl"] .hero-pause[aria-pressed="true"] .hero-pause-ico { transform: scaleX(-1); }
@media (prefers-reduced-motion: reduce) { .hero-bg video { display: none; } .hero-pause { display: none; } }
/* Page photo: its own band between the hero and the first content section.
   It spans the same column as everything else on the page (the hero text, the
   stats strip, the sections below) so its edges line up with theirs, sits in
   clear space instead of butting against the hero's bottom edge, and is cropped
   wide so it reads as a pause rather than a second hero. */
.photo-sec { padding-block: clamp(2.5rem, 4.5vw, 4rem) 0; }
/* When a dark section follows, the photo would sit flush on its top edge.
   Open the same gap below the photo as above it. (A light section brings its
   own top padding, so it needs nothing extra.) */
.photo-sec + .invert { margin-top: clamp(2.5rem, 4.5vw, 4rem); }
/* Sections that sit straight under a page head drop their top padding inline;
   under a photo they need it back. A photo that ends the page needs room
   before the footer. */
.photo-sec + section[style*="padding-top:0"] { padding-top: clamp(2.5rem, 4.5vw, 4rem) !important; }
.photo-sec:last-child { padding-bottom: clamp(2.5rem, 4.5vw, 4rem); }
.photo { margin: 0; }
.photo img { display: block; inline-size: 100%; block-size: auto; aspect-ratio: 21 / 9; object-fit: cover; border-radius: var(--r); background: #06262b; box-shadow: 0 18px 40px -24px rgba(5,32,25,.45); }
@media (max-width: 40rem) { .photo img { aspect-ratio: 16 / 10; } }
.vid { margin: var(--s-8) 0 0; max-inline-size: 34rem; }
.vid-wide { margin: 0 auto; max-inline-size: 56rem; }
.vid-wide .vid-cap { max-inline-size: none; }
.vid-el {
  inline-size: 100%; block-size: auto; display: block; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--bg-2); box-shadow: var(--sh-2);
}
.vid-cap { font-size: var(--t-sm); color: var(--tx-3); margin-block-start: .9rem; max-inline-size: 46ch; }
.invert .vid-el { border-color: var(--alt-line); }
.invert .vid-cap { color: var(--on-alt-2); }
@media print { .vid { display: none; } }

/* ---------------------------------------------------------------- reveal */
/* Reveal is applied only when JS is running (html.js is set by an inline
   script in <head>). Without it every .rv element would stay at opacity 0 and
   the site would render blank if the script failed to execute. */
html.js .rv { opacity: 0; transform: translateY(20px); transition: opacity var(--dur-l) var(--ease-out), transform var(--dur-l) var(--ease-out); }
html.js .rv.in { opacity: 1; transform: none; }
[data-stagger] > * { transition-delay: calc(var(--i, 0) * 70ms); }
@media (prefers-reduced-motion: reduce) { .rv { opacity: 1; transform: none; } }

/* -------------------------------------------------------------- diagrams */
.diagram { width: 100%; height: auto; display: block; overflow: visible; }

/* ---- the two forms of a mechanism ------------------------------------
   The SVG is width:100% over a fixed viewBox, so its type scales with the
   box. At 390px that put titles at 8.8px and chips at 6.4px — the site's
   signature visual, unreadable on the device most visitors arrive on.
   Counter-scaling the type does not work (the correction is quadratic in
   viewport width and overshoots on tablets), so below 46rem the drawing is
   replaced by the same steps as real HTML, at real sizes.

   The list is ALSO the accessible copy at every width. The SVG is
   aria-hidden and the list is only ever visually hidden, so assistive
   technology always gets structured text instead of one <desc> paragraph. */
.dg { margin: 0; }
.dg-rows {
  position: absolute; inline-size: 1px; block-size: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.dg figcaption { margin-top: var(--s-5); font-size: var(--t-sm); color: var(--tx-3); }

@media (max-width: 46rem) {
  .dg-svg { display: none; }
  .dg-rows {
    position: static; inline-size: auto; block-size: auto;
    overflow: visible; clip-path: none; white-space: normal;
    list-style: none; margin: 0; padding: 0; display: grid; gap: 1px;
    border: 1px solid var(--card-ln); border-radius: var(--r-lg);
    background: var(--card-ln); overflow: hidden;
  }
  .dg-rows li {
    display: grid; grid-template-columns: auto 1fr; gap: .95rem;
    align-items: start; padding: 1rem 1.15rem; margin: 0;
    background: var(--card);
  }
  .dg-rows li.on { background: var(--bg); }
  .dg-rows li.on > div > b { color: var(--brand); }
  .dg-n {
    font-family: var(--mono); font-size: var(--t-xs); letter-spacing: .12em;
    color: var(--sys-deep); padding-top: .2rem; min-inline-size: 1.6rem;
  }
  /* A chart's leading cell is a VALUE, not an ordinal, so it needs room and
     reads as data rather than as a step number. */
  .dg-rows li[data-kind="chart"] .dg-n { min-inline-size: 4.2rem; font-size: var(--t-sm); font-weight: 600; color: var(--brand); letter-spacing: 0; }
  .dg-rows li[data-kind="fail"] .dg-n { color: var(--rose-deep); }
  .dg-rows li[data-kind="pass"] .dg-n { color: var(--brand); }
  .dg-rows li > div > b { display: block; font-size: 1rem; font-weight: 640; color: var(--tx); }
  .dg-rows li > div > span { display: block; font-size: var(--t-sm); color: var(--tx-2); margin-top: .2rem; }
  .dg-tags { display: flex !important; flex-wrap: wrap; gap: .35rem; margin-top: .55rem !important; }
  .dg-tags i {
    font-style: normal; font-family: var(--mono); font-size: var(--t-xs);
    letter-spacing: .08em; color: var(--tx-2);
    border: 1px solid var(--card-ln); border-radius: 3px; padding: .16rem .45rem;
  }
  html[lang="ar"] .dg-n, html[lang="ar"] .dg-tags i { font-family: var(--text); letter-spacing: 0; }
  .invert .dg-rows { border-color: #33717a; background: #33717a; }
  .invert .dg-rows li { background: #124850; }
  .invert .dg-rows li.on { background: var(--alt); }
  .invert .dg-rows li > div > b { color: var(--on-alt); }
  .invert .dg-rows li > div > span { color: var(--on-alt-2); }
  .invert .dg-tags i { color: var(--on-alt-2); border-color: #33717a; }
  .invert .dg-rows li.on > div > b { color: var(--brand-deep); }
}

/* ---- the market band -------------------------------------------------- */
.stat-lead { margin-block: var(--s-6) var(--s-7); }
.stat-lead b {
  display: block; font-family: var(--sans); font-size: clamp(2.6rem, 6vw, 4rem);
  font-weight: 700; letter-spacing: -.03em; line-height: 1; color: var(--tx);
}
html[lang="ar"] .stat-lead b { letter-spacing: 0; line-height: 1.25; }
.stat-lead span { display: block; margin-top: .7rem; font-size: var(--t-lede); color: var(--tx-2); max-inline-size: 34ch; }
.stat-lead i { display: block; margin-top: .35rem; font-style: normal; font-size: var(--t-sm); color: var(--tx-3); }

.chart-grid { display: grid; gap: var(--s-4); grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); }
.chart-card {
  border: 1px solid var(--card-ln); border-radius: var(--r-lg);
  background: linear-gradient(180deg, var(--card), var(--card-2));
  padding: clamp(1.4rem, 2.2vw, 1.85rem); display: flex; flex-direction: column;
}
.chart-card h3 { margin: 0 0 1.15rem; font-size: var(--t-h4); }
.chart-note { margin: 1.15rem 0 0; font-size: var(--t-sm); color: var(--tx-2); }
.chart-src { margin-top: var(--s-6); font-size: var(--t-sm); color: var(--tx-3); }
.chart-src a { color: var(--brand); font-weight: 560; white-space: nowrap; }
.invert .chart-card { background: #124850; border-color: #33717a; }
.invert .chart-note { color: var(--on-alt-2); }

/* ---------------------------------------------------------------- charts
   HTML, not SVG. The market figures are a label, a number and a proportion —
   there is no geometry here that needs a drawing surface, and SVG text does
   not wrap, so every Arabic label was running past its box or under its own
   value. In HTML the label wraps, the value is a flex item that cannot be
   encroached on, and RTL is the browser's job. See src/charts.mjs.

   One data hue and a recessive track. Nothing here is about telling series
   apart, so nothing here earns a categorical palette — the fill clears 3:1
   against every surface it sits on and the track sits at ~1.4:1 because it is
   the container, not a second series.

   Text wears text tokens, never the series colour. Grid is a solid hairline,
   never dashed: dashing reads as "projection" when it is just a grid. */
.ck { margin: 0; }
.ck-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }

/* The collision fix, in one line: the label takes what is left and wraps
   inside it, the value reserves its own width and is never drawn over. */
.ck-head { display: flex; align-items: baseline; gap: .9rem; margin-block-end: .55rem; }
.ck-l {
  flex: 1 1 auto; min-inline-size: 0; overflow-wrap: break-word;
  font-family: var(--sans); font-size: var(--t-sm); font-weight: 620;
  letter-spacing: -.01em; line-height: 1.45; color: var(--tx);
}
/* Isolated and forced LTR: "+42%" in an Arabic paragraph is a neutral sign
   next to a number, and bidi resolution moved the plus to the far end, so the
   Arabic homepage read "42%+". The value is a number, and numbers with signs
   run left to right in every language on this site. */
.ck-v {
  flex: 0 0 auto; white-space: nowrap; direction: ltr; unicode-bidi: isolate;
  font-family: var(--mono); font-size: .95rem; font-weight: 560;
  font-variant-numeric: tabular-nums; color: var(--tx);
}
.ck-track { position: relative; block-size: .9rem; border-radius: 4px; background: var(--bg-3); overflow: hidden; }
.ck-bar { display: block; block-size: 100%; inline-size: var(--w, 0%); background: var(--brand); }
.ck-n { margin: .55rem 0 0; font-size: var(--t-xs); line-height: 1.6; color: var(--tx-3); }
.ck figcaption { margin-block-start: var(--s-5); font-size: var(--t-sm); color: var(--tx-3); }

/* The meter's headline wraps and scales instead of clipping: the Arabic for
   "SAR 9.23bn" is four times as wide, and at a fixed 30px it lost its first
   half to the edge of the card. */
.ck-meter .ck-hero {
  display: block; font-family: var(--sans); font-size: clamp(1.65rem, 4vw, 2.15rem);
  font-weight: 700; letter-spacing: -.025em; line-height: 1.12; color: var(--tx);
  overflow-wrap: break-word; text-wrap: balance;
}
html[lang="ar"] .ck-meter .ck-hero { letter-spacing: 0; line-height: 1.4; }
.ck-meter .ck-l { display: block; margin-block: .6rem .8rem; }
.ck-meter .ck-track { block-size: 1.05rem; }

/* Scored axes. Gridlines and the gate are placed with inset-inline-start, so
   they mirror themselves on the Arabic pages with no second code path. */
.ck-plot { position: relative; }
.ck-axes .ck-rows { position: relative; z-index: 1; }
.ck-grid, .ck-ref { position: absolute; inset-block: 0; inset-inline-start: var(--p); inline-size: 1px; }
.ck-grid { background: var(--line); }
/* The gate crosses the bars; the grid sits behind them. A threshold you
   cannot see against the thing it is a threshold for is decoration. */
.ck-ref { z-index: 2; }
/* The threshold reference. Solid, one step stronger than the grid — a dashed
   rule reads as "projection" and this is a real line in the scoring model. */
.ck-ref { background: var(--brand); opacity: .5; inline-size: 1.5px; }
.ck-scale { position: relative; block-size: 1.2rem; margin-block-start: .6rem; }
/* Zero-width centring boxes: a translateX(-50%) would need an RTL override to
   stay centred, and RTL overrides are how RTL breaks. */
.ck-scale > span { position: absolute; inset-block-start: 0; inset-inline-start: var(--p); inline-size: 0; display: flex; justify-content: center; }
.ck-scale i { font-style: normal; white-space: nowrap; font-family: var(--mono); font-size: var(--t-xs); color: var(--tx-3); }
.ck-ref-t i { color: var(--brand); font-weight: 560; }

html[lang="ar"] :is(.ck-v, .ck-scale i) { font-family: var(--text); letter-spacing: 0; }

.invert :is(.ck-l, .ck-v, .ck-hero) { color: var(--on-alt); }
.invert .ck-n { color: var(--on-alt-2); }
.invert .ck-scale i { color: var(--on-alt-2); }
.invert .ck-track { background: #33717a; }
.invert .ck-grid { background: var(--alt-ln); }
.invert .ck-ref { background: var(--brand-deep); opacity: .7; }
.invert .ck-ref-t i { color: var(--brand-deep); }
/* A brighter step for the dark register, chosen against that surface rather
   than flipped from the light one. 7.17:1 on the deep green. */
.invert .ck-bar { background: #3fbb9c; }

/* Bars grow once, on reveal, staggered down the chart — the motion says "read
   these in order" and then stops. Under prefers-reduced-motion they are just
   there. The axes chart is filled in by script after the reveal has run, so
   it transitions its width instead of replaying the entrance. */
@media (prefers-reduced-motion: no-preference) {
  .rv .ck-bar { transform: scaleX(0); transform-origin: left center; }
  html[dir="rtl"] .rv .ck-bar { transform-origin: right center; }
  .rv.in .ck-bar { animation: ck-grow .7s var(--ease-out) calc(var(--i, 0) * .09s + .12s) forwards; }
  @keyframes ck-grow { to { transform: scaleX(1); } }
  .ck-axes .ck-bar { transition: inline-size .65s var(--ease-out) calc(var(--i, 0) * .08s); }
}

/* The stop branch: a mechanism that can only succeed is a sales diagram. */
.dg-box-stop { fill: none; stroke: var(--rose-deep); stroke-width: 1; opacity: .55; }
.dg-stop { fill: var(--rose-deep); }
.invert .dg-box-stop { stroke: var(--rose); }
.invert .dg-stop { fill: var(--rose); }
.dg-box { fill: var(--bg-2); stroke: var(--line-2); }
.dg-line { stroke: var(--line-2); fill: none; }
.dg-t { fill: var(--tx); font-family: var(--sans); font-size: 13px; font-weight: 640; letter-spacing: -0.01em; }
.dg-s { fill: var(--tx-2); font-family: var(--text); font-size: 11px; }
.dg-m { fill: var(--sys); font-family: var(--mono); font-size: 9.5px; letter-spacing: .1em; }
.dg-accent { fill: var(--sys); }
html[lang="ar"] .dg-t, html[lang="ar"] .dg-s, html[lang="ar"] .dg-m { font-family: var(--text); letter-spacing: 0; }
html[lang="ar"] .dg-m { font-size: 10.5px; }
/* No .invert overrides for the diagram: the re-pointed tokens above make the
   base .dg-* rules resolve correctly in both polarities. */

/* ---- the pipeline traces itself, once ----------------------------------
   What was here before: the connectors dashed forever on a 2.4s loop and the
   accuracy-gate dot pulsed forever on a 3.2s loop. Perpetual motion with no
   way to stop it, carrying no information — the same thing worth objecting to
   in a hero video, at a smaller scale. WCAG 2.2.2 (Pause, Stop, Hide) covers
   anything that starts on its own and runs past five seconds.

   What happens now: when the diagram is revealed, one pulse travels down each
   connector in turn and each stage brightens as the pulse reaches it. Sources
   to foundation to retrieval to the gate to production, in that order, in
   about 2.2 seconds. Then the diagram is completely still. The motion states
   the sequence — which IS the argument the diagram is making — and stops.

   It hangs off .rv.in, the class the reveal observer already sets, so it fires
   when the diagram actually comes into view rather than on page load. With
   JavaScript off, .rv never becomes .in, no pulse runs, and the diagram is
   fully readable — which is the whole point of the progressive-enhancement
   rule further up this file. */
.flow { stroke: var(--sys); stroke-width: 1.5; fill: none; stroke-dasharray: 5 9; opacity: .85; }

/* One cadence value drives the whole sequence. The connectors between stages
   are only 26-32 units long on a 520-unit canvas, so the travelling dash has
   to be a large FRACTION of the path (40 of 100) rather than a few absolute
   units, or it is a speck nobody sees. pathLength normalises that. */
.diagram { --step: .38s; }
.dg-pulse {
  stroke: var(--brand-hi); stroke-width: 3; fill: none; stroke-linecap: round;
  stroke-dasharray: 40 100; stroke-dashoffset: 40; opacity: 0;
}
@keyframes dg-run {
  0%   { stroke-dashoffset: 40; opacity: 0; }
  18%  { opacity: 1; }
  80%  { opacity: 1; }
  100% { stroke-dashoffset: -100; opacity: 0; }
}
/* The pulse crosses the connector; the stage it lands on then washes through.
   Border alone was too quiet — 1px of colour change on a 464-unit box reads as
   nothing — so the fill carries it, at a strength that is noticeable without
   becoming a flash. color-mix keeps it correct in both page polarities. */
@keyframes dg-arrive {
  0%   { fill: var(--bg-2); stroke: var(--line-2); }
  35%  { fill: color-mix(in srgb, var(--brand-hi) 16%, var(--bg-2)); stroke: var(--brand-hi); }
  100% { fill: var(--bg-2); stroke: var(--line-2); }
}
@keyframes dg-wake { from { opacity: .25; } }

/* backwards, not both: 'both' would keep hold of fill/stroke after the run and
   override the base rules. The README records what that cost last time. */
.rv.in .dg-pulse { animation: dg-run .46s linear calc(var(--seg) * var(--step)) backwards; }
.rv.in .dg-stage { animation: dg-arrive .66s var(--ease-out) calc(var(--seg) * var(--step) + .3s) backwards; }
.rv.in .node-pulse { animation: dg-wake .55s var(--ease-out) calc(3 * var(--step) + .3s) backwards; }



@media print {
  .hdr, .ftr, .hero-cta, .amb, .grid-bg, .progress, .soc-rail, body::after { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .case { border-color: #ccc; background: #fff; }
}
.tag-feat { background: transparent; color: var(--brand); border: 1px solid var(--brand); }
.platform-card.is-featured { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand); }
.fset { border: 0; padding: 0; margin: 0; min-inline-size: 0; }
.fset legend { padding: 0; margin-bottom: .45rem; font-size: var(--t-sm); font-weight: 600; color: var(--tx); }
.fset .opts { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .3rem .6rem; }
.fset .opt input { inline-size: 1.05rem; width: 1.05rem; block-size: 1.05rem; height: 1.05rem; padding: 0; flex: none; margin: .2rem 0 0; accent-color: var(--brand); box-shadow: none; }
.fset .opt { font-weight: 500; }
.fset .opts { min-inline-size: 0; }
