/* ============================================================================
   PUBLIC SITE — layout and components. RTL-first, Arabic-first.

   BRAND-NEUTRAL BY CONTRACT. This file contains no hex literal, no rgb()/rgba(),
   no font-family name, no shadow value and no radius value. Every one of those is
   a custom property declared in wwwroot/brands/<id>/theme.css, which the layout
   links ahead of this file. A second association rebrands by replacing that one
   stylesheet — never by editing this one.
     · If a component needs a value that is not a token yet, ADD THE TOKEN.
     · `grep -nE '#[0-9a-fA-F]{3,8}\b|rgba?\(' site.css` must stay empty.

   Design rules this file enforces rather than merely follows (CLAUDE.md D11):
     · Flat. Depth is flat colour blocks + hairline borders + whitespace. The single
       shadow token is spent only on the drawer, the dropdown, the lightbox and the
       scrolled header.
     · --brand-decor and --accent are fills and rules. They NEVER carry text; their
       values in this brand are 3.0:1 and 2.5:1 (see theme.css).
     · Arabic body copy runs at --lh-body (1.9) and is never centred past a heading.
     · color-scheme + explicit control colours (D12), so Android force-dark cannot
       repaint an input into invisibility.
   ============================================================================ */

/* ---------------------------------------------------------------- reset ---- */

*, *::before, *::after { box-sizing: border-box; }

/* TRAP 7 — load-bearing. Do not "tidy" the !important away.
   The cascade sorts by ORIGIN before specificity, so the UA sheet's
   [hidden]{display:none} loses to any author rule that gives the element a display
   (.card{display:flex}). Without this line every `el.hidden = true` in an island is
   a silent no-op, and the drawer, the lightbox and the documents filter all ship
   visible. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--s9);   /* anchored headings clear the sticky header */
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;              /* belt and braces for RTL off-canvas — trap 12 */
}

img, svg, video { max-width: 100%; height: auto; display: block; }
svg { flex: none; }

h1, h2, h3, h4, h5 {
  margin: 0 0 var(--s3);
  font-family: var(--font-display);
  line-height: var(--lh-tight);
  font-weight: var(--h-weight);
  letter-spacing: var(--h-tracking);
  color: var(--h-color);
}
h1 { font-size: var(--fs-2xl); line-height: var(--lh-display); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
h4 { font-size: var(--fs-md); }

p { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }

a { color: var(--brand); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--brand-hover); }

ul, ol { margin: 0 0 var(--s4); padding-inline-start: 1.4em; }
li { margin-bottom: var(--s2); }

hr { border: 0; border-top: var(--bd) solid var(--line); margin: var(--s6) 0; }

/* Numbers stay LTR inside Arabic text — an IBAN or a phone number reverses otherwise. */
.ltr, [dir='ltr'] { direction: ltr; unicode-bidi: isolate; }
.num { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }

/* R5 — D12, and the single most expensive silent failure in this file before it was fixed.
   `:focus-visible` is Chrome 86; this project's floor is Chrome 81 (see CLAUDE.md D12). A
   selector LIST containing an unsupported selector is dropped IN ITS ENTIRETY, so writing
   `:focus, :focus-visible { … }` as one rule is worse than useless — it fails on 81 as well and
   the site has no focus ring at all. Three separate rules, in this order:
     · Chrome 81 parses rule 1, drops rules 2 and 3, and shows a ring on EVERY focus. Slightly
       noisier after a mouse click, which is strictly better than no focus indication.
     · A modern browser parses all three and ends up with the keyboard-only ring.
   Do not merge them, and do not "tidy" rule 1 away. */
:focus {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
:focus:not(:focus-visible) { outline: none; }
:focus-visible {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
.band--deep :focus,
.header :focus,
.footer :focus,
.topbar :focus { outline-color: var(--focus-ring-deep); }
.band--deep :focus-visible,
.header :focus-visible,
.footer :focus-visible,
.topbar :focus-visible { outline-color: var(--focus-ring-deep); }

::selection { background: var(--surface-strong); color: var(--brand-strong); }

/* Screen-reader-only. clip-path, NOT `left:-9999px` — TRAP 12: an absolutely
   positioned off-canvas element in an RTL document adds 10,000px of horizontal
   scroll to every page that carries one. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  /* R3 — D12. `inset-inline-start` is Chrome 87 (unlike margin/padding/border logical
     LONGHANDS, which are Chrome 69 and safe). A physical default plus a [dir='ltr'] pair is
     unambiguous in both directions; reverting to a bare `left` would reintroduce trap 12. */
  right: var(--s4);
  left: auto;
  top: -100px;
  z-index: var(--z-skip);
  background: var(--surface-deep);
  color: var(--on-deep);
  padding: var(--s3) var(--s5);
  border-radius: 0 0 var(--r-sm) var(--r-sm);
  font-weight: var(--btn-weight);
  text-decoration: none;
  transition: top var(--dur) var(--ease);
}
.skip:focus { top: 0; color: var(--on-deep); }
[dir='ltr'] .skip { right: auto; left: var(--s4); }

/* -------------------------------------------------------------- layout ---- */

/* R1 — D12. The rule to internalise for this whole file: logical LONGHANDS
   (margin-inline-start, padding-block-end, border-inline-start) are Chrome 69 and safe;
   logical SHORTHANDS (margin-inline, padding-inline, padding-block, inset, inset-inline) are
   Chrome 87 and are dropped silently on Chrome 81. `margin-inline: auto` here is what used to
   cost the container its centring AND its side padding on an old Android phone — text ran to
   the physical edge of the viewport on every page. */
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline-start: auto;
  margin-inline-end: auto;
  padding-inline-start: var(--gutter);
  padding-inline-end: var(--gutter);
}
.wrap--narrow { max-width: var(--container-narrow); }

/* `padding-block` here is what used to cost EVERY section its vertical padding on Chrome 81 —
   the page collapsed into one unbroken run of text. */
.band { padding-block-start: var(--band-y); padding-block-end: var(--band-y); }
.band--tight { padding-block-start: var(--band-y-tight); padding-block-end: var(--band-y-tight); }
.band--hero { padding-block-start: var(--band-y-hero); padding-block-end: var(--band-y-hero); }
.band--sand { background: var(--surface-soft); }
.band--sand-strong { background: var(--surface-strong); }
.band--deep { background: var(--surface-deep); color: var(--on-deep); }
.band--deep h1, .band--deep h2, .band--deep h3, .band--deep h4 { color: var(--on-deep); }
.band--deep a { color: var(--on-deep); }
.band--line { border-top: var(--bd) solid var(--line); }

.stack > * + * { margin-top: var(--s4); }
.stack-lg > * + * { margin-top: var(--s6); }

.grid { display: grid; gap: var(--grid-gap); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
/* TRAP 12: never write grid-template-columns as an INLINE style on these. An inline
   declaration cannot be overridden by a media query, and the phone layout dies. */

.split { display: grid; gap: clamp(24px, 5vw, 64px); grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1.35fr 1fr; align-items: start; }
  .split--even { grid-template-columns: 1fr 1fr; }
  .split--aside { grid-template-columns: 1fr 320px; }
  /* An aside that travels with the text instead of stranding ~800px of empty column beneath a
     signature card. 72px is the sticky header; --s5 is the breathing room under it. */
  .aside-sticky { position: sticky; top: calc(72px + var(--s5)); }
}

/* ------------------------------------------------------------- R2: gap ----- */
/* D12. `gap` in a GRID container is Chrome 66 and is safe — every `display: grid` below keeps
   its gap. `gap` in a FLEX container is Chrome 84, and 52 flex rows in this file silently lost
   their spacing on Chrome 81: buttons touched, chips ran together, the header lockup, the nav
   and the CTAs were glued edge to edge.
   No flex container in this file declares `gap` any more. It uses one of exactly two fallbacks,
   and never both, because a container that declared `gap` AND a margin fallback would double
   its spacing on a modern browser:
     · a NON-WRAPPING row  →  `> * + * { margin-inline-start: … }`  (exact, no overhang)
     · a WRAPPING row      →  a trailing margin on every child
   `margin-inline-start/-end` and `margin-block-end` are logical LONGHANDS (Chrome 69) and are
   safe. It is the SHORTHANDS that are Chrome 87. Do not "simplify" these back to `gap`, and do
   not swap them for physical `margin-left/right` — that is trap 12.

   `.cluster` is the shared utility for a wrapping row that has no component class of its own. */
.cluster { display: flex; flex-wrap: wrap; }
.cluster > * { margin-inline-end: var(--cx, var(--s3)); margin-block-end: var(--cy, var(--s2)); }

/* ---------------------------------------------------------- section head -- */

.section-head { margin-bottom: var(--s6); max-width: var(--measure-narrow); }
.section-head--center { margin-inline-start: auto; margin-inline-end: auto; text-align: center; }
.section-head--row {
  display: flex; flex-wrap: wrap;
  align-items: flex-end; justify-content: space-between;
  max-width: none;
}
.section-head--row > * { margin-inline-end: var(--s4); margin-block-end: var(--s2); }
.section-head--row > *:last-child { margin-inline-end: 0; }
.section-head p {
  color: var(--ink-muted); font-size: var(--fs-md); margin: 0;
  /* --measure-narrow (68ch) is too wide for what is always two lines. */
  max-width: var(--measure-lead);
}
.band--deep .section-head p { color: var(--on-deep-muted); }

/* The eyebrow: a short accent rule plus a small brand label. This is the site's
   signature mark and the only place --accent appears at full strength. */
.eyebrow {
  display: inline-flex; align-items: center;
  font-size: var(--fs-sm); font-weight: var(--h-weight); letter-spacing: .02em;
  color: var(--brand); margin-bottom: var(--s2);
}
.eyebrow::before {
  content: ''; flex: none;
  inline-size: var(--rule-w); block-size: var(--rule-h);
  margin-inline-end: var(--s2);
  background: var(--rule-color); border-radius: var(--r-xs);
}
.band--deep .eyebrow { color: var(--surface-strong); }
.section-head--center .eyebrow { justify-content: center; }

/* ------------------------------------------------------------ typography -- */

/* Long Arabic copy. Start-aligned, never centred, never wider than --measure. */
.prose {
  line-height: var(--lh-body);
  font-size: var(--fs-md);
  text-align: start;
  max-width: var(--measure);
}
/* ── Justified body copy (owner instruction, 2026-09-04) ────────────────────────────────────
   Every run of body text on the public site is set flush on BOTH edges, at every width.

   This is safer than it looks: `text-align: justify` never stretches a paragraph's LAST line,
   and a single-line run IS its own last line. So a one-line card note, hint or label is left
   exactly as it was — only genuinely multi-line copy changes.

   Two deliberate exclusions, both re-asserted below:
     • Headings, buttons, labels, table cells, nav and numeric values are never justified —
       stretching a two-word heading across the measure is not typography.
     • The centred surfaces stay centred. `.state p` in particular is documented above as the
       ONE centred paragraph in this system, bounded to 44ch and two lines.

   Known cost, accepted: Chrome justifies Arabic by stretching inter-word spaces ONLY — it has
   no kashida elongation, which is how Arabic is properly justified. On a narrow phone column
   that can open visible gaps between words. `text-align-last: auto` keeps each final line
   start-aligned so the last line never fans out across the measure. */
.prose p, .prose li,
.lead, .hero__lead,
.card__text, .hint, .choice__note,
.alert p, .field-note, .form-note {
  text-align: justify;
  text-align-last: auto;
}

/* Centring wins wherever it was chosen on purpose. */
.section-head--center .lead, .section-head--center .card__text,
.center .lead, .center .card__text, .center p,
.empty p, .empty .card__text,
.state p, .state .lead,
.lightbox__caption {
  text-align: center;
}
.prose > * + * { margin-top: var(--s4); }
.prose h2 { font-size: var(--fs-lg); margin-top: var(--s6); }
.prose h3 { font-size: var(--fs-md); margin-top: var(--s5); }
.prose ul, .prose ol { padding-inline-start: 1.5em; }
.prose li { line-height: var(--lh-body); }
.prose img { border-radius: var(--r-media); border: var(--bd) solid var(--line); }
.prose blockquote {
  margin: 0; padding: var(--s4) var(--s5);
  border-inline-start: var(--bd-strong) solid var(--accent);
  background: var(--surface-soft);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.prose a { color: var(--brand); }
.prose table { width: 100%; border-collapse: collapse; }
.prose td, .prose th { border: var(--bd) solid var(--line); padding: var(--s3); text-align: start; }

/* F6 — five paragraphs at one size with no entrance. The first paragraph carries the lead
   treatment through CSS rather than through markup surgery, because the body is admin-authored
   HTML and the view must not rewrite it. */
.prose--intro > p:first-child { color: var(--ink-muted); }

.lead { font-size: var(--fs-md); color: var(--ink-muted); line-height: var(--lh-body); }
.band--deep .lead { color: var(--on-deep-muted); }
.muted { color: var(--ink-muted); }
.small { font-size: var(--fs-sm); }
.tiny { font-size: var(--fs-xs); }
.center { text-align: center; }

/* -------------------------------------------------------------- buttons --- */

.btn {
  --btn-bg: var(--brand);
  --btn-fg: var(--on-deep);
  --btn-bd: var(--brand);
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--btn-min-h);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  border: var(--bd) solid var(--btn-bd);
  border-radius: var(--btn-radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-weight: var(--btn-weight);
  font-size: var(--btn-fs);
  letter-spacing: var(--btn-tracking);
  text-transform: var(--btn-transform);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
  /* Flat: no gradient, no inset highlight, no shadow. */
}
/* The icon/label gap. `> * + *` cannot be used: the label is an anonymous TEXT node, not an
   element, so the svg is the only element child and no sibling combinator ever matches. */
.btn > svg:first-child { margin-inline-end: var(--s2); }
.btn > svg:last-child:not(:first-child) { margin-inline-start: var(--s2); }
.btn:hover { --btn-bg: var(--brand-hover); --btn-bd: var(--brand-hover); color: var(--btn-fg); }
.btn:active { --btn-bg: var(--brand-strong); --btn-bd: var(--brand-strong); }
.btn[disabled], .btn[aria-disabled='true'] {
  --btn-bg: var(--line); --btn-bd: var(--line); --btn-fg: var(--ink-muted);
  cursor: not-allowed;
}

.btn--ghost { --btn-bg: transparent; --btn-fg: var(--brand-strong); --btn-bd: var(--line); }
.btn--ghost:hover { --btn-bg: var(--surface-soft); --btn-bd: var(--brand-decor); }

.btn--quiet { --btn-bg: var(--surface-soft); --btn-fg: var(--brand-strong); --btn-bd: var(--surface-strong); }
.btn--quiet:hover { --btn-bg: var(--surface-strong); --btn-bd: var(--brand-decor); }

/* On a deep band. */
.btn--invert { --btn-bg: var(--paper); --btn-fg: var(--brand-strong); --btn-bd: var(--paper); }
.btn--invert:hover { --btn-bg: var(--surface-strong); --btn-bd: var(--surface-strong); }
.btn--outline-invert { --btn-bg: transparent; --btn-fg: var(--on-deep); --btn-bd: var(--lightbox-chrome-line); }
.btn--outline-invert:hover { --btn-bg: var(--deep-fill-strong); --btn-bd: var(--on-deep); }

.btn--danger { --btn-bg: var(--danger); --btn-bd: var(--danger); --btn-fg: var(--on-deep); }
.btn--danger:hover { --btn-bg: var(--danger-hover); --btn-bd: var(--danger-hover); }

/* F8 — a live 1:1 contrast failure, not a polish item. A .btn is an <a> as often as it is a
   <button>, and `.band--deep a { color: var(--on-deep) }` is (0,1,1) while `.btn { color:
   var(--btn-fg) }` is (0,1,0). The band rule wins, so `.btn--invert` (white fill) painted white
   text on white — the primary تبرّع CTA in the home page's closing band was invisible. Restate
   the button's own colour at equal-or-greater specificity, once, for every deep ground.
   Regression check: for every .btn in the DOM, computed color !== computed background-color. */
.band--deep .btn,
.footer .btn,
.hero .btn { color: var(--btn-fg); }

.btn--sm { min-height: 36px; padding: 6px var(--s4); font-size: var(--fs-xs); }
.btn--lg { min-height: 52px; padding: 14px var(--s6); font-size: var(--fs-base); }
.btn--block { width: 100%; }

.btn-row { display: flex; flex-wrap: wrap; }
.btn-row > * { margin-inline-end: var(--s3); margin-block-end: var(--s3); }
.btn-row--center { justify-content: center; }

/* A text link that reads as an action — and is therefore held to the same 44px tap target as a
   button (D11). It sits in card footers and section heads next to real buttons; at its natural
   27px line box it was the one control on the page a thumb could miss. */
.link-more {
  display: inline-flex; align-items: center;
  min-height: var(--btn-min-h);
  font-weight: var(--btn-weight); font-size: var(--fs-sm);
  color: var(--brand); text-decoration: none;
}
.link-more::after { content: '←'; margin-inline-start: 6px; transition: transform var(--dur) var(--ease); }  /* RTL: onward = left */
.link-more:hover { color: var(--brand-hover); text-decoration: underline; }
.link-more:hover::after { transform: translateX(-3px); }
.band--sand .link-more, .band--sand-strong .link-more { color: var(--brand-strong); }

/* ---------------------------------------------------------------- cards --- */

.card {
  display: flex; flex-direction: column;
  background: var(--card-bg);
  border: var(--bd) solid var(--card-border-color);
  border-radius: var(--r-card);
  box-shadow: var(--card-shadow);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.card--link:hover { border-color: var(--card-hover-border); }
@media (prefers-reduced-motion: no-preference) {
  .card--link:hover { transform: translateY(var(--card-hover-lift)); }
}
.card--flat { border-radius: var(--r-sm); }
.card__body { padding: var(--card-pad); display: flex; flex-direction: column; flex: 1; }
.card__body > * + * { margin-block-start: var(--s3); }
.card__title { margin: 0; font-size: var(--fs-md); }
/* §5.4 — the icon tile used to sit ALONE above the title as its own flex-column row, which read
   as two objects in opposite corners of the card rather than one heading. Icon and title are one
   lockup. */
.card__lockup { display: flex; align-items: center; }
.card__lockup > * + * { margin-inline-start: var(--s4); }
.card__lockup .card__title { margin: 0; }
/* A .card__body is a stretch flex column, so every button in it grew to the full card width —
   including the ones that are not .btn--block. A page-level action is auto-width once there is
   room for it to be. */
@media (min-width: 700px) {
  .card__body > .btn:not(.btn--block) { align-self: flex-start; }
}
.card__title a { color: inherit; text-decoration: none; }
.card__title a:hover { text-decoration: underline; }
.card__text { margin: 0; color: var(--ink-muted); font-size: var(--fs-sm); }
.card__foot {
  margin-top: auto; padding-top: var(--s3);
  border-top: var(--bd) solid var(--line-soft);
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
}
.card__foot > * + * { margin-inline-start: var(--s3); }
/* R4 — D12. `aspect-ratio` is Chrome 88, so on Chrome 81 this block collapsed to ZERO height
   and every card lost its picture. The programme card's media is a FIXED height instead, which
   needs no ratio machinery at all and is what §4.3.2 specifies; the gallery tile, which must
   stay square at any column width, gets the padding-percentage fallback further down. */
.card__media { block-size: 168px; background: var(--surface-soft); overflow: hidden; }
.card__media img { width: 100%; height: 100%; object-fit: cover; }
/* Cards without a photograph keep the same media block, filled with a flat sand panel and the
   card's own icon. Half a grid with pictures and half without leaves a hole under every short
   card, because a grid row stretches to its tallest item. */
/* --green-200, not the tan: see the token's note in theme.css. The tile fills were where most
   of this page's beige actually lived. */
.card__media--blank {
  display: grid; place-items: center;
  background: var(--green-200);
  color: var(--brand);
  border-bottom: var(--bd) solid var(--card-border-color);
}

/* F2/C2 — the programme card.
   Before: a 16:10 media block was rendered for EVERY card, so a programme with no photograph was
   a 290×180 empty sand rectangle with a 24px icon lost in the middle — 78,760px² of flat beige
   per programme on a phone, ×7 programmes ≈ 2,500px of scroll to read seven titles. Worse, the
   ONE card that had a photograph rendered the photo AND an icon tile below it, which pushed its
   title ~75px below the titles beside it: titles did not align across a row.
   Now: a card with a summary keeps a 168px media block (fixed height — no aspect-ratio, see R4)
   and gains the summary that makes it a card rather than a link. A programme with no summary
   renders as a .tile-link instead, which is honest about being one line and an arrow.
   NEVER both a photo and an icon tile on the same card: if there is an image it fills the mark
   or the media block and the icon does not appear anywhere. */
.card--programme .card__body { padding: var(--card-pad); }
.card--programme .card__title { font-size: var(--fs-md); line-height: 1.45; color: var(--brand-strong); }
.card--programme .card__text {
  font-size: var(--fs-sm); color: var(--ink-muted); line-height: 1.8;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
/* Below 700px the media block is dropped entirely and the card becomes a row: ~132px per
   programme instead of ~360px, so seven programmes fit in about one screen rather than three. */
@media (max-width: 699px) {
  .card--programme { flex-direction: row; align-items: flex-start; padding: var(--s4); }
  .card--programme .card__media { display: none; }
  .card--programme .card__body { padding: 0; flex: 1; min-width: 0; }
  .card--programme .card__body > * + * { margin-block-start: var(--s2); }
  .card--programme .card__foot { border-top: 0; padding-top: var(--s2); }
  .card--programme > .icon-tile { margin-inline-end: var(--s4); }
}

/* The degenerate case: one line and an arrow. A card with a title and nothing else is a LINK,
   not a card — rendering it as one is what leaves 168px of beige under every title. */
.tile-link {
  display: flex; align-items: center;
  min-height: 88px;
  padding: var(--s5);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  background: var(--paper);
  color: inherit; text-decoration: none;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.tile-link > * + * { margin-inline-start: var(--s4); }
.tile-link:hover { border-color: var(--brand-decor); background: var(--surface-soft); color: inherit; }
.tile-link__mark {
  inline-size: 44px; block-size: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--green-200);
  color: var(--brand-strong);
  overflow: hidden;
}
.tile-link__mark img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.tile-link__body { flex: 1; min-width: 0; }
.tile-link__title {
  display: block;
  font-size: var(--fs-md); font-weight: var(--h-weight);
  color: var(--brand-strong); line-height: 1.5;
}
.tile-link__go { flex: none; color: var(--brand); font-size: var(--fs-lg); line-height: 1; }
@media (max-width: 559px) {
  .tile-link { padding: var(--s4); min-height: 76px; }
}

/* The 44px mark the mobile ROW layout needs. At ≥700px the media block above is the mark, so
   this one is not rendered at all — never a photo AND an icon tile on the same card. */
.card__mark { display: none; }
@media (max-width: 699px) {
  .card--programme .card__mark {
    display: grid;
    inline-size: 44px; block-size: 44px;
    margin-inline-end: var(--s4);
  }
}
.card__mark img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* Flat tile behind a programme icon — never a circle with a shadow. */
.icon-tile {
  inline-size: 48px; block-size: 48px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--green-200);
  color: var(--brand-strong);
}
.icon-tile--deep { background: var(--deep-fill-strong); color: var(--on-deep); }

/* -------------------------------------------------------------- badges ---- */

.badge {
  display: inline-flex; align-items: center;
  padding: 3px 10px;
  border-radius: var(--r-chip);
  border: var(--bd) solid var(--line);
  background: var(--surface-soft);
  color: var(--brand-strong);
  font-size: var(--fs-xs); font-weight: var(--h-weight); line-height: 1.6;
  white-space: nowrap;
}
.badge > svg:first-child { margin-inline-end: 6px; }
.badge--gold   { background: var(--accent-wash); border-color: var(--accent); color: var(--accent-ink); }
.badge--ok     { background: var(--ok-wash); border-color: var(--ok-line); color: var(--ok); }
.badge--danger { background: var(--danger-wash); border-color: var(--danger-line); color: var(--danger); }
.badge--plain  { background: transparent; }
.badge--deep   { background: var(--deep-fill-strong); border-color: var(--line-deep); color: var(--on-deep); }

/* ------------------------------------------------- placeholder review mark -- */
/* The «بحاجة إلى مراجعة» marker. It replaces .ribbon, which was a full-strength gold pill
   repeated ELEVEN times on the home page alone — ~200px wide against a ~92px call to action, so
   the disclaimer carried more than twice the visual weight of the action beside it, and four
   identical copies in a row turned the statistics band into a warnings band.
   The information is not reduced: every marker still carries the full sentence in a title and in
   an .sr-only sibling. Only the shouting is.

   THREE TIERS, and no others:
     .pmark        inline marker — cards, list rows, table cells, statistic cells. Dot + label.
     .pmark--block once per page, at the top of a page body whose MAIN content is placeholder.
     .pmark--dot   bare dot with an aria-label, for a dense table where a label would not fit. */
.pmark {
  display: inline-flex; align-items: center;
  /* No fill, no border, no pill. The label is --ink-muted (6.3:1), never --accent (2.5:1). */
  min-height: 22px;
  color: var(--ink-muted);
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.6;
  white-space: nowrap;
}
.pmark::before {
  content: ''; flex: none;
  inline-size: 6px; block-size: 6px;
  margin-inline-end: 6px;
  border-radius: var(--r-pill);
  background: var(--accent);
}
.band--deep .pmark, .band--deep .pmark--dot { color: var(--on-deep-faint); }

/* Tier 3 — the dot alone. Its aria-label carries the sentence the label would have. */
.pmark--dot {
  display: inline-block;
  inline-size: 6px; block-size: 6px;
  border-radius: var(--r-pill);
  background: var(--accent);
}
.pmark--dot::before { content: none; }

/* Tier 2 — the once-per-page block. This is the ONLY place the gold wash still appears at
   full strength, and it is allowed to because it is stating something about the whole page. */
.pmark--block {
  display: flex; align-items: flex-start;
  min-height: 0;
  padding: var(--s3) var(--s4);
  background: var(--accent-wash);
  border: var(--bd) solid var(--accent);
  border-inline-start: var(--bd-strong) solid var(--accent);
  border-radius: var(--r-sm);
  color: var(--accent-ink);
  font-size: var(--fs-sm); font-weight: 400; line-height: 1.7;
  white-space: normal;
}
.pmark--block::before { content: none; }
.pmark--block > svg { flex: none; margin-block-start: 3px; margin-inline-end: var(--s3); }

/* -------------------------------------------------------------- alerts ---- */

.alert {
  display: flex;
  padding: var(--s4) var(--s5);
  border: var(--bd) solid var(--line);
  border-inline-start: var(--bd-strong) solid var(--brand-decor);
  border-radius: var(--r-sm);
  background: var(--surface-soft);
  color: var(--ink);
  font-size: var(--fs-sm);
  line-height: 1.8;
}
.alert > * + * { margin-inline-start: var(--s3); }
.alert h3 { font-size: var(--fs-base); margin: 0 0 var(--s1); }
.alert--ok    { background: var(--ok-wash); border-color: var(--ok-line); border-inline-start-color: var(--ok); }
.alert--error { background: var(--danger-wash); border-color: var(--danger-line); border-inline-start-color: var(--danger); color: var(--danger-ink); }
.alert--gold  { background: var(--accent-wash); border-color: var(--accent); border-inline-start-color: var(--accent); color: var(--accent-ink); }
.alert__icon  { flex: none; margin-top: 2px; }
.alert ul { margin: 0; padding-inline-start: 1.2em; }

/* --------------------------------------------------------- breadcrumbs ---- */

.crumbs {
  background: var(--surface-soft);
  /* §5.3 — the crumbs strip and the .pagehead beneath it are both --surface-soft, so a full
     --line rule between them cuts one field into two sand slabs. A --line-soft hairline reads
     as a division inside one field. */
  border-bottom: var(--bd) solid var(--line-soft);
  font-size: var(--fs-sm);
}
.crumbs ol {
  display: flex; flex-wrap: wrap; align-items: center;
  list-style: none; margin: 0;
  padding-block-start: var(--s3); padding-block-end: var(--s3);
  padding-inline-start: 0; padding-inline-end: 0;
}
.crumbs li { margin: 0; display: flex; align-items: center; color: var(--ink-muted); }
.crumbs li + li { margin-inline-start: var(--s2); }
.crumbs li + li::before { content: '/'; margin-inline-end: var(--s2); color: var(--ink-faint); }
.crumbs a { color: var(--brand-strong); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }
.crumbs [aria-current='page'] { color: var(--ink-muted); }

/* --------------------------------------------------------------- pager ---- */

.pager {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  margin-top: var(--s6);
}
.pager > * { margin-inline-end: var(--s2); margin-block-end: var(--s2); }
.pager a, .pager span {
  min-width: var(--btn-min-h); min-height: var(--btn-min-h);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 var(--s3);
  border: var(--bd) solid var(--line);
  border-radius: var(--btn-radius);
  background: var(--paper);
  color: var(--brand-strong);
  font-size: var(--fs-sm); font-weight: var(--btn-weight);
  text-decoration: none;
}
.pager a:hover { border-color: var(--brand-decor); background: var(--surface-soft); }
.pager [aria-current='page'] { background: var(--brand); border-color: var(--brand); color: var(--on-deep); }
.pager .is-disabled { color: var(--ink-faint); background: var(--surface-soft); }
.pager__info {
  border: 0 !important; background: none !important;
  color: var(--ink-muted) !important; font-weight: 400 !important;
}

/* --------------------------------------------------------------- table ---- */

.table-wrap {
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  overflow-x: auto;                 /* wide tables scroll in their own box, never the page */
  background: var(--paper);
  -webkit-overflow-scrolling: touch;
}
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.table th, .table td { padding: var(--s3) var(--s4); text-align: start; vertical-align: middle; }
.table thead th {
  background: var(--surface-soft);
  color: var(--brand-strong);
  font-weight: var(--h-weight);
  border-bottom: var(--bd) solid var(--line);
  white-space: nowrap;
}
.table tbody tr + tr td { border-top: var(--bd) solid var(--line-soft); }
.table tbody tr:hover { background: var(--row-hover); }
.table td a { color: var(--brand-strong); font-weight: 600; text-decoration: none; }
.table td a:hover { text-decoration: underline; }
.table__num { color: var(--ink-muted); width: 1%; white-space: nowrap; }

/* --------------------------------------------------------------- forms ---- */
/* D12: every control declares its own background AND colour. Android's force-dark
   repaints anything it thinks it can guess at, and a control with an inherited
   background becomes a dark box with dark text. */

/* A <fieldset> ships with a browser border, a browser legend inset and — the one that actually
   bites — `min-inline-size: min-content`, which makes it refuse to shrink below its widest child
   and pushes a grid of cards past the viewport on a phone. All three are reset here. */
fieldset { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
legend { padding: 0; display: block; }

.field { display: flex; flex-direction: column; margin-bottom: var(--s4); }
.field > * + * { margin-block-start: 6px; }
.field--row { flex-direction: row; align-items: flex-start; }
.field--row > * + * { margin-block-start: 0; margin-inline-start: var(--s3); }
.field-grid { display: grid; gap: 0 var(--s4); grid-template-columns: 1fr; }
@media (min-width: 700px) { .field-grid--2 { grid-template-columns: 1fr 1fr; } }

.label { font-weight: var(--h-weight); font-size: var(--fs-sm); color: var(--brand-strong); line-height: 1.6; }
/* A survey question is the CONTENT of the card it sits in, not a caption above a control, so it
   is set at the card-title size. Same colour, same weight, same .req — only the step up in size. */
.label--lg { font-size: var(--fs-md); }
.label .req { color: var(--danger); }
.hint { font-size: var(--fs-xs); color: var(--ink-muted); line-height: 1.7; }

.input, .select, .textarea {
  width: 100%;
  /* 48px, not 46px: a touch target should not sit within 2px of its own 44px floor. The admin
     panel, which is mouse-first and dense, stays at 40px. */
  min-height: 48px;
  padding: 10px var(--s3);
  font: inherit;
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--field-fg);
  background-color: var(--field-bg);
  border: var(--bd) solid var(--field-line);
  border-radius: var(--r-field);
  appearance: none;
  -webkit-appearance: none;
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.textarea { min-height: 140px; resize: vertical; line-height: 1.8; }
.select {
  /* In RTL the chevron belongs on the LEFT; `background-position: left` is literal
     and correct here, not a direction bug. */
  background-image: var(--select-chevron);
  background-repeat: no-repeat;
  background-position: left var(--s3) center;
  padding-inline-end: 38px;
}
.input:hover, .select:hover, .textarea:hover { border-color: var(--brand-decor); }
.input:focus, .select:focus, .textarea:focus {
  border-color: var(--focus-ring);
  outline: var(--bd-strong) solid var(--focus-ring-soft);
  outline-offset: 0;
}
.input::placeholder, .textarea::placeholder { color: var(--field-placeholder); opacity: 1; }
.input[readonly], .input[disabled], .select[disabled], .textarea[disabled] {
  background-color: var(--surface-soft);
  color: var(--ink-muted);
  -webkit-text-fill-color: var(--ink-muted);   /* iOS/Android ignore `color` when disabled */
}
.input[aria-invalid='true'], .select[aria-invalid='true'], .textarea[aria-invalid='true'] {
  border-color: var(--danger);
  background-color: var(--danger-wash);
}
/* Chrome's autofill fill is a SYSTEM colour, and force-dark loves it. Pin both. */
.input:-webkit-autofill, .textarea:-webkit-autofill, .select:-webkit-autofill {
  -webkit-text-fill-color: var(--field-fg);
  -webkit-box-shadow: 0 0 0 1000px var(--field-bg) inset;
}

/* R7 — D12. `accent-color` is Chrome 93, so on Chrome 81 the consent checkbox rendered system
   blue: the only non-brand colour on the page, on the one control a beneficiary must tick to
   submit an aid request. The native input is kept in the DOM, focusable and in the accessibility
   tree (the .sr-only clip-path recipe, never `left:-9999px` — trap 12), and the visible box is a
   sibling driven by `input:checked + .check__box`. No :has(), no accent-color dependency. */
.check {
  display: flex; align-items: flex-start;
  font-size: var(--fs-sm); line-height: 1.75; color: var(--ink);
  cursor: pointer; padding: var(--s2) 0;
  min-height: var(--btn-min-h);
}
.check input[type='checkbox'], .check input[type='radio'] {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: 0; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  cursor: pointer;
}
.check__box {
  flex: none;
  inline-size: 20px; block-size: 20px;
  margin-block-start: 3px; margin-inline-end: var(--s3);
  border: 2px solid var(--field-line);
  border-radius: var(--r-xs);
  background-color: var(--field-bg);
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.check:hover .check__box { border-color: var(--brand-decor); }
.check input:checked + .check__box { border-color: var(--brand); background-color: var(--brand); }
/* The tick is drawn with two borders on a rotated box: no data URI, no second colour token, and
   nothing to fail to load. It is not elevation, so the one-shadow rule does not apply. */
.check input:checked + .check__box::after {
  content: ''; display: block;
  inline-size: 5px; block-size: 10px;
  margin: 0 auto;
  border: solid var(--on-deep);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
/* R5 — TWO rules, never one selector list. The native input is clipped to 1px, so the ring on
   the SIBLING box is the only focus indication this control has; a list containing
   :focus-visible (Chrome 86) is dropped in its entirety on the Chrome 81 floor and takes the
   :focus half down with it. */
.check input:focus + .check__box {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
.check input:focus-visible + .check__box {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
.check input[aria-invalid='true'] + .check__box { border-color: var(--danger); }

/* Radio cards — the request-type picker, which is the D10 form's primary control.
   R6 — D12. This used to be `.choice:has(input:checked)`. `:has()` is Chrome 105, so on
   Chrome 81 the picker had NO SELECTED STATE AT ALL: a visitor could tick a type and see
   nothing change. The state is a sibling selector now, which has worked since CSS 2.1, and the
   radio is drawn by CSS rather than by `accent-color` (Chrome 93) so it is brand-coloured
   everywhere. Do not reintroduce :has() here or anywhere else in this file. */
.choice-grid { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
/* The survey's answer sets are SHORT labels («نعم» / «راضٍ جدا») rather than a request type with
   a description and an attachment list, and the option COUNT varies per question — three on most,
   FIVE on «ما هو تقييمك لأداء الجمعية بشكل عام؟». So the count must not come from the markup:
   TRAP 12 says an inline `grid-template-columns: repeat(@Options.Count, 1fr)` cannot be overridden
   by a media query, and the phone would silently keep the cramped desktop row. It comes from the
   available width instead.

   `auto-fit` (not auto-fill) is what makes one rule serve both counts: at ≥700px the track floor
   is small enough that five tracks fit, and a three-option question COLLAPSES the two empty ones
   and stretches its three to fill the row. Three across, five across, one line either way.
   Below 700px — the breakpoint .field-grid--2 and .card--programme already use — every option is
   its own full-width row, which is the only layout that keeps a 44px target and an unwrapped
   Arabic label on a 390px phone. */
.choice-grid--compact { grid-template-columns: 1fr; }
@media (min-width: 700px) {
  .choice-grid--compact { grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); }
}
.choice { display: block; cursor: pointer; }
.choice input {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: 0; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}
.choice__box {
  display: flex; align-items: flex-start;
  block-size: 100%;
  padding: var(--s4);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper);
  transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.choice__box::before {
  content: ''; flex: none;
  inline-size: 20px; block-size: 20px;
  margin-inline-end: var(--s3); margin-block-start: 2px;
  border: 2px solid var(--field-line);
  border-radius: var(--r-pill);
  background: var(--field-bg);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.choice input:hover + .choice__box,
.choice:hover .choice__box { border-color: var(--brand-decor); background: var(--surface-soft); }
.choice input:checked + .choice__box { border-color: var(--brand); background: var(--surface-soft); }
/* The inner dot. This box-shadow is a CONTROL's fill, not elevation — it never blurs and never
   falls outside the element — so it is exempt from the one-shadow rule exactly as a border is. */
.choice input:checked + .choice__box::before {
  border-color: var(--brand);
  box-shadow: inset 0 0 0 4px var(--field-bg), inset 0 0 0 10px var(--brand);
}
/* R5 — TWO rules, never one selector list, for the same reason as .check above: on Chrome 81
   the list is invalid and BOTH halves are dropped, leaving the D10 form's primary control with
   no visible focus at all. */
.choice input:focus + .choice__box {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
.choice input:focus-visible + .choice__box {
  outline: var(--bd-strong) solid var(--focus-ring);
  outline-offset: 2px;
}
.choice__label { font-weight: var(--h-weight); color: var(--brand-strong); font-size: var(--fs-sm); display: block; }
.choice__note { font-size: var(--fs-xs); color: var(--ink-muted); line-height: 1.7; display: block; }
.choice__note + .choice__note { margin-block-start: var(--s1); }

/* Linear scale — the 1–5 rating on /survey. One component, and it obeys the same three rules the
   radio cards above it do: the native input stays in the DOM and in the accessibility tree, the
   visible box is a SIBLING driven by `input:checked + .scale__box`, and nothing here depends on
   :has() (Chrome 105) or accent-color (Chrome 93). A rating a visitor cannot see themselves give
   is a rating they give twice, and on the Chrome 81 floor D12 sets that is exactly what the two
   modern approaches produce.

   Grid rather than flex, so the `gap` is the Chrome 66 one and not the Chrome 84 one (R2). The
   step count comes from the QUESTION's ScaleMin/ScaleMax, so a fork that publishes a 1–10 scale
   gets ten columns from the same rule; below 480px the columns share the row instead of holding
   a fixed width, so the scale never wraps and never overflows. */
.scale {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--s2);
}
/* A rating scale is the one control that does NOT stack on a phone: 1..5 read as a run, and a
   vertical column of digits stops being a scale. The steps share the row instead, which still
   leaves each one wider than the 44px floor at 390px. Above the phone breakpoint they take a
   fixed width and sit at the inline-start rather than stretching to the full measure. */
@media (min-width: 700px) {
  .scale { grid-auto-columns: minmax(0, 76px); justify-content: start; }
}
.scale__item { display: block; cursor: pointer; }
/* The .sr-only clip-path recipe — never left:-9999px (trap 12). */
.scale__item input {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: 0; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
}
.scale__box {
  display: flex; align-items: center; justify-content: center;
  min-block-size: var(--btn-min-h);
  padding: var(--s2);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  background-color: var(--paper);
  color: var(--brand-strong);
  font-size: var(--fs-md); font-weight: var(--h-weight);
  font-variant-numeric: tabular-nums;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.scale__item input:hover + .scale__box,
.scale__item:hover .scale__box { border-color: var(--brand-decor); background-color: var(--surface-soft); }
.scale__item input:checked + .scale__box {
  border-color: var(--brand); background-color: var(--brand); color: var(--on-deep);
}
/* R5 — TWO rules, never one selector list. A list containing :focus-visible (Chrome 86) is
   dropped in its ENTIRETY on Chrome 81, which would leave this control with no focus ring at
   all — and the input itself is clipped to 1px, so its own ring is not a fallback. */
.scale__item input:focus + .scale__box {
  outline: var(--bd-strong) solid var(--focus-ring); outline-offset: 2px;
}
.scale__item input:focus-visible + .scale__box {
  outline: var(--bd-strong) solid var(--focus-ring); outline-offset: 2px;
}

.field-error { color: var(--danger); font-size: var(--fs-xs); font-weight: 600; }
.field-error:empty { display: none; }

/* The honeypot. TRAP 12: `left:-9999px` in an RTL document creates 10,000px of
   horizontal scroll on every page that carries a form. Size it to nothing and clip. */
.hp {
  position: absolute !important;
  inline-size: 1px; block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  opacity: 0;
  pointer-events: none;
}

.filterbar {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  padding: var(--s4);
  background: var(--surface-soft);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  margin-bottom: var(--s5);
}
.filterbar > * { margin-inline-end: var(--s3); margin-block-end: var(--s3); }
.filterbar > *:last-child { margin-inline-end: 0; }
/* Kill the field's default 16px bottom margin, but keep the SAME --s3 trailing margins the other
   filterbar children carry (`> *` above). The old `margin: 0` reset zeroed the block-end too, so
   the field's border box sat 12px BELOW the submit button's: `align-items: flex-end` aligns MARGIN
   boxes, and the button's 12px bottom margin that the field lacked pulled its visible box up.
   Matching the margins makes the input and the «بحث» button share one baseline. Longhands only —
   `margin-inline`/`margin-block` shorthands break on the Chrome 81 floor (D12). */
.filterbar .field {
  margin-block-start: 0; margin-block-end: var(--s3);
  margin-inline-start: 0; margin-inline-end: var(--s3);
  flex: 1 1 320px;
}
.filterbar .field--narrow { flex: 0 1 240px; }
.chips { display: flex; flex-wrap: wrap; }
.chips > * { margin-inline-end: var(--s2); margin-block-end: var(--s2); }
.chip {
  display: inline-flex; align-items: center;
  min-height: 36px; padding: 0 var(--s4);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-chip);
  background: var(--paper);
  color: var(--brand-strong);
  font: inherit; font-size: var(--fs-xs); font-weight: var(--btn-weight);
  text-decoration: none; cursor: pointer;
  transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.chip:hover { border-color: var(--brand-decor); background: var(--surface-soft); color: var(--brand-strong); }
.chip[aria-pressed='true'], .chip.is-on {
  background: var(--brand); border-color: var(--brand); color: var(--on-deep);
}
.chip > svg:first-child { margin-inline-end: 6px; }
.chip__count { font-weight: 400; opacity: .75; margin-inline-start: 6px; }

/* ---------------------------------------------------------------- hero ---- */

.hero {
  position: relative;
  background: var(--hero-bg);
  color: var(--on-deep);
  overflow: hidden;
  isolation: isolate;
}
/* The photograph is held right down so the type keeps its 10:1 against the block.
   D11: the hero must be readable when nothing moves — and nothing here does. */
.hero__bg {
  /* R3 — physical and symmetric, so there is nothing for RTL to get wrong. */
  position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: -2;
  background-size: cover;
  background-position: center;
  opacity: var(--hero-image-opacity);
}
.hero::after {
  content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: -1;
  background: var(--hero-overlay);
}
.hero__inner {
  display: grid; gap: var(--s6);
  padding-block-start: var(--hero-y); padding-block-end: var(--hero-y);
  align-items: center;
}
@media (min-width: 960px) { .hero__inner { grid-template-columns: 1.25fr .75fr; } }
.hero__title {
  font-size: var(--fs-3xl);
  color: var(--on-deep);
  margin: 0 0 var(--s4);
  line-height: var(--lh-hero);
  max-width: 18ch;
}
/* F3 — the eyebrow above the H1 used to repeat the association's name verbatim, 40px above the
   same twelve characters. It now carries the licence line, every part of which comes from
   SiteSetting. */
.hero__eyebrow {
  display: block;
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.7;
  /* --on-deep-muted, not --on-deep-faint: the hero ground is the green PLUS a photograph at
     --hero-image-opacity behind a gradient, so its effective luminance varies across the band
     and the faint tint's 5.37:1 headroom is not guaranteed there the way it is in the footer. */
  color: var(--on-deep-muted);
  margin-bottom: var(--s3);
}
.hero__lead {
  font-size: var(--fs-md);
  color: var(--on-deep-muted);
  max-width: 56ch;
  margin: 0 0 var(--s3);
  line-height: 1.85;
}
.hero__cta { display: flex; flex-wrap: wrap; margin-block-start: var(--s5); }
.hero__cta > * { margin-inline-end: var(--s3); margin-block-end: var(--s3); }
/* F3 — the association's only slogan used to be set at --fs-sm, smaller and quieter than the
   button labels under it. It is the one line of copy the association actually wrote. */
.hero__tagline {
  display: inline-flex; align-items: center;
  font-size: var(--fs-tagline); font-weight: var(--h-weight); line-height: 1.4;
  color: var(--surface-strong); margin-bottom: var(--s3);
}
.hero__tagline::before {
  content: ''; flex: none;
  inline-size: var(--rule-w); block-size: var(--rule-h);
  margin-inline-end: var(--s3);
  background: var(--rule-color); border-radius: var(--r-xs);
}
/* Hero slide dots. Created by the island — the markup ships with an empty, hidden holder, so a
   page whose script never runs shows no dots rather than dots that do nothing. */
.hero__dot {
  inline-size: 34px; block-size: var(--btn-min-h);
  padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.hero__dot::before {
  content: ''; display: block;
  inline-size: 26px; block-size: 3px; border-radius: var(--r-xs);
  background: var(--on-deep-faint);
  transition: background-color var(--dur) var(--ease);
}
.hero__dot.is-on::before { background: var(--accent); }
.hero__dot:hover::before { background: var(--on-deep); }

.hero__facts {
  border: var(--bd) solid var(--line-deep);
  border-radius: var(--r-lg);
  background: var(--deep-fill);
  padding: var(--s5);
  display: grid; gap: var(--s4);
  margin: 0;
}
.hero__fact { display: grid; gap: var(--s0); margin: 0; }
.hero__fact dt { font-size: var(--fs-xs); color: var(--on-deep-faint); }
.hero__fact dd { margin: 0; font-size: var(--fs-md); font-weight: var(--h-weight); color: var(--on-deep); }

/* Quick-action rows inside the hero panel. Flat, hairline-separated, 44px+ tap targets. */
.hero__action {
  display: flex; align-items: center;
  min-height: var(--btn-min-h);
  padding: var(--s2) 0;
  color: var(--on-deep);
  text-decoration: none;
  border-radius: var(--r-sm);
  transition: opacity var(--dur) var(--ease);
}
.hero__action > * + * { margin-inline-start: var(--s3); }
.hero__action + .hero__action,
.hero__fact + .hero__action { border-top: var(--bd) solid var(--line-deep); padding-top: var(--s4); }
.hero__action b { display: block; font-size: var(--fs-base); font-weight: var(--h-weight); }
.hero__action small { display: block; font-size: var(--fs-xs); color: var(--on-deep-faint); line-height: 1.6; }
.hero__action:hover { color: var(--on-deep); opacity: .85; }
.hero__action:hover b { text-decoration: underline; }

/* Interior page header — flat band, no photo. */
.pagehead { background: var(--surface-soft); border-bottom: var(--bd) solid var(--line); }
/* F13 — a ~430px sand slab for an eyebrow, a heading and one line of lead. A FIXED --s6 rather
   than --band-y-tight (which reaches 56px on a page that needs 32) makes this a ~150px band. */
.pagehead__inner { padding-block-start: var(--s6); padding-block-end: var(--s6); }
.pagehead h1 { margin: 0 0 var(--s2); }
.pagehead p { margin: 0; color: var(--ink-muted); font-size: var(--fs-md); max-width: var(--measure-narrow); }
.pagehead p { max-width: var(--measure-lead); }
.pagehead__row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; }
.pagehead__row > * { margin-inline-end: var(--s4); margin-block-end: var(--s3); }
.pagehead__row > *:last-child { margin-inline-end: 0; }

/* --------------------------------------------------------------- stats ---- */

.stats {
  display: grid; gap: var(--bd);
  background: var(--line);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}
@media (min-width: 560px) { .stats { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat { background: var(--paper); padding: var(--card-pad); display: grid; gap: var(--s2); align-content: start; }
.stat .pmark { margin-block-start: var(--s1); justify-self: start; }
.stat__rule { inline-size: var(--rule-w); block-size: var(--rule-h); background: var(--rule-color); border-radius: var(--r-xs); }
.stat__value {
  font-size: var(--fs-stat);
  font-weight: var(--h-weight-strong);
  color: var(--brand-strong);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
/* C1(b) — the value and its unit used to be one run of text at one weight («١٤٠٠ سلة»), so the
   number did not read as a number. Splitting the unit off at half the size is the whole fix. */
.stat__unit {
  font-size: var(--fs-unit); font-weight: var(--h-weight);
  color: var(--brand);
  margin-inline-start: var(--s2);
  vertical-align: baseline;
}
.stat__label { font-size: var(--fs-sm); color: var(--ink-muted); line-height: 1.6; }
.band--deep .stats { background: var(--line-deep); border-color: var(--line-deep); }
.band--deep .stat { background: transparent; }
.band--deep .stat__value { color: var(--on-deep); }
.band--deep .stat__unit { color: var(--surface-strong); }
.band--deep .stat__label { color: var(--on-deep-muted); }

/* ------------------------------------------------------------- gallery ---- */

.gallery-grid { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
@media (min-width: 700px) { .gallery-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } }
/* R4 — D12. `aspect-ratio` is Chrome 88: on Chrome 81 every tile in the album collapsed to
   zero height and the page rendered as a column of 1px lines. The padding-percentage box is the
   fallback, and `@supports (aspect-ratio: 1)` is a RELIABLE feature query (unlike a flex-gap
   test), so the modern path stays clean. */
.gallery-item {
  display: block; position: relative;
  block-size: 0; padding-block-start: 100%;
  border: var(--bd) solid var(--line);
  border-radius: var(--r-media);
  overflow: hidden;
  background: var(--surface-soft);
  padding-inline-start: 0; padding-inline-end: 0; padding-block-end: 0;
  cursor: zoom-in;
  transition: border-color var(--dur) var(--ease);
}
.gallery-item img {
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}
@supports (aspect-ratio: 1) {
  .gallery-item { block-size: auto; padding-block-start: 0; aspect-ratio: 1 / 1; }
  .gallery-item img { position: static; }
}
.gallery-item:hover { border-color: var(--brand-decor); }
@media (prefers-reduced-motion: no-preference) {
  .gallery-item:hover img { transform: scale(1.04); }
}

/* F4 — the strip used to be `grid-auto-columns: minmax(160px, 1fr)` inside an `overflow-x: auto`
   box, so the tiles collapsed to their minimum, their widths were ragged, and a NATIVE OS
   SCROLLBAR rendered directly under the row at 145px tall: the single most dated element on the
   site. At desktop it is a real grid — six tiles, 3×2, no scrollbar, no ragged widths. Below
   700px it stays horizontal, because a phone should not scroll past six squares, but the
   scrollbar is hidden and the affordance is a peeking next tile. */
.gallery-strip { display: grid; gap: var(--s3); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 700px) { .gallery-strip { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 699px) {
  .gallery-strip {
    grid-auto-flow: column;
    grid-template-columns: none;
    grid-auto-columns: 46%;          /* the next tile always peeks — that IS the affordance */
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    -ms-overflow-style: none;
    scrollbar-width: none;
  }
  .gallery-strip::-webkit-scrollbar { display: none; }
  .gallery-strip > * { scroll-snap-align: start; }
}

/* Lightbox — one of the three surfaces allowed the shadow token. */
.lightbox {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: var(--z-lightbox);
  background: var(--lightbox-bg);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s4);
}
.lightbox__img {
  max-width: min(1100px, 94vw); max-height: 86vh;
  width: auto; height: auto;
  object-fit: contain;
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-pop);
}
.lightbox__bar {
  position: absolute; right: 0; left: 0; top: 0;
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s4);
  color: var(--on-deep); font-size: var(--fs-sm);
}
.lightbox__btn {
  min-inline-size: 48px; min-block-size: 48px;
  display: grid; place-items: center;
  border: var(--bd) solid var(--lightbox-chrome-line);
  border-radius: var(--r-sm);
  background: var(--lightbox-chrome);
  color: var(--on-deep); font: inherit; font-size: var(--fs-lg); cursor: pointer;
}
.lightbox__btn:hover { background: var(--lightbox-chrome-hover); }
.lightbox__nav {
  position: absolute; right: var(--s4); left: var(--s4); top: 50%;
  transform: translateY(-50%);
  display: flex; justify-content: space-between; pointer-events: none;
}
.lightbox__nav .lightbox__btn { pointer-events: auto; }
.lightbox__caption { position: absolute; right: 0; left: 0; bottom: 0; padding: var(--s4); color: var(--on-deep-muted); font-size: var(--fs-sm); text-align: center; }

/* ----------------------------------------------------------- documents ---- */

/* F7 — seventy individually bordered, rounded, floating cards where the content is ONE list.
   The container carries the border and the radius; the rows carry a hairline between them. Grid
   `gap` is Chrome 66 and safe, and it is 0 here anyway. */
.doc-list {
  display: grid; gap: 0;
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  background: var(--paper);
  overflow: hidden;
  transition: opacity var(--dur) var(--ease);
}
/* Set by the filter island while a debounced search is in flight. A debounced submit with no
   feedback reads as a dead page. */
.doc-list.is-busy { opacity: .45; pointer-events: none; }
.doc {
  display: flex; align-items: center;
  min-height: 72px;
  padding: var(--s4) var(--s5);
  border: 0; border-radius: 0;
  background: var(--paper);
  text-decoration: none; color: inherit;
  transition: background-color var(--dur) var(--ease);
}
.doc + .doc { border-top: var(--bd) solid var(--line-soft); }
/* No border-colour change on hover: there is no border to change. */
.doc:hover { background: var(--surface-soft); color: inherit; }
.doc > * + * { margin-inline-start: var(--s4); }
/* F12 — the red PDF chip was the only red on the public site and read as an error state on 100%
   of rows. One neutral chip for every extension; a fork that genuinely scans by file type
   overrides --chip-neutral-* per extension. */
.doc__ext {
  inline-size: 44px; block-size: 44px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-xs);
  background: var(--chip-neutral-bg);
  color: var(--chip-neutral-fg);
  border: var(--bd) solid var(--chip-neutral-line);
  font-size: var(--fs-xs); font-weight: var(--h-weight-strong); letter-spacing: .04em;
}
.doc__main { flex: 1; min-width: 0; }
.doc__title {
  display: block;
  max-inline-size: 72ch;
  font-weight: var(--h-weight); color: var(--brand-strong);
  font-size: var(--fs-sm); line-height: 1.6;
}
.doc__meta {
  display: flex; flex-wrap: wrap; align-items: center;
  font-size: var(--fs-xs); color: var(--ink-muted);
}
.doc__meta > * { margin-inline-end: var(--s3); }
.doc__meta > *:last-child { margin-inline-end: 0; }
/* Size and download travel together at the row's inline-end, so the affordance is next to the
   fact that decides whether someone on mobile data taps it. */
.doc__trail { flex: none; display: flex; align-items: center; }
.doc__trail > * + * { margin-inline-start: var(--s4); }
.doc__size { font-size: var(--fs-xs); color: var(--ink-muted); white-space: nowrap; }
.doc__go {
  flex: none;
  inline-size: 40px; block-size: 40px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  color: var(--brand);
}
.doc:hover .doc__go { background: var(--surface-hover); }
@media (max-width: 559px) {
  .doc { padding-inline-start: var(--s4); padding-inline-end: var(--s4); }
  .doc__size { display: none; }   /* the size is repeated in the meta line on a phone */
}

/* ------------------------------------------------------- people / lists --- */

.person {
  display: grid; gap: var(--s3);
  padding: var(--card-pad);
  border: var(--bd) solid var(--card-border-color);
  border-radius: var(--r-card);
  background: var(--card-bg);
  box-shadow: var(--card-shadow);
  text-align: center;
}
/* F11 — nine members in a 4-column auto-fill grid left rows of 4/4/1 and one orphan. The count
   is KNOWN and fixed, so the columns are declared rather than inferred: 3×3 at desktop.
   TRAP 12: never write these as an inline style — an inline grid-template-columns cannot be
   overridden by a media query and the phone layout dies. */
.people-grid { display: grid; gap: var(--grid-gap); grid-template-columns: 1fr; }
@media (min-width: 620px) { .people-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .people-grid { grid-template-columns: repeat(3, 1fr); } }

/* RULING (§4.3.5): a 12px-rounded SQUARE, not a circle. A circle is border-radius 50%, which
   sits outside D11's 16px cap; rather than carve an exception, every avatar, icon tile,
   extension chip and thumbnail in this system is the same rounded rectangle.
   The initial that used to sit here identified nobody — for عبدالله and عايض the isolated ع
   renders as a shape most readers parse as ٤, and four of the nine members resolved to م or ع.
   It is a ROLE glyph now, which groups by the thing the page is actually organised around. */
.person__avatar {
  inline-size: 56px; block-size: 56px;
  margin-inline-start: auto; margin-inline-end: auto;
  display: grid; place-items: center;
  border-radius: var(--r-avatar);
  background: var(--green-200);
  color: var(--brand-strong);
  font-weight: var(--h-weight-strong); font-size: var(--fs-lg);
  overflow: hidden;
}
.person__avatar img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.person__name { font-weight: var(--h-weight); color: var(--brand-strong); font-size: var(--fs-base); line-height: 1.6; }
.person__role { font-size: var(--fs-sm); color: var(--ink-muted); }
.person .pmark { justify-content: center; }
.person--lead { border-color: var(--brand-decor); background: var(--surface-soft); }

.numbered { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3); }
.numbered > li {
  display: flex; align-items: flex-start;
  padding: var(--s4) var(--s5);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper);
  margin: 0;
  line-height: var(--lh-body);
}
.numbered__n {
  inline-size: 32px; block-size: 32px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-xs);
  background: var(--green-200);
  color: var(--brand-strong);
  font-size: var(--fs-xs); font-weight: var(--h-weight-strong);
  font-variant-numeric: tabular-nums;
}
.numbered > li > * + * { margin-inline-start: var(--s4); }
.numbered__body { flex: 1; min-width: 0; }

/* F5 — partners were twelve pills of ragged width (100px to 420px) centre-justified across
   three lines. Pills are this system's INTERACTIVE FILTER vocabulary (.chip), so twelve
   non-interactive pills read as filters that do nothing. A partner list is EVIDENCE, not a tag
   cloud: uniform cell height, uniform rhythm, twelve names read in one pass.
   The rule that this enforces: a pill is either a .badge (label, no hover) or a .chip (control,
   hover + pressed). There is no third pill. */
.partners {
  display: grid; gap: var(--bd);
  background: var(--line);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
  list-style: none; margin: 0; padding: 0;
}
@media (min-width: 640px)  { .partners { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .partners { grid-template-columns: repeat(3, 1fr); } }
.partner {
  margin: 0;
  background: var(--paper);
  min-height: 84px;
  padding: var(--s4) var(--s5);
  display: flex; align-items: center;
}
.partner > * + * { margin-inline-start: var(--s4); }
.partner__mark {
  inline-size: 48px; block-size: 48px; flex: none;
  border-radius: var(--r-xs);
  background: var(--surface-soft);
  color: var(--brand-decor);
  display: grid; place-items: center;
  overflow: hidden;
}
.partner__mark img { inline-size: 100%; block-size: 100%; object-fit: contain; }
.partner__name { font-size: var(--fs-sm); line-height: 1.6; color: var(--ink); min-width: 0; }
.partner__name a { color: var(--brand-strong); text-decoration: none; }
.partner__name a:hover { text-decoration: underline; }
.band--deep .partners { background: var(--line-deep); border-color: var(--line-deep); }
.band--deep .partner { background: transparent; }
.band--deep .partner__name { color: var(--on-deep); }

/* Definition list used as a small data panel — request status, branch details. */
.datalist {
  display: grid; gap: 6px; margin: 0;
  padding: var(--s4);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-soft);
}
.datalist dt { font-size: var(--fs-xs); color: var(--ink-muted); }
.datalist dt + dd { margin-bottom: var(--s2); }
.datalist dd { margin: 0; font-weight: var(--h-weight); color: var(--brand-strong); }
.datalist dd:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------- copy field ---- */

.copyfield {
  display: flex; flex-wrap: wrap; align-items: center;
  padding: var(--s4);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-soft);
}
.copyfield__value {
  flex: 1 1 260px;
  font-size: var(--fs-md);
  font-weight: var(--h-weight);
  color: var(--brand-strong);
  /* Same fix as .footer__bank dd: LTR ORDERING, not left ALIGNMENT. `text-align: left` pushed
     the IBAN to the far left of an RTL row, away from the label above it and the copy button
     beside it. `unicode-bidi: isolate` is what actually keeps the digits in order. */
  unicode-bidi: isolate;
  text-align: start;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
  user-select: all;
}
.copyfield > * { margin-inline-end: var(--s3); margin-block-end: var(--s2); }
.copyfield > *:last-child { margin-inline-end: 0; }
.copyfield__label { font-size: var(--fs-xs); color: var(--ink-muted); flex-basis: 100%; }

/* ------------------------------------------------------------ feedback ---- */

/* The flat `background-color` is declared SEPARATELY from the gradient on purpose: it is what
   the block degrades to under reduced motion and on any browser that drops the animation. A
   skeleton that is a generic grey rectangle is worse than a spinner, because it promises a shape
   it does not deliver — these mirror the real row. */
.skeleton {
  display: block;
  background-color: var(--skeleton-a);
  background-image: linear-gradient(90deg, var(--skeleton-a) 25%, var(--skeleton-b) 37%, var(--skeleton-a) 63%);
  background-size: 400% 100%;
  border-radius: var(--r-sm);
  min-height: 1em;
}
.skeleton--text  { block-size: 1em; margin-block-end: var(--s2); }
.skeleton--text:last-child { inline-size: 62%; }   /* a ragged last line reads as text */
.skeleton--title { block-size: 1.4em; inline-size: 55%; margin-block-end: var(--s4); }
.skeleton--media { block-size: 168px; border-radius: var(--r-media); }
@media (prefers-reduced-motion: no-preference) {
  .skeleton { animation: skel 1.4s ease-in-out infinite; }
  @keyframes skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
}

.empty {
  padding: var(--s8) var(--s5);
  text-align: center;
  border: var(--bd) dashed var(--line);
  border-radius: var(--r-card);
  background: var(--surface-soft);
  color: var(--ink-muted);
}

/* ------------------------------------------------------- page-level state -- */
/* F14 — «استقبال الطلبات مغلق» is the D10 module's most important screen and it had no design:
   one gold .alert floating in an otherwise empty white band. .empty (the dashed box above) is
   right for a filtered list that returned nothing; it is not enough for a page whose whole
   purpose is currently unavailable. Also used by /requests/track not-found and by 404. */
.state {
  max-inline-size: 560px;
  margin-inline-start: auto; margin-inline-end: auto;
  padding-block-start: var(--s8); padding-block-end: var(--s8);
  text-align: center;
}
.state__icon {
  inline-size: 72px; block-size: 72px;
  margin-inline-start: auto; margin-inline-end: auto; margin-block-end: var(--s5);
  display: grid; place-items: center;
  border-radius: var(--r-md);
  background: var(--green-200);
  color: var(--brand-strong);
}
.state h1, .state h2 { font-size: var(--fs-xl); margin-block-end: var(--s3); }
/* The ONE place in this system where a paragraph is centred, and it is bounded: 44ch and at most
   two short lines. Anything longer goes in a start-aligned .prose block below it. */
.state p {
  color: var(--ink-muted);
  font-size: var(--fs-md); line-height: var(--lh-body);
  text-align: center;
  max-inline-size: 44ch;
  margin-inline-start: auto; margin-inline-end: auto;
}
/* Inside a band the band already supplies the vertical rhythm; --s8 on top of --band-y is
   152px of nothing above the icon. */
.band .state { padding-block-start: var(--s5); }
.state .btn-row { justify-content: center; margin-block-start: var(--s6); }
.state .datalist { text-align: start; margin-block-start: var(--s6); }

/* -------------------------------------------------------------- header ---- */

.topbar { background: var(--surface-deep); color: var(--on-deep-muted); font-size: var(--fs-xs); }
.topbar .wrap {
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: space-between; min-height: 38px;
}
.topbar .wrap > * { margin-inline-end: var(--s5); margin-block-end: var(--s2); margin-block-start: var(--s2); }
.topbar .wrap > *:last-child { margin-inline-end: 0; }
.topbar__group { display: flex; flex-wrap: wrap; align-items: center; }
.topbar__group > * { margin-inline-end: var(--s4); }
.topbar__group > *:last-child { margin-inline-end: 0; }
.topbar a { color: var(--on-deep); text-decoration: none; display: inline-flex; align-items: center; }
.topbar a > * + * { margin-inline-start: 5px; }
.topbar a:hover { text-decoration: underline; color: var(--on-deep); }
@media (max-width: 640px) { .topbar__group--secondary { display: none; } }

.header {
  position: sticky; top: 0; z-index: 100;
  background: var(--paper);
  border-bottom: var(--bd) solid var(--line);
  transition: box-shadow var(--dur) var(--ease);
}
.header.is-stuck { box-shadow: var(--shadow-pop); }
.header__inner { display: flex; align-items: center; min-height: 72px; }
.header__inner > * + * { margin-inline-start: var(--s4); }
/* .is-stuck may shrink the header; nothing else changes — no colour, no blur, no backdrop. */
.header.is-stuck .header__inner { min-height: 64px; }

/* The wordmark is set in TYPE, not baked into the bitmap: the association has no vector mark
   (Docs/07 OQ-2) and the only original anywhere in this repo is an 84×88 PNG, which would turn
   to mush at any size that could carry a name.
   The served logo.png is now a 252×264 master resampled from that original (Lanczos, premultiplied
   alpha, light unsharp) — it adds no detail that was not there, but it means the browser is
   DOWNSCALING to the display size on every device instead of upscaling on a 2×/3× phone, which is
   what made the mark look soft. At 50px CSS the mark is still well inside its source resolution
   at 3× DPR. The real fix is still a vector from the owner. */
.brand { display: flex; align-items: center; text-decoration: none; flex: none; }
.brand > * + * { margin-inline-start: var(--s3); }
.brand__mark {
  inline-size: 60px; block-size: 60px; flex: none;
  display: grid; place-items: center;
  background: var(--surface-soft);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.brand__mark img { inline-size: 50px; block-size: 52px; object-fit: contain; }
.brand__text { display: grid; line-height: 1.25; }
.brand__name { font-weight: var(--h-weight-strong); color: var(--brand-strong); font-size: clamp(.95rem, .9rem + .3vw, 1.125rem); }
.brand__sub { font-size: var(--fs-xs); color: var(--ink-muted); font-weight: 600; }

.nav { display: none; margin-inline-start: auto; }
@media (min-width: 1060px) { .nav { display: flex; align-items: center; } }
.nav > * + * { margin-inline-start: var(--s0); }
.nav > * { position: relative; }
.nav a, .nav summary {
  display: inline-flex; align-items: center;
  min-height: var(--btn-min-h); padding: 0 var(--s3);
  border-radius: var(--r-sm);
  color: var(--brand-strong);
  font-size: var(--fs-sm); font-weight: 600;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav a > svg, .nav summary > svg { margin-inline-start: 5px; }
.nav a:hover, .nav summary:hover { background: var(--surface-soft); color: var(--brand); }
.nav a[aria-current='page'] { color: var(--brand); background: var(--surface-soft); }
.nav a[aria-current='page']::after {
  content: ''; position: absolute; right: var(--s3); left: var(--s3); bottom: var(--s0);
  block-size: 2px; background: var(--accent); border-radius: var(--r-xs);
}

/* Dropdown on <details>: opens with zero JavaScript; the island only adds Escape
   and click-outside. A menu that needs a bundle to open is a menu that fails on a
   throttled connection. */
.nav details > summary { list-style: none; }
.nav details > summary::-webkit-details-marker { display: none; }
.nav details[open] > summary { background: var(--surface-soft); color: var(--brand); }
.nav__panel {
  position: absolute; top: 100%; right: 0; left: auto; z-index: var(--z-dropdown);
  /* The panel used to open flush against the summary and land over the page <h1>. */
  margin-block-start: var(--s2);
  min-inline-size: 240px;
  display: grid; gap: 1px;
  padding: var(--s2);
  background: var(--paper);
  border: var(--bd) solid var(--line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
}
[dir='ltr'] .nav__panel { right: auto; left: 0; }
.nav__panel a { min-height: 40px; width: 100%; justify-content: flex-start; }

.header__cta { display: none; margin-inline-start: var(--s2); }
.header__cta > * + * { margin-inline-start: var(--s2); }
@media (min-width: 1060px) { .header__cta { display: flex; } }

/* F9 — below 1060px the header used to drop BOTH calls to action and leave only the burger, so
   on the device most visitors use, طلب مساعدة went from one tap to three. One action survives. */
.header__cta-mobile { display: flex; margin-inline-start: auto; }
@media (min-width: 1060px) { .header__cta-mobile { display: none; } }
/* Below 360px the control keeps its 44px target and drops its LABEL, not itself. */
@media (max-width: 360px) {
  .header__cta-mobile .btn { padding-inline-start: var(--s3); padding-inline-end: var(--s3); }
  .header__cta-mobile .btn > span { display: none; }
  .header__cta-mobile .btn > svg:first-child:not(:last-child) { margin-inline-end: 0; }
}

.burger {
  margin-inline-start: var(--s2);
  min-inline-size: var(--btn-min-h); min-block-size: var(--btn-min-h);
  display: grid; place-items: center;
  border: var(--bd) solid var(--line);
  border-radius: var(--btn-radius);
  background: var(--paper);
  color: var(--brand-strong);
  cursor: pointer;
}
.burger:hover { background: var(--surface-soft); border-color: var(--brand-decor); }
@media (min-width: 1060px) { .burger { display: none; } }

/* Mobile drawer. Enters from the inline-end edge, which in RTL is the right. */
.drawer { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: var(--z-drawer); }
.drawer__scrim {
  position: absolute; top: 0; right: 0; bottom: 0; left: 0;
  background: var(--scrim); border: 0; padding: 0; width: 100%;
  z-index: var(--z-scrim);
}
/* R3 — ASYMMETRIC, so it gets the physical default plus the [dir='ltr'] pair. The RTL branch is
   the default because <html dir="rtl"> and v1 has no /en route (D6); the LTR branch is insurance
   for a D14 fork. Four lines, and without them the panel does not sit against the edge on 81. */
.drawer__panel {
  position: absolute; top: 0; bottom: 0; right: 0; left: auto;
  /* ABOVE the scrim. The scrim carries --z-scrim and the panel had no z-index (auto = 0), so the
     scrim painted OVER the panel: the menu looked dimmed AND every tap on a link hit the scrim
     (data-drawer-close) and shut the drawer instead of navigating — the mobile "can't navigate"
     bug. The panel must sit on top of the sheet that dims the page behind it. */
  z-index: calc(var(--z-scrim) + 1);
  inline-size: min(340px, 88vw);
  background: var(--paper);
  border-inline-start: var(--bd) solid var(--line);
  box-shadow: var(--shadow-pop);
  display: flex; flex-direction: column;
  overflow-y: auto;
}
[dir='ltr'] .drawer__panel { right: auto; left: 0; }
@media (prefers-reduced-motion: no-preference) {
  .drawer__panel { animation: drawer-in var(--dur-slow) var(--ease); }
  /* translateX(100%) is physical, and in an RTL document the panel's own edge is on
     the right — so the offscreen start position is +100%, not -100%. */
  @keyframes drawer-in { from { transform: translateX(100%); } to { transform: none; } }
}
.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s4); border-bottom: var(--bd) solid var(--line);
}
.drawer__head > * + * { margin-inline-start: var(--s3); }
.drawer__nav { padding: var(--s3); display: grid; gap: 2px; }
.drawer__nav a {
  display: flex; align-items: center; min-height: 46px;
  padding: 0 var(--s3);
  border-radius: var(--r-sm);
  color: var(--brand-strong); font-weight: 600; font-size: var(--fs-sm);
  text-decoration: none;
}
.drawer__nav a:hover { background: var(--surface-soft); }
.drawer__nav a[aria-current='page'] { background: var(--surface-soft); color: var(--brand); }
.drawer__group { padding: var(--s4) var(--s3) var(--s2); font-size: var(--fs-xs); font-weight: var(--h-weight-strong); color: var(--ink-muted); }
.drawer__foot { margin-top: auto; padding: var(--s4); border-top: var(--bd) solid var(--line); display: grid; gap: var(--s2); }

.iconbtn {
  min-inline-size: var(--btn-min-h); min-block-size: var(--btn-min-h);
  display: grid; place-items: center;
  border: var(--bd) solid var(--line); border-radius: var(--btn-radius);
  background: var(--paper); color: var(--brand-strong); cursor: pointer;
}
.iconbtn:hover { background: var(--surface-soft); }

/* Static no-JS menu. The header island removes it the moment the drawer works, so
   nobody ever sees both. */
.nojs-menu { border-bottom: var(--bd) solid var(--line); background: var(--surface-soft); }
.nojs-menu .wrap {
  display: flex; flex-wrap: wrap;
  padding-block-start: var(--s3); padding-block-end: var(--s3);
}
.nojs-menu .wrap > * { margin-inline-end: var(--s4); margin-block-end: var(--s2); }
.nojs-menu a { font-size: var(--fs-sm); font-weight: 600; color: var(--brand-strong); text-decoration: none; }
.nojs-menu a:hover { text-decoration: underline; }
@media (min-width: 1060px) { .nojs-menu { display: none; } }

/* -------------------------------------------------------------- footer ---- */

.footer { background: var(--surface-deep); color: var(--on-deep-muted); font-size: var(--fs-sm); }
/* Tightened 2026-09-04 (owner: the footer was too tall). --s8 top was hero-band spacing on a
   block that is a shortcut list, not a section. The link trim above did the bulk of the work;
   this takes the remaining slack off the top and out of the row rhythm below. */
.footer .wrap { padding-block-start: var(--s6); padding-block-end: var(--s4); }
.footer h4 { color: var(--on-deep); font-size: var(--fs-base); margin-bottom: var(--s3); }
.footer a { color: var(--on-deep-muted); text-decoration: none; }
.footer a:hover { color: var(--on-deep); text-decoration: underline; }
.footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.footer li { margin: 0; line-height: 1.8; }
.footer__grid { display: grid; gap: var(--s6); grid-template-columns: 1fr; }
@media (min-width: 620px) { .footer__grid { grid-template-columns: repeat(2, 1fr); } }
/* Rebalanced. The bank block used to live inside the brand column and made it far taller than
   the three link columns beside it, which run out at five or six items — so the bottom-left of
   the footer was a large void. The bank is a full-width strip below the grid now, and column one
   is widened so the regulator / supervising-authority lines stop wrapping to an orphan word. */
@media (min-width: 1000px) { .footer__grid { grid-template-columns: 1.7fr 1fr 1fr 1fr; } }
/* The brand column is the tallest, so it alone sets the grid's height and the whitespace under the
   shorter link columns. Tightening its internal rhythm (s4 → s3) shortens the whole footer band. */
.footer__brand { display: grid; gap: var(--s3); align-content: start; }
.footer__lockup { display: flex; align-items: center; }
.footer__lockup > * + * { margin-inline-start: var(--s3); }
.footer__lockup img { inline-size: 64px; block-size: 67px; object-fit: contain; }
.footer__lockup b { color: var(--on-deep); font-size: var(--fs-md); display: block; line-height: 1.3; }
.footer__lockup span { font-size: var(--fs-xs); color: var(--on-deep-faint); }
.footer__about { line-height: var(--lh-body); color: var(--on-deep-muted); margin: 0; max-width: 46ch; }

/* Label over value. These were single ~50-character strings and wrapped to a one-word orphan
   line («…لتنمية القطاع غير / الربحي»); split, each value fits its column and the block gains a
   hierarchy it did not have.
   CONTRAST, MEASURED (values live in theme.css; only the ratios are quoted here, because this
   file may not name a colour): --on-deep-faint composited over --surface-deep measures 5.37:1,
   so it passes AA on the green ground; --on-deep-muted measures 7.31:1. --accent would measure
   4.04:1 there and stays barred as text on the dark ground exactly as on the light one. */
.footer__facts { display: grid; gap: var(--s3); margin: 0; }
.footer__facts dt { font-size: var(--fs-xs); color: var(--on-deep-faint); margin-block-end: var(--s0); }
.footer__facts dd { margin: 0; color: var(--on-deep-muted); line-height: 1.6; max-width: 42ch; }

/* The bank strip. Full width and RULED, not a bordered card: as the only box in the whole
   footer, stranded at the top of the far column, it read as a foreign object pasted in. */
.footer__bank {
  margin-top: var(--s4); padding-top: var(--s4);
  border-top: var(--bd) solid var(--line-deep);
  display: flex; flex-wrap: wrap; align-items: flex-end;
}
.footer__bank > * { margin-inline-end: var(--s6); margin-block-end: var(--s3); }
.footer__bank-facts { display: flex; flex-wrap: wrap; margin: 0; }
.footer__bank-facts > * { margin-inline-end: var(--s7); margin-block-end: var(--s3); }
.footer__bank-facts > *:last-child { margin-inline-end: 0; }
/* The copy control sits directly beside the value it copies and on its baseline — not below it
   and not at the far end of the row. */
.footer__bank-actions { display: flex; flex-wrap: wrap; align-items: center; }
.footer__bank-actions > * { margin-inline-end: var(--s2); }
.footer__bank dt { font-size: var(--fs-xs); color: var(--on-deep-faint); margin-block-end: var(--s1); }
.footer__bank dd {
  margin: 0;
  color: var(--on-deep);
  /* An account number is REFERENCE DATA, not a headline. At --h-weight in the brightest
     --on-deep it was the loudest thing in the footer — louder than the association's own name. */
  font-size: var(--fs-sm); font-weight: 600; letter-spacing: .02em;
  /* The digits need LTR ORDERING, not left ALIGNMENT. Those are different things, and
     conflating them is what stranded the value at the far LEFT edge of an RTL column while its
     label stayed at the right edge, visually orphaned from it. A Latin/digit run is already
     strong-LTR under the bidi algorithm, so `unicode-bidi: isolate` alone orders it correctly;
     `text-align: start` then puts it at the inline-start edge, directly under its own label. */
  unicode-bidi: isolate;
  text-align: start;
  font-variant-numeric: tabular-nums;
  /* Not `break-all`: a 24-character IBAN chopped mid-run is hard to read back and hard to check
     against a bank app. `anywhere` breaks ONLY when the line genuinely cannot fit. */
  overflow-wrap: anywhere;
}

.footer__legal {
  margin-top: var(--s4); padding-top: var(--s4);
  border-top: var(--bd) solid var(--line-deep);
  display: grid; gap: var(--s3);
  font-size: var(--fs-xs); color: var(--on-deep-faint);
}
/* The three registry facts used to be crushed onto one line opposite the nav links AND the
   copyright. They are the licence: they get their own row. */
.footer__legal-registry { display: flex; flex-wrap: wrap; }
.footer__legal-registry > * { margin-inline-end: var(--s5); margin-block-end: var(--s2); }
.footer__legal-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.footer__legal-row > * { margin-inline-end: var(--s5); margin-block-end: var(--s2); }
.footer__legal-row > *:last-child { margin-inline-end: 0; }
.footer__legal-links { display: flex; flex-wrap: wrap; }
.footer__legal-links > * { margin-inline-end: var(--s5); }
.footer__legal b { color: var(--on-deep); }
.footer__social { display: flex; flex-wrap: wrap; }
.footer__social > * { margin-inline-end: var(--s2); margin-block-end: var(--s2); }
.footer__social a {
  inline-size: var(--btn-min-h); block-size: var(--btn-min-h);
  display: grid; place-items: center;
  border: var(--bd) solid var(--line-deep); border-radius: var(--r-sm);
  color: var(--on-deep);
}
.footer__social a:hover { background: var(--deep-fill-strong); }

/* ------------------------------------------------------------ entrance ---- */
/* Applied only after the island confirms IntersectionObserver exists — a page whose
   script fails must never be left with invisible sections. */
@media (prefers-reduced-motion: no-preference) {
  .reveal-ready .reveal { opacity: 0; transform: translateY(14px); }
  .reveal-ready .reveal.is-in {
    opacity: 1; transform: none;
    transition: opacity var(--dur-enter) var(--ease), transform var(--dur-enter) var(--ease);
  }
}

/* ------------------------------------------------------------- utility ---- */

.flow-gap { display: flex; flex-wrap: wrap; align-items: center; }
.flow-gap > * { margin-inline-end: var(--s3); margin-block-end: var(--s2); }
.flow-gap > *:last-child { margin-inline-end: 0; }
.items-center { align-items: center; }
.figure {
  width: 100%;
  border-radius: var(--r-card);
  border: var(--bd) solid var(--card-border-color);
}
.portrait {
  inline-size: 96px; block-size: 96px;
  border-radius: var(--r-pill); object-fit: cover;
  border: var(--bd) solid var(--line);
}
.between { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; }
.between > * { margin-inline-end: var(--s4); margin-block-end: var(--s3); }
.between > *:last-child { margin-inline-end: 0; }
.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.mt-4 { margin-top: var(--s4); }
.mb-5 { margin-bottom: var(--s5); }
.mt-6 { margin-top: var(--s6); }
.nowrap { white-space: nowrap; }

@media print {
  .topbar, .header, .footer, .drawer, .nojs-menu, .btn, .pager, .filterbar, .skip { display: none !important; }
  .band { padding-block-start: 8pt; padding-block-end: 8pt; }
  a[href]::after { content: ' (' attr(href) ')'; font-size: 9pt; }
}
