/* Ilham — bilingual authority site mockup. ONE stylesheet for both render
   states: logical properties (margin/padding-inline, text-align start/end) mean
   the same rules mirror correctly under dir=ltr and dir=rtl. Direction-specific
   font stacks are set by the [lang] attribute. Brand palette per spec §3. */

:root {
  --linen:  #ECE4D7;  /* default background everywhere */
  --navy:   #263247;  /* body text + light-bg headers (never a fill) */
  --gold:   #B8915A;  /* swallow, dividers, dark-bg headers, accents */
  --sage:   #7D8C6A;  /* subheadings / labels */
  --copper: #986448;  /* secondary warmth, sparing */
  --sand:   #D9D0C3;  /* borders / dividers */
  --white:  #ffffff;  /* high-contrast only */
  --serif: "Cormorant Garamond", Georgia, serif;
  --sans:  "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* Arabic render swaps both heading + body to Noto Naskh Arabic (Cormorant/Inter
   carry no Arabic glyphs; the guide names no separate AR body face). */
:root[lang="ar"] {
  --serif: "Noto Naskh Arabic", "Cormorant Garamond", serif;
  --sans:  "Noto Naskh Arabic", "Inter", sans-serif;
}

* { box-sizing: border-box; }
/* Base type scale bumped up 2026-07-08 (Steve: "make the fonts bigger again").
   rem-based sizes below scale from this; applies to BOTH EN + AR (shared sheet). */
html { font-size: 118.75%; scroll-behavior: smooth; } /* 19px base (from 16px); smooth anchor scroll to #enquire */
html, body { margin: 0; padding: 0; }
body {
  background: var(--linen);
  color: var(--navy);
  font-family: var(--sans);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }

/* keep phone/email/dates LTR even inside an RTL page (spec §2) */
.ltr-lock { direction: ltr; unicode-bidi: isolate; }

h1, h2 { font-family: var(--serif); font-weight: 600; color: var(--navy); line-height: 1.1; margin: 0; }
/* Section labels (WHAT ILHAM IS / WAYS TO WORK TOGETHER / ABOUT DR HIBA / ENQUIRE).
   Up-sized 2026-07-11 (Steve: they need "beefing up a notch"), referenced to the
   DR HIBA KHALED hero eyebrow he approved — it is the same h3 label family, so the
   set now reads as ONE typographic system with the hero eyebrow at the top of it,
   rather than a big name and a row of small unrelated ticks. */
h3 { font-family: var(--sans); font-size: clamp(.92rem, 1.3vw, 1.2rem); letter-spacing: .18em; text-transform: uppercase; color: var(--sage); font-weight: 700; margin: 0 0 1rem; }
p { margin: 0 0 1rem; }
a { color: inherit; }

.wrap { max-width: 1080px; margin-inline: auto; padding-inline: clamp(1.25rem, 5vw, 3rem); }
section { padding-block: clamp(3rem, 8vw, 6rem); }

/* pending-translation banner (AR render) */
.pending-note {
  background: var(--copper); color: var(--white);
  text-align: center; font-family: var(--sans); font-size: .8rem;
  letter-spacing: .04em; padding: .5rem 1rem;
}

/* ── nav ── (logo much bigger per Steve 2026-07-08) */
.nav { border-block-end: 1px solid var(--sand); background: var(--linen); }
/* Logo is the brand centrepiece: centred + large in a taller header band, with
   the language toggle floated to the trailing corner (Steve: it read as missing
   when small in the corner). inset-inline-end mirrors right/left for LTR/RTL. */
.nav .wrap { position: relative; display: flex; align-items: center; justify-content: center; padding-block: clamp(1.5rem, 4vw, 2.5rem); }
.brand { display: flex; align-items: center; gap: 1rem; text-decoration: none; }
.lang { position: absolute; inset-inline-end: clamp(1.25rem, 5vw, 3rem); top: 50%; transform: translateY(-50%); }
/* Real logo lockup (Design-png.png) as the displayed site logo — prominent. */
.brand .logo-img { height: 150px; width: auto; flex: none; display: block; }
/* Explicit ILHAM text wordmark beside the mark (Steve: brand name must be explicit). */
.brand .brand-label { font-family: var(--serif); font-size: clamp(2.6rem, 5vw, 3.6rem); letter-spacing: .22em; color: var(--navy); line-height: 1; font-weight: 600; }
:root[lang="ar"] .brand .brand-label { letter-spacing: 0; font-size: clamp(3rem, 6vw, 4rem); }
@media (max-width: 600px) { .brand .brand-label { font-size: 2rem; letter-spacing: .18em; } :root[lang="ar"] .brand .brand-label { font-size: 2.4rem; } }
/* SVG + text wordmark are the FALLBACK (shown only if the img fails). */
.brand .mark { width: 76px; height: 76px; flex: none; }
.brand .word { font-family: var(--serif); font-size: 2.6rem; letter-spacing: .2em; color: var(--navy); line-height: 1; }
:root[lang="ar"] .brand .word { letter-spacing: 0; font-size: 3rem; }
@media (max-width: 600px) {
  /* Stack the header so the centred logo+wordmark never collide with the toggle. */
  .nav .wrap { flex-direction: column; gap: .6rem; padding-block: 1.25rem; }
  .lang { position: static; transform: none; order: -1; align-self: flex-end; }
  .brand .logo-img { height: 96px; }
  .brand .mark { width: 56px; height: 56px; }
  .brand .word { font-size: 2rem; }
}

/* ── swallow brand device: gold rule + centred swallow, per colour rules ── */
.swallow-divider { display: flex; align-items: center; gap: 1rem; margin-block: clamp(2rem, 6vw, 4rem); }
.swallow-divider::before, .swallow-divider::after { content: ""; height: 1px; background: var(--gold); flex: 1; opacity: .55; }
.swallow-divider svg { width: 34px; height: 34px; flex: none; }
.section-mark { width: 30px; height: 30px; margin-block-end: 1rem; }

/* ── pull quote (the one-line description) — Cormorant italic gold ── */
.pullquote { text-align: center; max-width: 24ch; margin-inline: auto; margin-block: clamp(1rem,4vw,2rem); }
.pullquote p { font-family: var(--serif); font-style: italic; font-size: clamp(1.6rem, 4vw, 2.4rem); line-height: 1.25; color: var(--gold); margin: 0; }
/* ── testimonial — a pull quote, not a testimonials section ───────────────
   Per the design approach doc: a testimonial IS a pull quote, so it reuses the
   pattern the brand guide already defines rather than introducing a review
   component. No section header, no carousel, no grid, no photo, no quote-mark
   glyph — the brand guide sanctions only Dr Hiba's own photography, and a
   curly-quote icon would be a new visual element.
   COPPER, not gold: the hero tagline owns gold, and two gold pull quotes on one
   page would compete. Copper is the guide's own "where gold would be too
   bright" note.
   Attribution reads as a CITATION, not a marketing byline — so it borrows the
   existing h3/label treatment (Inter, uppercase, letterspaced, sage) already
   used for eyebrows elsewhere on the site. */
.testimonial { padding-block: clamp(2rem, 6vw, 4rem); }
.testimonial blockquote { margin: 0; text-align: center; max-width: 46ch; margin-inline: auto; }
.testimonial blockquote p {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem); line-height: 1.35;
  color: var(--copper); margin: 0;
}
.testimonial cite {
  display: block; margin-block-start: 1.25rem;
  font-family: var(--sans); font-style: normal;
  font-size: .78rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--sage);
}

/* PLACEHOLDER STATE — dev/review only, never ships to production.
   Deliberately loud: dashed rule + a flag line, so nobody reviewing the page
   can mistake fabricated copy for a real client testimonial. Removing the
   `is-placeholder` class and the .testimonial-flag element is the entire
   cleanup when real, consented quotes arrive. */
.testimonial.is-placeholder blockquote {
  border: 1px dashed var(--copper); border-radius: 4px;
  padding: clamp(1.25rem, 3vw, 2rem); opacity: .92;
}
.testimonial-flag {
  font-family: var(--sans); font-size: .68rem; letter-spacing: .2em;
  text-transform: uppercase; text-align: center; color: var(--copper);
  margin: 0 0 .75rem; font-weight: 700;
}

/* CONSENT-PENDING STATE — distinct from PLACEHOLDER above. This is REAL
   content (the subject's actual words, verified), not fabricated demo copy —
   so it must NOT read as fake. It is withheld from PUBLIC builds only
   because the subject has not yet been asked for commercial-use consent
   (public self-publication is not that consent). Same loud dashed treatment
   so reviewers can't miss the state, different label so nobody reads it as
   "not real." Removing the `consent-pending` class and .testimonial-consent-
   flag element happens ONLY after the subject has said yes — see the note
   on the block itself for who to ask. */
.testimonial.consent-pending blockquote {
  border: 1px dashed var(--sage); border-radius: 4px;
  padding: clamp(1.25rem, 3vw, 2rem); opacity: .96;
}
.testimonial-consent-flag {
  font-family: var(--sans); font-size: .68rem; letter-spacing: .2em;
  text-transform: uppercase; text-align: center; color: var(--sage);
  margin: 0 0 .75rem; font-weight: 700;
}

.lang { font-family: var(--sans); font-size: .82rem; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; color: var(--sage); border: 1px solid var(--sand); border-radius: 999px; padding: .35rem .9rem; }
.lang b { color: var(--navy); }

/* ── hero ── */
.hero .wrap { display: grid; grid-template-columns: 1.1fr .9fr; gap: clamp(1.5rem, 5vw, 4rem); align-items: center; }
.hero-copy .eyebrow { color: var(--sage); }
.hero h1 { font-size: clamp(2.4rem, 6vw, 4rem); margin-block-end: 1.25rem; }
.hero .tagline { font-family: var(--serif); font-style: italic; font-size: clamp(1.4rem, 3vw, 2rem); color: var(--gold); margin-block-end: 2rem; }
.hero-photo img { width: 100%; aspect-ratio: 3/4; object-fit: cover; object-position: center top; border-radius: 4px; }
/* Mobile: photo BELOW the copy+CTA (Steve: photo-first is too upfront). Desktop unchanged. */
@media (max-width: 760px) { .hero .wrap { grid-template-columns: 1fr; } .hero-copy { order: 1; } .hero-photo { order: 2; } }

/* buttons */
.btn { display: inline-block; background: var(--navy); color: var(--white); font-family: var(--sans); font-size: .95rem; letter-spacing: .03em; text-decoration: none; padding: .9rem 1.6rem; border-radius: 3px; border: none; cursor: pointer; }
.btn.gold { background: var(--gold); }

/* divider */
.rule { height: 1px; background: var(--sand); border: 0; margin: 0; }
.rule.gold { height: 2px; background: var(--gold); width: 64px; margin-block: 0 1.5rem; }

/* ── what-ilham (prose) ── */
.prose { max-width: 60ch; font-size: 1.12rem; }
.prose.lead p:first-child { font-family: var(--serif); font-size: 1.5rem; font-style: italic; color: var(--copper); }

/* STAT LINE — deliberately NOT the .testimonial/.prose.lead serif-italic-
   copper treatment. That treatment is this site's pull-quote signature; a
   verified statistic wearing it reads as an unattributed testimonial (Steve
   caught this 2026-07-30 on families-youth.html). Sans-serif, upright, navy,
   with an eyebrow label — parses as data, not as someone speaking. */
.stat-eyebrow { font-family: var(--sans); font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--sage); font-weight: 700; margin: 0 0 .5rem; display: block; }
.stat-line { font-family: var(--sans); font-style: normal; font-size: 1.05rem; color: var(--navy); margin: 0; }

/* ── ways cards ── */
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.25rem; margin-block-start: 2.5rem; }
@media (max-width: 820px) { .cards { grid-template-columns: 1fr; } }
.card { background: var(--white); border: 1px solid var(--sand); border-radius: 4px; padding: 1.75rem; border-block-start: 3px solid var(--gold); }
.card h4 { font-family: var(--serif); font-size: 1.35rem; font-weight: 600; color: var(--navy); margin: 0 0 .6rem; }
.card p { font-size: .98rem; margin: 0; color: var(--navy); }
/* paused 4th slot — visually deferred, re-enablable (spec §1.4) */
.card.paused { background: transparent; border-style: dashed; border-block-start-color: var(--sand); display: flex; align-items: center; justify-content: center; text-align: center; opacity: .7; }
.card.paused .tag { font-family: var(--sans); font-size: .72rem; letter-spacing: .12em; text-transform: uppercase; color: var(--sage); }

/* ── about ── */
.about .wrap { display: grid; grid-template-columns: .8fr 1.2fr; gap: clamp(1.5rem, 5vw, 4rem); align-items: center; }
.about-photo img { width: 100%; aspect-ratio: 3/4; object-fit: cover; object-position: center top; border-radius: 4px; }
@media (max-width: 760px) { .about .wrap { grid-template-columns: 1fr; } }

/* ── enquire (dark section) ── */
.enquire { background: var(--sage); color: var(--white); }
.enquire h2, .enquire h3 { color: var(--white); }
.enquire h3 { color: rgba(255,255,255,.85); }
/* THE ENQUIRE ACCENT — Steve asked whether its absence was a miss or deliberate.
   MEASURED: it was a MISS IN THE MARKUP (the <hr> was simply never written into the
   Enquire section); nothing in CSS was suppressing it. But the naive fix — copy the
   gold rule across — would have been a valid-looking success against the wrong
   object: gold #B8915A on sage #7D8C6A measures 1.24:1, effectively INVISIBLE. It
   would have shipped looking fixed and been unreadable.
   White measures 3.60:1 and passes WCAG 1.4.11 (3:1, non-text). So the rule is
   restored, and its COLOUR adapts to the ground it sits on — which is also why the
   markup stays uniform (`rule gold` = the 2px accent RULE, not a literal colour).
   Linen was the other candidate at 2.85:1 — marginal, so not chosen. Steve's call. */
/* THE SECTION-HEADER ACCENT IS PART OF THE COMPONENT, not markup each page must
   remember. It used to be a hand-placed <hr class="rule gold"> written into every
   section by hand — which is precisely HOW ENQUIRE LOST IT (someone wrote the
   section and did not write the rule), and why the sub-pages drifted from the
   homepage. Steve caught the symptom on growth-lab/aspiring-leaders; the cause was
   that a shared pattern was being reproduced by hand five times.
   Now: write <h3>, get the accent. It cannot drift, and a new page inherits it. */
h3:not(.eyebrow)::before {
  content: ""; display: block;
  width: 64px; height: 2px; background: var(--gold);
  margin-block-end: 1.5rem;
}
/* On the green Enquire section, gold measures 1.24:1 — effectively invisible (see
   the measured answer above). White measures 3.60:1 and passes WCAG 1.4.11. The
   accent adapts its colour to the ground it sits on; the COMPONENT is the same. */
.enquire h3:not(.eyebrow)::before { background: var(--white); }
.enquire .lead-line { font-family: var(--serif); font-size: 1.5rem; font-style: italic; margin-block-end: 2rem; max-width: 44ch; }
.form { max-width: 520px; display: grid; gap: 1rem; }
.form label { display: block; font-family: var(--sans); font-size: .82rem; letter-spacing: .08em; text-transform: uppercase; margin-block-end: .35rem; color: rgba(255,255,255,.85); }
.form input, .form textarea { width: 100%; padding: .8rem .9rem; border: 1px solid rgba(255,255,255,.4); background: rgba(255,255,255,.08); color: var(--white); border-radius: 3px; font-family: var(--sans); font-size: 1rem; text-align: start; }
.form input::placeholder, .form textarea::placeholder { color: rgba(255,255,255,.55); }
.form .stub-note { font-family: var(--sans); font-size: .78rem; color: rgba(255,255,255,.7); }

/* ── footer ── */
.footer { background: var(--navy); color: var(--linen); }
.footer .wrap { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem; padding-block: 2.5rem; font-family: var(--sans); font-size: .88rem; }
.footer a { color: var(--gold); text-decoration: none; }
.footer .cols { display: flex; gap: 1.5rem; align-items: center; flex-wrap: wrap; }

/* utility: let a wrapper's children act as direct flex/grid items of the parent,
   so JS-rendered menu links / cards slot in without an extra layout box. */
.contents { display: contents; }

/* ── subnav: data-driven page menu (rendered from ILHAM_PAGES in pages.js).
   Adding a page is a data change — one array entry — never a layout rebuild. ── */
.subnav { border-block-end: 1px solid var(--sand); background: var(--linen); }
.subnav .wrap { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: clamp(1rem, 3vw, 2.25rem); padding-block: .7rem; }
.subnav a { font-family: var(--sans); font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; text-decoration: none; color: var(--sage); padding-block: .2rem; border-block-end: 2px solid transparent; transition: color .15s, border-color .15s; }
.subnav a:hover, .subnav a[aria-current="page"] { color: var(--navy); border-block-end-color: var(--gold); }
:root[lang="ar"] .subnav a { letter-spacing: 0; font-size: .9rem; }

/* ── hero rebalanced to a single centred column (hero photo removed 2026-07-10,
   Steve: too person-centric for a helping-others practice) ── */
.hero .wrap { display: block; text-align: center; }
.hero-copy { max-width: 44ch; margin-inline: auto; }

/* ── linked page-card: the modular info/product-selection card, rendered from
   ILHAM_PAGES into the offerings grid. Same .card language, made navigable. ── */
a.card-link { text-decoration: none; display: flex; flex-direction: column; transition: border-color .15s, transform .15s, box-shadow .15s; }
a.card-link:hover { border-block-start-color: var(--copper); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(38, 50, 71, .08); }
a.card-link .card-more { margin-block-start: auto; padding-block-start: .9rem; font-family: var(--sans); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase; color: var(--gold); }
a.card-link .card-more::after { content: " \2192"; }
[dir="rtl"] a.card-link .card-more::after { content: " \2190"; }

/* ═══════════════════════════════════════════════════════════════════════════
   Hiba revision, 2026-07-11. Central statement + nested Families & Youth hub.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── central statement: the page's ANCHOR, not a pull-quote ──────────────────
   Hiba's direction was explicit — this is the centrepiece the offering cards
   branch FROM, so it must out-weigh the .pullquote treatment it replaces
   (which topped out at 2.4rem in a 24ch column). Near-full-width, and a step
   up in scale, so the hub-and-spoke reads as a hub with spokes rather than as
   a heading with a list under it. Uses the brand type hierarchy per the guide:
   Cormorant, Gold, with the supporting line in Copper italic. */
/* NOT a `ch` max-width. `ch` resolves against THIS element's font-size (~19px
   inherited), not the 65px display type inside it — so a 30ch column came out only
   ~270px wide and broke the statement into a five-line stack. Hiba asked for the
   page's ANCHOR, near-full-width; a narrow ragged column is the opposite of that.
   Absolute bound, so the measure can never silently depend on which element
   resolves `ch`. Verified in the rendered DOM, not in the source. */
.statement { text-align: center; max-width: min(92%, 900px); margin-inline: auto; margin-block: clamp(1rem, 4vw, 2.5rem); }
.statement p { font-family: var(--serif); font-size: clamp(2.1rem, 5.5vw, 3.4rem); line-height: 1.15; color: var(--gold); font-weight: 600; margin: 0; }
.statement .statement-support { font-family: var(--serif); font-style: italic; font-size: clamp(1.15rem, 2.4vw, 1.6rem); color: var(--copper); font-weight: 500; margin-block-start: 1rem; }
:root[lang="ar"] .statement p { line-height: 1.4; }

/* ── ways cards: 5 offerings branching from the statement as EQUAL options ────
   auto-fit keeps them equal-width and reflows 3+2 -> 2+2+1 -> 1 without any
   breakpoint maths, so a 6th card is still a data change, not a layout rebuild
   (the whole point of the pages.js registry). */
.cards { grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); }

/* ── stage cards on the Families & Youth hub: the two nested sub-pages ───────
   Same .card / .card-link language as the offerings grid — a stage is just a
   linked page card, so it inherits the hover + "Explore →" affordance. */
.stage-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.25rem; margin-block-start: 2.5rem; }

/* ── age/format line under a stage sub-page's tagline ── */
.age-line { font-family: var(--sans); font-size: .78rem; letter-spacing: .14em; text-transform: uppercase; color: var(--sage); font-weight: 700; margin-block-end: 2rem; }
:root[lang="ar"] .age-line { letter-spacing: 0; font-size: .95rem; }

/* ── the two-stage overview on the hub ("How this works") ── */
.stages { max-width: 60ch; font-size: 1.12rem; }
.stages .stage-lead { font-family: var(--serif); font-size: 1.5rem; font-style: italic; color: var(--copper); }
dl.stages { margin: 0; }
.stages dt { font-family: var(--serif); font-size: 1.25rem; font-weight: 600; color: var(--navy); margin-block-start: 1.5rem; }
.stages dd { margin: .35rem 0 0; color: var(--navy); }

/* ═══════════════════════════════════════════════════════════════════════════
   2026-07-11 (iteration 2) — MATCH HIBA'S OWN MOCKUPS.
   Her two reference images were embedded in the feedback .docx all along
   (word/media/image1.png, image2.png); everyone read the doc's TEXT, which
   pointed at external html files, and nobody opened the container. Extracted to
   docs/hiba-feedback-embedded/. HER STRUCTURE, OUR TOKENS (Steve).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── image1.png: "Ways to work together" is a HUB-AND-SPOKE TREE, not a grid ──
   The statement sits in a bordered hub BOX with connector lines branching down
   to the offering cards, org-chart style. The flat card grid it replaces said
   nothing about the RELATIONSHIP between the statement and the offerings — which
   is the entire point Hiba was making. */
.hub-box {
  background: var(--white); border: 1px solid var(--gold); border-radius: 4px;
  padding: clamp(1.5rem, 4vw, 2.5rem); max-width: 640px; margin-inline: auto; text-align: center;
}
.hub-box .hub-statement { font-family: var(--serif); font-weight: 600; font-size: clamp(1.5rem, 3.2vw, 2.15rem); line-height: 1.2; color: var(--navy); margin: 0; }
.hub-box .hub-support { font-family: var(--serif); font-style: italic; font-size: clamp(1.05rem, 2vw, 1.35rem); color: var(--gold); margin: .8rem 0 0; }

/* the stem: hub box down to the horizontal bus */
.hub-stem { width: 1px; height: 2.75rem; background: var(--gold); margin-inline: auto; }

.spokes { --gap: 1rem; --drop: 2.75rem; display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(265px, 1fr)); margin-block-start: 0; }

/* Connectors are drawn ONLY where all five spokes share ONE row. Once the grid
   wraps, a "tree" line joining cards on different rows is a lie about the
   structure — so below this width the tree degrades honestly to the hub box
   plus stacked cards, with no connectors at all. */
@media (min-width: 1240px) {
  .spokes { grid-template-columns: repeat(5, 1fr); }
  /* Only ever drawn once pages.js has stamped the real end-caps. Without JS there
     is no rail at all — which is correct: a rail with dangling stubs on both ends
     is worse than no rail. */
  .spokes.spokes-ready { padding-block-start: var(--drop); }
  .spokes.spokes-ready > .card { position: relative; }
  /* vertical drop into each card */
  .spokes.spokes-ready > .card::before {
    content: ""; position: absolute; top: calc(-1 * var(--drop)); left: 50%;
    width: 1px; height: var(--drop); background: var(--gold);
  }
  /* Horizontal rail: each card draws its own segment, extending half a gap into
     each neighbour so the segments JOIN into one continuous rule.
     ⚠️ The end-caps key off .spoke-first / .spoke-last — classes pages.js stamps on
     the ACTUAL first and last CARD — never off :first-child/:last-child. The
     <noscript> fallback is a child of this grid, so :last-child matched IT and the
     real last card overshot its own drop by half a gap: Steve's "side-twig".
     Derived from the cards, this holds at any card count. */
  .spokes.spokes-ready > .card::after {
    content: ""; position: absolute; top: calc(-1 * var(--drop));
    left: calc(-1 * var(--gap) / 2); right: calc(-1 * var(--gap) / 2);
    height: 1px; background: var(--gold);
  }
  .spokes.spokes-ready > .card.spoke-first::after { left: 50%; }
  .spokes.spokes-ready > .card.spoke-last::after  { right: 50%; }
}

/* ── image2.png: the two-stage progression on the Families & Youth hub ──────
   A bordered container holding STAGE ONE -> STAGE TWO with an arrow between and
   "TWO STAGES, ONE JOURNEY" beneath, inside the border. The stage boxes are also
   the navigation to the sub-pages (they are literally Growth Lab and Aspiring
   Leaders), so the page does not list the same two things twice. */
.stage-flow { border: 1px solid var(--gold); border-radius: 6px; padding: clamp(1.25rem, 3vw, 2rem); margin-block-start: 2rem; }
/* stretch, not center: unequal-height boxes centred against each other leave their
   TOPS misaligned, which reads as a wonky pair. Hiba's mockup has them level. */
.stage-flow-row { display: grid; grid-template-columns: 1fr auto 1fr; gap: clamp(.75rem, 2vw, 1.5rem); align-items: stretch; }
.stage-flow-row .stage-arrow { align-self: center; }
.stage-arrow { color: var(--gold); font-size: 1.75rem; line-height: 1; text-align: center; }
[dir="rtl"] .stage-arrow { transform: scaleX(-1); }
.stage-caption { text-align: center; margin-block-start: 1.5rem; margin-block-end: 0; font-family: var(--sans); font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; color: var(--gold); font-weight: 700; }
:root[lang="ar"] .stage-caption { letter-spacing: 0; font-size: .9rem; }
.stage-eyebrow { font-family: var(--sans); font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--sage); font-weight: 700; margin: 0 0 .5rem; display: block; }
:root[lang="ar"] .stage-eyebrow { letter-spacing: 0; font-size: .88rem; }
/* Stack on narrow screens; the arrow turns to point DOWN, since the progression
   is still a progression — it just runs vertically. */
@media (max-width: 700px) {
  .stage-flow-row { grid-template-columns: 1fr; }
  .stage-arrow { transform: rotate(90deg); }
  [dir="rtl"] .stage-arrow { transform: rotate(90deg); }
}

/* ── HERO VERTICAL RHYTHM (Steve, 2026-07-11, verbatim: "Need more space for dr
   hiba khalid at the top … not quite sitting right for me on desktop. Work better
   on mobile view. On either format, the Dr Hiba piece needs more space some how.")

   THE CAUSE, and it explains exactly why mobile felt right and desktop did not:
   the eyebrow was a FIXED .8rem while the h1 scales clamp(2.4rem … 4rem). So the
   name/headline ratio is ~3:1 on mobile but ~5:1 on desktop — the name shrinks
   RELATIVE to the headline precisely as the screen grows. Mobile was never
   "better designed"; it was just the width where the fixed size happened to sit
   in proportion. Scaling the eyebrow WITH the headline makes the relationship
   hold at every width, instead of being right at one of them by accident.

   The <header> also carried NO padding of its own (only `section` had any), so
   the name sat hard under the subnav rule at every width — "on either format". */
.hero { padding-block: clamp(2.75rem, 6vw, 5rem); }
.hero .eyebrow {
  color: var(--sage);
  /* Tuned AGAINST the measured mobile ratio, not by eye. At 1440 the headline is
     76px; a 1.15vw eyebrow resolved to 16.6px = 1:4.6, while mobile sat at 1:2.8 —
     still diverging, which is the whole defect. 1.6vw puts desktop at ~23px = 1:3.3,
     inside mobile's proportion. The quality bar was mobile; this is how you HIT it
     rather than approximate it. */
  font-size: clamp(.9rem, 1.6vw, 1.4rem);
  letter-spacing: .22em;                        /* the name earns presence from tracking, not weight */
  margin-block-end: clamp(1.35rem, 2.6vw, 2.1rem);
}
.hero h1 { margin-block-end: clamp(1rem, 1.8vw, 1.5rem); }
.hero .tagline { margin-block-end: clamp(1.75rem, 3vw, 2.5rem); }
/* the stage sub-pages put an age line between tagline and CTA — keep that rhythm */
.hero .tagline + .age-line { margin-block-start: -1rem; margin-block-end: clamp(1.75rem, 3vw, 2.5rem); }
:root[lang="ar"] .hero .eyebrow { letter-spacing: 0; font-size: clamp(1rem, 1.4vw, 1.2rem); }

/* ── The spokes row needs MORE ROOM THAN THE PAGE MEASURE. ───────────────────
   Hiba's mockup carries FOUR cards; Steve's fifth makes five, and five cards
   inside the site's 1080px wrap leaves ~190px of content each — which shredded
   the titles into one-word lines ("Leadership / development / & / programme /
   design"). Every DOM assertion still passed: five cards, five connectors, one
   row, all true. It just looked broken. A layout defect is invisible to a layout
   assertion, which is the second time today that has cost me a round.
   So this ONE section breaks out to a wider measure, and the spoke type tightens
   to suit the narrower column. The rest of the page keeps the 1080 measure. */
/* ── The spokes row, sized to the SCREEN rather than to the page measure ──────
   Steve: "wondering if the boxes could widen a little when the page opens out …
   split the heading a little more … leave room for likely explore links."

   Five cards is two more than the grid was designed for and one more than Hiba's
   mockup carries, so the row needs the width the rest of the page does not. The
   section breaks out to 1600px and trims its own side padding, so the cards WIDEN
   as the viewport opens instead of sitting in a narrow band inside empty margins.

   `--cta-slot` is a RESERVED footer on EVERY card — today only Families & Youth has
   an Explore link. Reserving the space now means the five cards already align, and
   adding the remaining links later drops them INTO the slot without reflowing the
   tree or moving a single connector. No invented links; just the room for them. */
.hub-section .wrap { max-width: 1600px; padding-inline: clamp(1rem, 3vw, 2rem); }

@media (min-width: 1240px) {
  .spokes { --cta-slot: 2.4rem; }
  .spokes .card { padding: 1.5rem 1.35rem calc(1.5rem + var(--cta-slot)); position: relative; }
  .spokes .card h4 {
    font-size: 1.15rem; line-height: 1.3;
    /* kills the orphaned "&" in "Leadership development & programme design" — the
       browser balances the lines itself, so it stays fixed at ANY card width
       instead of being hand-tuned to one. */
    text-wrap: balance;
    margin-block-end: 1rem;   /* Steve: "split the heading a little more" */
  }
  .spokes .card p { font-size: .95rem; line-height: 1.6; margin: 0; }
  /* the Explore link sits INSIDE the reserved slot, so a card with one and a card
     without one are exactly the same height */
  .spokes a.card-link .card-more {
    position: absolute; inset-block-end: 1.5rem; inset-inline-start: 1.35rem;
    margin: 0; padding: 0; font-size: .7rem;
  }
}

/* ══ VERTICAL HUB-AND-SPOKE VARIANT (?tree=vertical) ═════════════════════════
   Steve: "Try vertical — we might need to consider the horizontal…"

   The horizontal row's real constraint is arithmetic: five cards across a page
   can never be wider than a fifth of the page, so the copy gets squeezed no
   matter how the container is tuned. Running the spokes DOWN gives every card the
   FULL measure — the cramped wrapping disappears at the root rather than being
   fought with font sizes.

   A trunk drops from the hub, a spine runs down, and a twig branches into each
   card. The spine STOPS at the last card's twig — the same overshoot bug as the
   horizontal rail's "side-twig", rotated 90°, and fixed the same way: from the
   .spoke-last stamp pages.js derives from the real cards, so it holds at any count. */
.tree-vertical { --spine: 1.75rem; --twig: 2rem; max-width: 1000px; margin-inline: auto; }
.tree-vertical .hub-box { max-width: none; }
/* the trunk: drops from beneath the hub, on the spine's axis */
.tree-vertical .hub-stem { margin-inline: var(--spine) auto 0; height: 2.5rem; }

@media (min-width: 760px) {
  .tree-vertical .spokes { grid-template-columns: 1fr; padding-block-start: 0; padding-inline-start: calc(var(--spine) + var(--twig)); }
  .tree-vertical .spokes.spokes-ready > .card { position: relative; padding: 1.5rem 1.5rem calc(1.5rem + 2.4rem); }
  .tree-vertical .spokes.spokes-ready > .card h4 { font-size: 1.3rem; text-wrap: balance; margin-block-end: 1rem; }
  .tree-vertical .spokes.spokes-ready > .card p { font-size: 1rem; line-height: 1.6; margin: 0; }
  .tree-vertical .spokes.spokes-ready a.card-link .card-more { position: absolute; inset-block-end: 1.5rem; inset-inline-start: 1.5rem; margin: 0; padding: 0; font-size: .72rem; }

  /* twig: spine -> card, at the card's vertical centre */
  .tree-vertical .spokes.spokes-ready > .card::before {
    content: ""; position: absolute; top: 50%; inset-inline-start: calc(-1 * var(--twig));
    width: var(--twig); height: 1px; background: var(--gold);
  }
  /* spine: each card draws its own segment, joining through the gaps */
  .tree-vertical .spokes.spokes-ready > .card::after {
    content: ""; position: absolute; inset-inline-start: calc(-1 * var(--twig));
    top: calc(-1 * var(--gap) / 2); bottom: calc(-1 * var(--gap) / 2);
    width: 1px; height: auto; background: var(--gold);
  }
  /* first meets the trunk; last STOPS DEAD at its own twig — no dangling spur */
  .tree-vertical .spokes.spokes-ready > .card.spoke-first::after { top: 0; }
  .tree-vertical .spokes.spokes-ready > .card.spoke-last::after  { bottom: 50%; }
}
/* mobile: no spine — a stacked card list, same as the horizontal variant degrades to */
@media (max-width: 759px) {
  .tree-vertical .hub-stem { margin-inline: auto; }
  .tree-vertical .spokes.spokes-ready > .card::before,
  .tree-vertical .spokes.spokes-ready > .card::after { content: none; }
}

/* HONEYPOT — hidden from humans, present for bots. Not display:none: some bots skip
   those. Off-screen + zero-size is filled by naive crawlers and never seen or
   focusable by a person (aria-hidden + tabindex=-1 on the field itself). */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- stacked testimonial cards (aspiring-leaders) — added at prod cutover 2026-08-02 ---- */
/* STACKED TESTIMONIAL CARDS — overnight build 2026-07-31, Steve's spec verbatim:
   "sliding stacked cards … click each and a new one slides up — no automated
   sliding. We keep a good one on top and a few others below." aspiring-leaders
   ONLY (EN + AR), per his own scope-down. mockup/ is dev-served live, so this
   is review-surface work, not a production ship — see the DEV-REVIEW state
   below, same convention as .is-placeholder/.consent-pending above.
   Design choice: the peek offset is purely BLOCK-direction (vertical) so it
   needs zero RTL-specific handling — a horizontal peek would need mirroring
   under dir="rtl" and this sidesteps that entirely. JS toggles which card is
   .is-top vs .is-back; CSS only draws the two states, it never animates on
   its own (no @keyframes here — no automated sliding, matches the spec). */
.testi-stack { position: relative; max-width: 46ch; margin-inline: auto; }
/* overflow:hidden + a JS-measured min-height (testi-stack.js) is load-bearing,
   not decorative: cards hold DIFFERENT-length quotes, so without a clipped,
   fixed-height container the back card's excess text bleeds out below the
   peek instead of staying hidden — caught visually testing the AR page
   2026-07-31, not assumed safe from the CSS alone. */
.testi-stack-inner { position: relative; overflow: hidden; }
.testi-card {
  position: relative;
  padding-block: clamp(1rem, 3vw, 1.5rem);
  transition: transform .3s ease, opacity .3s ease;
  /* height is set inline by testi-stack.js (uniform across all cards in a
     stack) — flex + center means a shorter quote sits centered in that
     fixed box instead of stranded at the top with dead space below it. */
  display: flex; flex-direction: column; justify-content: center;
  box-sizing: border-box;
}
/* OPAQUE background + a real border on EVERY card, not just the top one —
   this is the second correction after visually testing the AR page
   2026-07-31: transform:scale() shrinks from the CENTER, so a translateY+
   scale combination on a same-height back card left well under 10px of
   actual visible peek (measured, not eyeballed: 4.7px) — invisible in
   practice despite "working" by the numbers. Fix drops scale/translate
   entirely: the back card is simply NARROWER (width reduction via inline
   inset, logical properties so it never needs RTL mirroring) and sits with
   more inline padding below the top edge, so a real card-shaped strip with
   its own border is visible below/beside the top card — the actual "a few
   others below" Steve asked for, not a numeric approximation of it. */
.testi-card {
  border: 1px solid var(--sand); border-radius: 4px; background: var(--linen);
}
.testi-card.is-top { position: relative; z-index: 2; opacity: 1; inset-inline: 0; }
.testi-card.is-back {
  position: absolute; inset-block-start: 18px; inset-inline: 5%; width: 90%;
  z-index: 1; opacity: .7; cursor: pointer; border-color: var(--gold);
}
/* mouse-only bonus affordance — matches Steve's "click each" phrasing literally.
   The real accessible control is .testi-next below; back cards stay aria-hidden
   and are never given tabindex, so a keyboard/AT user is never sent to a
   target ARIA says isn't there. Sighted mouse users get both paths. */
/* card 3+ (not built tonight — only 2 cards exist — but the rule is harmless
   and means a third card added later stacks correctly without a CSS change) */
.testi-card.is-back ~ .testi-card.is-back { inset-block-start: 32px; inset-inline: 8%; width: 84%; opacity: .45; }

.testi-stack-controls { display: flex; justify-content: center; margin-block-start: 1.5rem; }
.testi-next {
  font-family: var(--sans); font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--sage); background: transparent; border: 1px solid var(--sand); border-radius: 999px;
  padding: .55rem 1.4rem; cursor: pointer;
}
.testi-next:hover, .testi-next:focus-visible { color: var(--navy); border-color: var(--gold); }
.testi-next:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
/* single-card stacks (no second testimonial available) never render this
   control — JS omits it entirely rather than shipping a button that does
   nothing when clicked. */

.testi-stack-status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
