/* ============================================================
   Safadi Center — NEW WEBSITE TEMPLATE
   Concept: CANVAS — a neutral space brought to life
            through creativity, colour and expression.

   This stylesheet is PALETTE-AGNOSTIC. Every colour is a token
   defined by the page in a `.theme-*` block (see site-01.html /
   site-02.html). Recolouring the site = swapping that one block.

   Structure mirrors the existing live site (website/index.html)
   so the two can be compared section for section.
   ============================================================ */

/* ---------- tokens that never change between palettes ---------- */

:root {
  /* Per the typography guide (website/assets/website fonts.docx): ONE
     typeface for the whole site — headings and body both — with weight
     doing the work a second typeface used to. --font-display and
     --font-text now resolve to the same family on purpose; they stay
     separate tokens because every heading rule already reads
     var(--font-display) and a single var(--font) rename would touch far
     more selectors for no visual difference. Swap either token alone and
     the two halves of the site would drift apart again. */
  --font-display: "Red Hat Display", "Segoe UI", sans-serif;
  --font-text: "Red Hat Display", "Segoe UI", sans-serif;
  --font-ar: "Noto Kufi Arabic", sans-serif;

  --container: 1180px;
  /* navbar height — the hero subtracts it to fill exactly one screen */
  --nav-h: 76px;
  --radius: 4px;
  --radius-md: 10px;
  --radius-lg: 22px;

  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 0.55s;
}

/* ---------- reset & base ---------- */

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

html {
  scroll-behavior: smooth;
  /* .navbar is sticky (top:0), so an anchor jump (Home, About, Programs…)
     lands with the target's top edge hidden behind the bar unless every
     scroll target reserves this much room above it. One rule here covers
     every #id link in the nav instead of scroll-margin-top on each section.
     +44px on top of the bar's own height so flush-landing sections (id on
     .section-head — Volunteer, Internship, Connect, Propose) get a little
     breathing room instead of touching the navbar edge to edge. */
  scroll-padding-top: calc(var(--nav-h) + 44px);
}

body {
  font-family: var(--font-text);
  overflow-x: hidden;          /* nothing may push the page sideways */
  font-size: 16.5px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; }

[lang="ar"] { font-family: var(--font-ar); }

.container { max-width: var(--container); margin: 0 auto; padding: 0 28px; }

::selection { background: var(--primary); color: var(--on-primary); }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- template ribbon (identifies this as a mockup) ---------- */

.tpl-ribbon {
  background: var(--ink);
  color: var(--paper);
  font-size: 11.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 700;
}
.tpl-ribbon .container {
  display: flex; align-items: center; gap: 14px;
  padding-top: 9px; padding-bottom: 9px; flex-wrap: wrap;
}
.tpl-ribbon b { color: var(--highlight); }
.tpl-ribbon .tpl-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); flex: none;
}
.tpl-ribbon a {
  margin-left: auto; border-bottom: 1px solid currentColor;
  padding-bottom: 1px; opacity: 0.75;
}
.tpl-ribbon a:hover { opacity: 1; }

/* ---------- typography helpers ---------- */

.kicker {
  /* the guide's "small label" case: uppercase is fine here because the
     label itself stays short (EXPLORE, UPCOMING EVENTS) — weight carries
     the emphasis rather than a heavier size or an even heavier cut. */
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--primary);
  display: flex; align-items: center; gap: 13px;
  margin-bottom: 16px;
}
.kicker::before {
  content: ""; width: 34px; height: 3px; flex: none;
  background: var(--accent); border-radius: 2px;
}

.section-title {
  font-family: var(--font-display);
  font-weight: 700;
  /* H2 in the type scale: 38-42px desktop / 30-32px mobile */
  font-size: clamp(30px, 4vw, 42px);
  line-height: 1.15;
  letter-spacing: -0.015em;
  margin-bottom: 16px;
  /* Big titles only, on request — was inheriting body's near-black --ink;
     now the same navy the logo itself uses (--logo-ink), not --primary
     (a close but not identical blue already used for .accent/em below). */
  color: var(--logo-ink);
}
.section-title em { font-style: normal; color: var(--primary); }

.section-sub { color: var(--muted); max-width: 620px; }

.section { padding: clamp(64px, 8vw, 108px) 0; }
.section-tint { background: var(--surface); }

.section-head { max-width: 720px; margin-bottom: 48px; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 14px 28px;
  border: 2px solid transparent;
  border-radius: var(--btn-radius, 999px);
  /* Buttons in the type scale: 14-15px, weight 600 — Semibold is enough
     emphasis on a filled/outlined pill; 800 was fighting the button shape
     for attention. */
  font-size: 14px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.3s var(--ease-out), background 0.3s, color 0.3s, box-shadow 0.3s;
}
.btn-solid { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.btn-solid:hover { background: var(--primary-deep); border-color: var(--primary-deep); transform: translateY(-2px); box-shadow: var(--shadow-lift); }
/* Ghost used to be an outlined copy of --primary — same navy as the
   solid button next to it, just hollow. Tried --brand-1 (ochre) first,
   on request; also on request, that read as orange rather than the
   logo's actual red, so it's --accent (terracotta, --brand-2) instead
   — the same hue --btn-accent already fills solid, here just hollow.
   The two sharing a hue is fine: shape and fill-vs-outline still tell
   them apart, and --accent is a real logo colour either way.
   color-mix, not a fourth hardcoded hex, for the hover shade —
   --primary-deep is a literal because it predates this pattern; no
   reason to hand-pick a new one when the tool for it already exists
   in this file (see the hero scrim below). */
.btn-ghost { background: transparent; color: var(--accent); border-color: var(--accent); }
.btn-ghost:hover {
  background: var(--accent); color: var(--on-accent); transform: translateY(-2px);
}
.btn-accent { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.btn-accent:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); filter: brightness(0.94); }
.btn-lg { padding: 17px 36px; }
.btn-sm { padding: 10px 18px; font-size: 10.5px; }

/* The full-size buttons get the same trailing arrow the small
   tertiary links already have (below) — "this is clickable" should
   read the same across every tier, not just the quietest one. Pure
   CSS, no markup change, so it applies to every existing button (and
   any new one) for free. .btn-book is excluded: it already has its
   own affordance, the ticket notch, in that same corner — an arrow
   there would just fight it for the same 20px of space. */
.btn-solid::after,
.btn-ghost:not(.btn-sm)::after,
.btn-accent:not(.btn-book)::after {
  content: ""; width: 7px; height: 7px; flex: none;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-1px) rotate(-45deg);
  transition: transform 0.25s var(--ease-out);
}
.btn-solid:hover::after,
.btn-ghost:not(.btn-sm):hover::after,
.btn-accent:not(.btn-book):hover::after {
  transform: translateY(-1px) rotate(-45deg) translate(2px, -2px);
}

/* The site's actual default shape: two corners cut on the diagonal
   rather than any of them rounded — echoes the logo's own angular
   petal, not its arch (that was tried first and didn't land). A
   clip-path, not a radius, because a radius can only curve a corner —
   it can't cut one on a straight diagonal. .btn-book overrides this
   below to keep its own shape; .btn-ghost.btn-sm has no fill for it
   to apply to, but is excluded explicitly for clarity. */
.btn-solid, .btn-ghost, .btn-accent {
  border-radius: 0;
  clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
}
/* The smallest ghost buttons ("See the roles", "Find us"...) are quiet,
   tertiary nudges, not calls to action — dropping the box and just
   underlining the text says that at a glance, and keeps the faceted
   shape meaning "press this" everywhere else on the site. */
.btn-ghost.btn-sm {
  background: none; border: 0; border-radius: 0; clip-path: none; padding: 3px 0 5px; gap: 7px;
  border-bottom: 2px solid currentColor;
}
.btn-ghost.btn-sm:hover {
  background: none; color: color-mix(in srgb, var(--accent) 74%, black);
  transform: translateX(3px); box-shadow: none;
}
.btn-ghost.btn-sm::after {
  content: ""; width: 6px; height: 6px; flex: none;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-1px) rotate(-45deg); transition: transform 0.25s var(--ease-out);
}
.btn-ghost.btn-sm:hover::after { transform: translateY(-1px) rotate(-45deg) translate(2px,-2px); }

/* The one shape reserved for an actual reservation — a bitten notch and
   a dashed perforation on one edge, like a torn ticket stub. Deliberately
   not the default: it only means something because it's not everywhere.
   Its own corners stay the ORIGINAL arch rather than following whatever
   the sitewide default is this week — a booking button is a special
   case on purpose, not a variant that should drift with the default.
   clip-path: none overrides the facet rule above (equal specificity,
   later in the file, so no !important needed); the notch circle sits
   10px outside the button's own box, and a clip-path on this element
   would have sliced that overhang clean off.
   The notch's fill assumes it sits on --surface (a .section-tint block,
   true of the auditorium booking CTA it was built for) — move it onto a
   plain --paper section and update the fill to match. */
.btn-book {
  position: relative; overflow: visible; padding-right: 40px;
  clip-path: none; border-radius: 999px 999px 6px 6px;
}
.btn-book::after {
  content: ""; position: absolute; right: -10px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--surface); border: 2px solid currentColor;
}
.btn-book::before {
  content: ""; position: absolute; right: 24px; top: 10px; bottom: 10px; width: 0;
  border-left: 1.5px dashed currentColor; opacity: 0.55;
}

/* ---------- navbar ---------- */

.navbar {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
  /* soft drop shadow so the bar reads as sitting above the page. The live
     site only styles this for a .is-scrolled state that never fires, so it
     is unconditional here — the bar is sticky and overlaps content anyway. */
  box-shadow: 0 10px 34px -18px rgba(46, 47, 51, 0.35);
}
/* 18px, not the original 30px — part of the nav-overflow fix; see the
   .nav-links comment below. */
/* max-width: 1208px, not the shared 1180px --container every other
   section uses — this element carries BOTH .container and .navbar-inner
   (index.html etc.), and the later rule wins the tie at equal
   specificity, so this is a navbar-only override, not a site-wide
   change. 2026-09-25 ("give a little bit space" between logo↔Home and
   Get Involved↔icons, "but keep them identical"): those two gaps are
   entirely .nav-links'/.nav-social's own margin-left:auto absorbing
   whatever's left over in the row after brand+links+cta+social+gaps —
   padding added anywhere INSIDE that row just shrinks the auto margins
   by the same amount and cancels out (confirmed live). The only way to
   actually grow both gaps, together, while still keeping the logo flush
   left and the icons flush right, is to hand the row more free space to
   split in the first place — 28px more here becomes +14px on each side.
   Safe against the nav-overflow fix documented below: this only WIDENS
   the navbar's own content box past --container's 1180px cap, it never
   narrows anything, so the worst-case 1241px measurement that tuning
   was done against still holds — at 1241px the navbar-inner is already
   wide enough to hit this 1208px cap too, so the row simply gets 28px
   MORE available width there than the original tuning assumed, all of
   it extra slack, none of it taken away (verified live: still one row,
   no overflow, at the same 1241px width the comment below tunes
   against). */
.navbar-inner { display: flex; align-items: center; gap: 18px; height: var(--nav-h); max-width: 1208px; }

/* flex-shrink:0 — .brand-icon and .brand-word are both already
   flex:none, but .brand itself (the flex item wrapping them, inside
   navbar-inner's own flex row) was not, so once the row's total
   content stopped fitting --container it was the one thing left to
   shrink. A shrunk .brand box doesn't shrink its flex:none children
   with it, so the icon+wordmark just overflowed the smaller box and
   sat on top of "Home" — this is what actually caused that, not the
   wordmark SVG. See the .nav-links comment for the rest of the fix. */
.brand { display: flex; align-items: center; gap: 12px; flex: none; }
/* Icon and wordmark are two separate pieces rather than one combined image,
   so the motion can move the tile mark without dragging "SAFADI CENTER"
   along with it. The wordmark is wordmark-horizontal.svg via <img>; the icon
   is inlined into each navbar (see the comment on that markup for why it
   can't be an <img>). Both were cropped from the same logo-horizontal.svg
   lockup, so icon and letterforms are pixel-identical to the original. */
/* 54x56 rather than height + width:auto — the inline SVG's intrinsic ratio
   (275:285) would resolve to the same thing, but stating it keeps the navbar
   from reflowing by a pixel while the page is still parsing. overflow:visible
   so the tiles can drift outside their own viewBox on hover without clipping. */
.brand-icon { width: 54px; height: 56px; flex: none; overflow: visible; }
/* Same deliberate optical nudge as .footer-word, scaled down for the
   smaller navbar mark (26px vs the footer's 30px tall wordmark). */
.brand-word { height: 34px; width: auto; flex: none; transform: translateY(2px); }

/* The tile mark's colours and motion live in logo-mark.css — shared with
   join.html / joinus.html, which do not load this stylesheet. Sizing per
   context stays here. */

/* 10px, not the original 24px — adding "Renting" as a 9th item pushed
   the desktop row's natural width (brand + links + social + CTA) past
   the site's fixed 1180px --container (1124px inside navbar-inner's
   own 28px+28px padding), so above the 1240px hamburger breakpoint
   the row no longer "fits with real room to spare" the way the
   breakpoint comment below describes. Two things happened at once:
   flexbox fell back to shrinking every flex child including .brand
   (which had no flex-shrink:0 of its own, unlike its own icon/word
   children, both already flex:none) — that let the logo overflow its
   shrunk box and sit on top of "Home"; separately, even with .brand
   protected, the remaining natural content (~1225px) still didn't
   fit the ~1124px available. Raising the 1240px breakpoint would NOT
   have helped — --container caps at 1180px for every viewport past
   it regardless of how wide the window gets, so there is no wider
   "desktop" state to grow into. The real fix is trimming spacing:
   this gap plus navbar-inner's, .nav-social's, the icon size and
   .nav-cta's padding (all below) were tuned together, live, against
   the worst case (1241px, right at the breakpoint) until the row fit
   with ~16px of measured slack — comfortable, not razor's-edge.
   Re-measure the same way if another nav item or longer label is
   added later; see .brand above and .navbar-inner / .nav-social /
   .nav-social-link / .nav-cta below for the rest of this fix. */
/* margin-left:auto here AND on .nav-social below — two auto margins on
   the same flex row split the row's leftover space EQUALLY between them
   (a standard flexbox behaviour, not a coincidence), which is what keeps
   logo↔Home and Get Involved↔icons at the same width as each other at
   any viewport, on request, without hand-tuning two separate values that
   could drift apart. Brand sits flush against the row's own left edge
   (nothing before it wants space) and .nav-social flush against its right
   edge (nav-toggle is display:none at this width, so it claims none of
   the row) — since .container's own padding is symmetric and the row
   fills it edge to edge on both sides, the empty margin before the logo
   and after the icons come out equal too, for the same reason. Verified
   live against the same 1241px worst case the comment above this block
   already tunes against — still one row, no overflow.
   2026-09-25 ("give a little bit space in these area... but keep them
   identical"): tried adding padding directly to .brand/.nav-social first
   and it produced NO visible change — auto claims 100% of whatever is
   left over in the row, so widening either end's own box just shrinks
   what the auto margin has left to absorb by the exact same amount,
   cancelling out (confirmed live). The two gaps are the row's free
   space, not something padding can add to from inside the row — so the
   actual fix is .navbar-inner's own max-width below, which hands the
   row more free space to split in the first place; this rule and the
   mechanism it describes are otherwise untouched. */
.nav-links { display: flex; gap: 10px; margin-left: auto; }
.nav-link {
  /* Navigation in the type scale: ~15px, weight 500 — nudged down from
     700 (heavy weight was doing the legibility work the letter-spacing
     already does at this size). */
  font-size: 14px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); padding: 6px 0;
  position: relative; transition: color 0.25s;
  white-space: nowrap;
}
.nav-link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px; border-radius: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.35s var(--ease-out);
}
.nav-link:hover, .nav-link.is-active { color: var(--ink); }
.nav-link:hover::after, .nav-link.is-active::after { transform: scaleX(1); }

/* "The Academy" (formerly "Programs") gets its own version of the logo's
   colour-reacts-to-you moment, on request — plain text has no tiles to float apart, so
   what carries over is the COLOUR half: instead of fading to the
   same neutral ink every other nav link does, it takes the accent
   red the underline already turns on hover and intensifies it,
   exactly like the logo's own tiles do. Click / keyboard-activate
   goes a shade deeper plus a small press-in scale, matching the
   logo's :active state and the slower timing it was just given. */
.nav-link-programs:hover, .nav-link-programs.is-active {
  color: var(--accent); filter: saturate(1.3) brightness(1.05);
}
.nav-link-programs { transition: color 0.25s, filter 0.55s var(--ease-out), transform 0.55s var(--ease-out); }
.nav-link-programs:active, .nav-link-programs:focus-visible {
  color: var(--accent); filter: saturate(1.6) brightness(0.9); transform: scale(0.94);
}

/* "Get Involved" moved out of the nav-links row and next to the social
   icons, on request. Originally a bordered .btn.btn-ghost.btn-sm button;
   converted to plain .nav-link styling (on request, "same design like the
   other menu name") — the HTML now carries `class="nav-link nav-cta"`
   instead of the old button classes, so it gets .nav-link's own
   type/underline-on-hover for free. It only exists here for desktop: it
   vanishes below the nav breakpoint (.nav-cta / .nav-social are
   display:none there, same as before), so .nav-link-involved-mobile below
   is a second, plain copy of the same link kept inside .nav-links purely
   so the hamburger menu still offers a way to it on mobile.
   ⚠️ margin-left is NEGATIVE, and that is not a typo. .nav-cta sits
   OUTSIDE .nav-links (a separate flex child of .navbar-inner, so it can
   land after .nav-links but before .nav-social) — it does not get
   .nav-links' own 10px inter-link gap for free, only .navbar-inner's
   wider 18px one. -8px pulls that back down to the same 10px every other
   pair of nav links sits at (18 − 8 = 10), measured live against the
   Contact↔Get Involved gap rather than guessed. */
.nav-cta { margin-left: -8px; }
/* "a little special" — accent-coloured and bold rather than the muted
   weight-600 grey every other link uses, so it still reads as a call to
   action even without the button chrome it used to carry. Hover
   intensifies the same colour (the --accent-branded pattern
   .nav-link-programs already uses above) instead of fading to the plain
   .nav-link:hover ink — fading to ink would make the "special" link look
   MOST ordinary exactly when a visitor's pointer finds it. */
.nav-cta { color: var(--accent); font-weight: 700; }
.nav-cta:hover, .nav-cta.is-active { color: var(--accent); filter: saturate(1.3) brightness(1.05); }
.nav-link-involved-mobile { display: none; }
/* A periodic nudge so the one link that's actually a call to action
   doesn't just sit there looking like the rest of the menu — most of
   each 6s cycle is stillness (0–90%), then a quick wobble (90–100%,
   same shape as the Programs icons' own hover shake further down this
   file, just smaller — this is a passing nudge, not a hover reaction)
   before it settles back to rest and waits for the next cycle. Paused
   on hover/focus so it doesn't fight the colour-intensify hover state
   right above, or restart mid-wobble under a resting pointer.
   prefers-reduced-motion already kills every animation site-wide (see
   the bottom of this file), so this needs no separate opt-out. */
@keyframes nav-cta-nudge {
  0%, 90%, 100% { transform: rotate(0deg); }
  91% { transform: rotate(-9deg); }
  93% { transform: rotate(8deg); }
  95% { transform: rotate(-6deg); }
  97% { transform: rotate(4deg); }
  99% { transform: rotate(-2deg); }
}
.nav-cta {
  display: inline-block;
  transform-origin: 50% 65%; /* low in the word, closer to the baseline it sits on — rotating from dead centre made it read as tumbling rather than a nudge */
  animation: nav-cta-nudge 6s ease-in-out infinite;
}
.nav-cta:hover, .nav-cta:focus-visible { animation-play-state: paused; }

/* Replaces the old "Reserve a Space" nav-cta button — social follow
   icons plus a WhatsApp icon that opens joinus.html (the guest
   sign-up page) in a new tab, so leaving the main site to join isn't
   a dead end for whoever clicked it. */
/* gap 6px and the circles 32px, not the original 8px/36px — part of
   the nav-overflow fix; see the .nav-links comment above. */
/* margin-left on top of .navbar-inner's own 18px gap — after the nav-cta/
   nav-social swap (site-wide, on request) the two sat right on top of the
   base gap and read as crowded; this is the breathing room asked for. */
/* margin-left:auto (was a fixed 18px) — now the one that absorbs the
   row's free space, so the icons sit at the right edge while the links +
   Get Involved cluster stays left, close to the logo. See the comment on
   .nav-links' own margin-left for the rest of this rebalance, and for
   the 2026-09-25 note on where the extra gap actually comes from now. */
.nav-social { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.nav-social-link {
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  color: var(--ink);
  transition: background 0.2s, border-color 0.2s, color 0.2s;
}
/* Facebook/Instagram/YouTube fill most of the circle (20px, scaled
   down from 22px along with the 36px->32px circle above); WhatsApp
   stays at its original smaller size on request. */
.nav-social-link svg { width: 20px; height: 20px; fill: currentColor; }
.nav-social-link:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
/* WhatsApp keeps its own colour rather than the generic site accent —
   it's a different, recognisable action (join via WhatsApp-style
   messaging) from "follow us", not just a fourth social icon — but,
   like Facebook/Instagram/YouTube below, that colour is now pulled
   from the logo (--brand-3, palette-01.css) rather than WhatsApp's
   own real-world green, so all four read as this Center's identity
   rather than a row of stock app badges. Olive is the "spirit" match
   for green among the logo's four colours. */
.nav-social-whatsapp { background: var(--brand-3); border-color: var(--brand-3); color: #fff; }
.nav-social-whatsapp svg { width: 16px; height: 16px; }
.nav-social-whatsapp:hover { filter: brightness(1.12); background: var(--brand-3); border-color: var(--brand-3); }
/* Facebook, Instagram and YouTube are filled with the logo's own
   colours (--brand-1..4, palette-01.css) rather than each platform's
   real-world brand colour — on request, so the row reads as this
   Center's identity, not a generic row of app badges. Each is picked
   for the closest "spirit" match to the platform's own hue: Facebook's
   blue -> navy, YouTube's red -> terracotta, Instagram's warm end of
   its gradient -> ochre. Each :hover re-asserts its own colour so the
   generic .nav-social-link:hover rule above doesn't wash it back out. */
.nav-social-facebook { background: var(--brand-4); border-color: var(--brand-4); color: #fff; }
.nav-social-facebook:hover { filter: brightness(1.12); background: var(--brand-4); border-color: var(--brand-4); }
.nav-social-instagram { background: var(--brand-1); border-color: var(--brand-1); color: #fff; }
.nav-social-instagram:hover { filter: brightness(1.12); background: var(--brand-1); border-color: var(--brand-1); }
.nav-social-youtube { background: var(--brand-2); border-color: var(--brand-2); color: #fff; }
.nav-social-youtube:hover { filter: brightness(1.12); background: var(--brand-2); border-color: var(--brand-2); }
/* Join Us (joinus.html, the guest sign-up form QR codes point at) — a
   fifth icon added 2026-09-23 once WhatsApp stopped doubling as its
   link (WhatsApp now opens a real chat, above). The logo's four tiles
   are already spoken for by the other four icons, so this uses
   --brand-5 (slate, palette-01.css), the one identity colour with no
   icon yet. */
.nav-social-joinus { background: var(--brand-5); border-color: var(--brand-5); color: #fff; }
.nav-social-joinus:hover { filter: brightness(1.12); background: var(--brand-5); border-color: var(--brand-5); }

.nav-toggle { display: none; background: none; border: 0; cursor: pointer; padding: 8px; }
.nav-toggle span { display: block; width: 24px; height: 2px; background: var(--ink); margin: 5px 0; transition: 0.3s; }

/* ---------- hero ---------- */

.hero { position: relative; overflow: hidden; background: var(--hero-bg); }
.hero-inner {
  display: grid; grid-template-columns: 1.05fr 0.95fr;
  gap: 56px; align-items: center;
  padding: clamp(28px, 3.5vw, 48px) 28px clamp(72px, 8vw, 110px);
}
.hero-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(40px, 6.4vw, 76px);
  line-height: 1.05;
  letter-spacing: -0.01em;
  display: flex; flex-direction: column;
  /* Big titles only, on request — was inheriting body's near-black --ink;
     now the same navy the logo itself uses. Shared with .page-hero-title
     just below, so every subpage hero picks it up too. */
  color: var(--logo-ink);
}
/* Shared with .page-hero-title (About/Academy/Gallery/Spaces/News/Contact/
   Involved) — same "one word picked out in navy on a tan highlight"
   treatment as the home hero's "Culture", not a lighter-weight subpage
   cousin of it. Upright, not italic (dropped site-wide on request — it
   read busier on longer words like "Involved" than on short nouns like
   "Center"/"Space"). */
.hero-title .accent, .page-hero-title .accent { color: var(--primary); }
.hero-title .mark-word, .page-hero-title .mark-word {
  position: relative; display: inline-block; width: fit-content;
}
.hero-title .mark-word::after, .page-hero-title .mark-word::after {
  content: ""; position: absolute; left: -2px; right: -2px; bottom: 0.1em;
  height: 0.26em; background: var(--highlight); opacity: 0.42;
  z-index: -1; border-radius: 3px;
}
.hero-lede { margin-top: 22px; font-size: 19px; color: var(--muted); max-width: 520px; }
.hero-actions { display: flex; gap: 14px; margin-top: 32px; flex-wrap: wrap; }
/* text-align:start + dir="rtl" sets it to the RIGHT edge of the copy column,
   which is what the live site does — it is the Arabic line's own reading
   start, so it aligns to its own text direction rather than the page's. */
.hero-ar {
  margin-top: 26px; font-size: 19px; color: var(--primary);
  font-family: var(--font-ar);
  text-align: start;
}

/* ---------- hero variant: photo + texture cover ----------
   Used by design 01. Mirrors the current live site: a blurred photo of
   the building faded in from the right, with a tiled texture over it.
   Scoped to .hero-cover so design 02 keeps its colour-block canvas. */

/* One photo treatment, two hosts: the full-screen home hero and the
   short subpage band. Sharing the rule is what keeps every page on the
   same picture — a second copy would drift the first time one is tuned. */
.hero-cover .hero-photo,
.page-hero .hero-photo {
  position: absolute;
  background-image: var(--hero-photo, url("hero-building.jpg"));
  background-size: cover;
  background-position: var(--hero-photo-position, center 38%);
  filter: blur(var(--hero-photo-blur, 3px)) saturate(var(--hero-photo-saturate, 0.82));
  opacity: var(--hero-photo-opacity, 0.5);
  /* fades out towards the copy so the text never sits on busy pixels */
  -webkit-mask-image: var(--hero-photo-mask, radial-gradient(ellipse 82% 78% at 76% 42%, #000 0%, transparent 72%));
          mask-image: var(--hero-photo-mask, radial-gradient(ellipse 82% 78% at 76% 42%, #000 0%, transparent 72%));
  pointer-events: none;
}
/* Same bleed on both hosts now — the subpage band used to sit flush
   (inset: 0), which showed a harder edge under the blur than the home
   hero's overscanned crop does. */
.hero-cover .hero-photo,
.page-hero .hero-photo { inset: -24px; }

/* The home hero specifically stacks TWO photos and cross-fades between
   them (hero-rotate.js flips .is-hero-active on a timer) — background-
   image itself can't transition, so both layers sit on top of each
   other and only opacity moves. Position/filter/mask are still shared
   with every other .hero-photo via the rule above; only the source
   image and the fade are these two classes' own. Specificity is bumped
   (three classes, not two) so this reliably beats that rule's own
   opacity regardless of source order. .page-hero is untouched — those
   pages have one photo and no rotation to run. */
.hero-cover .hero-photo.hero-photo-1 { background-image: var(--hero-photo); }
.hero-cover .hero-photo.hero-photo-2 { background-image: var(--hero-photo-2); }
.hero-cover .hero-photo.hero-photo-1,
.hero-cover .hero-photo.hero-photo-2 {
  opacity: 0;
  transition: opacity 2.2s var(--ease-out);
}
.hero-cover .hero-photo.hero-photo-1.is-hero-active,
.hero-cover .hero-photo.hero-photo-2.is-hero-active {
  opacity: var(--hero-photo-opacity, 0.92);
}

.hero-cover .hero-texture {
  position: absolute; inset: 0;
  background-image: var(--hero-texture);
  background-size: var(--hero-texture-size, 96px);
  opacity: var(--hero-texture-opacity, 0.06);
  -webkit-mask-image: radial-gradient(ellipse 92% 90% at 74% 42%, #000 0%, transparent 70%);
          mask-image: radial-gradient(ellipse 92% 90% at 74% 42%, #000 0%, transparent 70%);
  pointer-events: none;
}

/* The marquee band must sit on the bottom edge of the FIRST SCREEN, whatever
   the window height. Matching the live site's fixed paddings can't do that:
   its hero is ~866px tall, so on any viewport shorter than that — including
   1536x864, one of the commonest scaled-laptop sizes — the band falls below
   the fold. Measured, not assumed. So the height is structural instead: the
   hero fills the viewport below the sticky navbar and the band, as the last
   child, is pinned to the bottom by construction rather than by arithmetic.
   svh (not vh) so a mobile URL bar doesn't push it off-screen. */
.hero-cover {
  display: flex; flex-direction: column;
  /* +1px: --nav-h is .navbar-inner's height, the navbar adds a 1px border-bottom */
  min-height: calc(100svh - var(--nav-h) - 1px);
}

/* the logo composition is gone, so the copy gets the full width */
.hero-cover .hero-inner {
  display: flex; flex-direction: column; justify-content: center;
  /* the base .hero-inner sets align-items:center for its GRID layout; in a
     flex column that becomes horizontal centering and collapses the copy
     column to its widest child. It has to be reset here. */
  align-items: flex-start;
  /* .container carries margin:0 auto; auto cross-axis margins beat `stretch`
     in a flex column and shrink this to fit-content, so the width is explicit */
  width: 100%;
  flex: 1 1 auto; position: relative; z-index: 1;
  padding-top: 56px;
  padding-bottom: 60px;
}
/* 665px, the live site's measured hero-copy width — the title breaks
   "Where / Culture / Comes to Life" on it the way the live site does.
   Subpage heroes share it too, so a lede wraps at the same measure the
   home hero uses instead of stretching across the full 1180px container. */
.hero-cover .hero-copy,
.page-hero-fold .hero-copy { width: 100%; max-width: 665px; }

/* Size and spacing matched to the live site's hero (website/styles.css).
   The title is capped by viewport HEIGHT as well as width: at the live site's
   full 84px the hero needs an 869px viewport (measured), so on anything
   shorter the type has to give — otherwise it is the band that gives. Scaling
   on vh keeps that a smooth taper rather than a visible jump at a breakpoint. */
.hero-cover .hero-title {
  font-size: clamp(34px, min(6.6vw, 8.6vh), 84px);
  line-height: 1.04;
}
/* Heavier, darker type so the lede and the Arabic line survive sitting on
   busy parts of the painted illustration (window mullions, tower entrance)
   without needing a backdrop behind them — a bold weight has thicker
   strokes and --ink is close to black, so both hold up on a light stripe
   or a dark one. */
.hero-cover .hero-lede {
  margin-top: 26px; font-size: 18px; font-weight: 700; color: var(--ink);
}
.hero-cover .hero-actions,
.page-hero-fold .hero-actions { margin-top: 38px; }
/* Full ink + bold text alone wasn't holding up on every part of the photo
   (it rides over both the light stone facade and the darker glass tower),
   so the Arabic line now sits on its own solid chip — same dark-pill
   treatment the About section's caption already uses — which guarantees
   contrast regardless of what's behind it, rather than betting on where
   the line happens to land. */
.hero-cover .hero-ar {
  margin-top: 34px; opacity: 1; font-weight: 600;
  display: inline-block;
  color: var(--paper);
  background: color-mix(in srgb, var(--ink) 90%, transparent);
  padding: 10px 18px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
}

/* both overlays are absolute inside .hero, so the strip has to be lifted */
.hero-cover .hero-strip { position: relative; z-index: 2; flex: none; }

/* The ghost button's thin border + text alone could get lost against the
   photo's own similarly-toned window mullions — give it a light backdrop
   so it reads as a button on any part of the image, not just the plainer
   patches. The solid button doesn't need this; its fill already carries
   its own contrast. */
.hero-cover .btn-ghost,
.page-hero .btn-ghost {
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(4px);
}
.hero-cover .btn-ghost:hover,
.page-hero .btn-ghost:hover { background: var(--accent); }

/* Short window: tighten the gaps too, so the taper above doesn't have to
   carry the whole difference on its own. */
@media (max-height: 880px) {
  .hero-cover .hero-inner { padding-top: 34px; padding-bottom: 34px; }
  .hero-cover .hero-lede { margin-top: 18px; font-size: 16.5px; }
  .hero-cover .hero-actions { margin-top: 26px; }
  .hero-cover .hero-ar { margin-top: 22px; }
}
@media (max-height: 780px) {
  .hero-cover .hero-inner { padding-top: 24px; padding-bottom: 24px; }
  .hero-cover .hero-lede { margin-top: 14px; font-size: 16px; }
  .hero-cover .hero-actions { margin-top: 20px; }
  .hero-cover .hero-ar { margin-top: 16px; }
}

/* the CANVAS composition: colour blocks + the logo mark */
.hero-canvas {
  position: relative;
  aspect-ratio: 1 / 1;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(6, 1fr);
  gap: 10px;
  padding: 18px;
  background: var(--canvas-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
}
.hero-canvas .blk { border-radius: var(--blk-radius, 4px); animation: blkIn 0.9s var(--ease-out) both; }
.hero-canvas .blk:nth-child(1) { grid-area: 1 / 1 / 3 / 3; background: var(--brand-1); animation-delay: 0.05s; }
.hero-canvas .blk:nth-child(2) { grid-area: 1 / 5 / 2 / 7; background: var(--brand-2); animation-delay: 0.13s; border-radius: var(--blk-radius) var(--blk-radius) 90px var(--blk-radius); }
.hero-canvas .blk:nth-child(3) { grid-area: 3 / 1 / 5 / 2; background: var(--brand-3); animation-delay: 0.21s; }
.hero-canvas .blk:nth-child(4) { grid-area: 5 / 2 / 7 / 4; background: var(--brand-4); animation-delay: 0.29s; border-radius: 999px var(--blk-radius) var(--blk-radius) var(--blk-radius); }
.hero-canvas .blk:nth-child(5) { grid-area: 4 / 6 / 7 / 7; background: var(--brand-5); animation-delay: 0.37s; }
.hero-canvas .blk:nth-child(6) { grid-area: 2 / 5 / 4 / 6; background: var(--brand-1); opacity: 0.45; animation-delay: 0.45s; border-radius: 999px; }

.hero-canvas .canvas-mark {
  grid-area: 2 / 2 / 6 / 6;
  place-self: center;
  width: 78%;
  filter: drop-shadow(0 14px 30px rgba(0,0,0,0.14));
  animation: markIn 1s var(--ease-out) 0.3s both;
}
@keyframes blkIn { from { opacity: 0; transform: scale(0.72); } to { opacity: 1; transform: none; } }
@keyframes markIn { from { opacity: 0; transform: translateY(18px) scale(0.9); } to { opacity: 1; transform: none; } }

.hero-strip {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  overflow: hidden;
}
.hero-strip-track {
  display: flex; align-items: center; gap: 22px;
  padding: 15px 0; white-space: nowrap;
  animation: strip 34s linear infinite;
  width: max-content;
}
/* Was a plain <span>, now an <a> — clickable through to that door's
   filtered course list, same as the program cards below. Reset to the
   text's own styling since an anchor doesn't carry it by default; :hover
   swaps to the program's own colour (--pc, same token the dot beside it
   already uses) so the row confirms it's clickable without a full colour
   change at rest, which would fight the strip's own quiet, muted read. */
.hero-strip-track a {
  font-family: var(--font-display);
  font-size: 14px; font-weight: 500;
  letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--muted); text-decoration: none;
  transition: color 0.2s;
}
.hero-strip-track a:hover { color: var(--pc); }
.hero-strip-track i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
@keyframes strip { to { transform: translateX(-50%); } }

/* ---------- about ---------- */

.about-grid { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 60px; align-items: center; }
/* The Story/Founder section's own photo panel, smaller than the shared
   .about-grid ratio (which index.html's homepage teaser and involved.html
   also use, unchanged) — scoped to #story only. */
/* President Message's kicker starts level with the photo's own top edge
   (start of frame), rather than .about-grid's shared vertical centering —
   the taller, denser copy block (3 paragraphs + stats) reads oddly
   floating above a shorter photo otherwise. */
#story .about-grid,
/* TTalks' own "About" block (ttalks.html) reuses the exact same narrower
   panel — on request, "same size of about image page" — rather than the
   wider shared .about-grid default. */
#about-ttalks .about-grid { grid-template-columns: 0.66fr 1.14fr; align-items: start; }
.about-media { position: relative; }
/* offset outline behind the panel, from the live site's .about-media::before —
   sized off .about-media's own box, which matches .about-swatch exactly
   since it's the only in-flow child (.about-caption is absolutely positioned) */
.about-media::before {
  content: ""; position: absolute;
  inset: 22px -22px -22px 22px;
  border: 1.5px solid var(--accent);
  border-radius: var(--radius-lg);
  z-index: -1;
}
/* a calm, single-tone panel — six competing colour blocks plus a mark
   on top read as clutter (flagged after seeing it rendered). Now a
   full-bleed illustration instead of a centred logo mark. */
.about-swatch {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  background: var(--surface-2);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}
/* hero-gathering.png is a wide (≈1.67:1) illustration going into a
   narrower, taller (4:5) box — object-fit:cover crops its sides down
   to a centred vertical strip. Nudge object-position if a redraw ever
   needs a different slice centred (e.g. more of the archway, less of
   the accent shapes at the far edges). */
.about-swatch-mark {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center;
}
.about-caption {
  position: absolute; left: 18px; bottom: -22px;
  background: var(--ink); color: var(--paper);
  padding: 12px 20px; border-radius: var(--radius-md);
  font-size: 12px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  box-shadow: var(--shadow-soft);
}
.about-caption [lang="ar"] { text-transform: none; letter-spacing: 0; }

.about-copy p { color: var(--muted); max-width: 560px; }
.about-goals { list-style: none; margin: 28px 0 36px; display: grid; gap: 12px; }
.about-goals li { display: flex; gap: 13px; align-items: flex-start; color: var(--ink); font-size: 15.5px; }
.about-goals li::before {
  content: ""; width: 11px; height: 11px; flex: none; margin-top: 8px;
  border-radius: 3px; background: var(--accent);
}
.about-goals li:nth-child(2)::before { background: var(--brand-1); }
.about-goals li:nth-child(3)::before { background: var(--brand-3); }
.about-goals li:nth-child(4)::before { background: var(--brand-4); }

.about-stats {
  display: flex; gap: 44px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 28px;
}
.stat { display: flex; flex-direction: column; }
.stat-num {
  font-family: var(--font-display); font-weight: 600;
  font-size: 38px; line-height: 1; color: var(--primary);
}
.stat-label { font-size: 11px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); margin-top: 8px; }

/* ---------- programs ---------- */

/* 2026-09-25: a single row that drifts on its own and can be pushed
   either way with the arrows either side of it — turned down live twice
   before landing here: first a bordered card grid, then a static
   honeycomb packing ("i m not satisfied with this... rotating infinite
   to left or right with arrow left and right of the row to show that it
   can control them"). The row itself is three back-to-back copies of
   the 12 doors (site.js's renderPrograms()) so it always has real cards
   to scroll into on either side; .carousel-viewport clips and edge-fades
   that strip down to one visible window. See initProgramCarousel() in
   site.js for the actual drive (autoplay / arrow-nudge / drag). */
.program-carousel {
  --door-size: clamp(84px, 8vw, 132px);
  position: relative;
  display: flex; align-items: center;
  gap: clamp(10px, 1.8vw, 22px);
  /* A paper tray under the whole row (arrows included) — lighter than
     this section's own --surface tint, the same relationship
     .calendar-card has to its section further down the page, so it
     reads as one raised, self-contained control rather than icons
     loose on the section background. It also gives the viewport's
     edge-fade something to dissolve INTO: the mask only fades the
     track's own opacity, so a faded card now reveals this tray's solid
     edge instead of trailing off into nothing. */
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-soft);
  padding: 10px 16px;
}
/* Scoped to Programs only — .section-head's 48px margin-bottom is shared
   by every other section on the page and stays as-is there; only the
   gap immediately above this particular row needed trimming. */
.programs .section-head { margin-bottom: 16px; }
.carousel-viewport {
  position: relative;
  overflow: hidden;
  flex: 1 1 auto; min-width: 0;
  /* Fades the two edges rather than cutting the row off hard — a card
     visibly dissolving mid-scroll reads as "more on either side", the
     same "there's a row here, not a fixed set of icons" signal the
     arrows give, just passive. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 52px, #000 calc(100% - 52px), transparent);
          mask-image: linear-gradient(to right, transparent, #000 52px, #000 calc(100% - 52px), transparent);
}
/* The "scanner" ring/magnifier that used to sit at the viewport's centre
   (a fixed circle framing whichever card scrolled under it, with its
   name in a pill above) was removed 2026-09-28, on request — "remove
   the circle in the middle, they doesn't like it". Recoverable from git
   history if it's ever wanted back (site.js's updateLens(), .carousel-
   lens/-back/-front/-label here). */
/* .program-track is the strip requestAnimationFrame translates — width:
   max-content so it's exactly as wide as its 33 cards, never wrapped.
   Generous vertical padding (rather than overflow:visible, which drops
   viewport's overflow-x:hidden along with it) gives the hover tooltip
   above a card, and the scale-up on hover, room inside the viewport's
   own clipped box so neither one is cut by the mask. */
.program-track {
  display: flex; align-items: center;
  gap: clamp(14px, 2.2vw, 30px);
  width: max-content;
  padding: 60px 4px 28px;
  cursor: grab;
  touch-action: pan-y; /* keeps the PAGE scrollable vertically on touch while a horizontal drag is captured */
  will-change: transform;
}
.program-track.is-dragging { cursor: grabbing; }
.carousel-arrow {
  flex: none;
  width: 48px; height: 48px; border-radius: 50%;
  display: grid; place-items: center;
  /* --surface, not --paper — the tray itself is --paper now, and a
     same-colour button on a same-colour card would read as flat unless
     you look closely at its border. */
  border: 1px solid var(--line); background: var(--surface);
  color: var(--primary); font-size: 26px; line-height: 1; font-family: var(--font-display);
  cursor: pointer;
  transition: background 0.25s var(--ease-out), color 0.25s var(--ease-out), border-color 0.25s var(--ease-out), transform 0.25s var(--ease-out);
}
.carousel-arrow:hover { background: var(--primary); color: var(--on-primary); border-color: var(--primary); transform: scale(1.07); }
.carousel-arrow:active { transform: scale(0.94); }
/* .program-card is an <a> (clickable through to that door's filtered
   course list — see site.js renderPrograms()) sized to exactly the
   circle — no card chrome, no caption in normal flow; see .program-tooltip
   below for the name. */
.program-card {
  position: relative;
  flex: none;
  width: var(--door-size); height: var(--door-size);
  text-decoration: none; color: inherit;
  transition: transform 0.3s var(--ease-out);
}
/* `.reveal`'s own transition SHORTHAND (further down this file) implicitly
   resets transition-delay to 0s for every property it lists — at equal
   specificity to plain `.program-card` it wins the tie by coming later in
   the file, so the entrance stagger below was silently never applying at
   all (confirmed: computed transitionDelay read "0s, 0s" regardless of
   --reveal-delay). Reads --reveal-delay (site.js) for the one-time
   entrance stagger only — see the comment on that inline property for why
   it isn't a plain inline `transition-delay` instead — and is reset to 0s
   on hover (below) so an icon responds immediately rather than waiting
   out its own entrance stagger. */
#programGrid .program-card { transition-delay: var(--reveal-delay, 0s); }
/* Scales in place, the way the watch face itself magnifies the icon under
   the crown/finger — it's expected to overlap its packed neighbours
   rather than push them aside, which is what z-index is for here. */
#programGrid .program-card:hover { transform: scale(1.16); z-index: 3; }
/* Higher specificity than the #programGrid .program-card rule above (that
   ID beats a plain :hover's extra class on its own — an ID always outranks
   any number of classes), or this reset would never actually win. */
#programGrid .program-card:hover { transition-delay: 0s; }
/* 2026-09-24: each door's own icon file (assets/icons/<id>.svg — the
   client's own artwork, a colour circle with a white pictogram baked in)
   shown exactly as authored — no recolouring, only scaled to fill the
   slot. A soft halo in the icon's OWN colour (--pc — see ICON_ACCENT in
   site.js, not the unrelated category token) blooms in on hover. */
/* A slow, gentle bob so the honeycomb doesn't sit completely still — each
   icon runs out of phase with its neighbours via --float-delay (site.js,
   a negative animation-delay so every icon starts mid-cycle rather than
   all rising together), so the row reads as loosely alive rather than one
   synchronized wave. Paused on hover so the scale + halo hover feedback
   below reads clearly instead of competing with a still-bobbing icon.
   prefers-reduced-motion already kills every animation site-wide (see the
   bottom of this file), so this needs no separate opt-out. */
@keyframes hive-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
/* Hover shake ("اهتزاز") — takes over from the idle bob the moment a
   pointer lands on the icon: a quick, small-amplitude wobble rather than
   a violent rattle, so it reads as a playful nudge, not an error state.
   Rotates around the chip's own centre (transform-origin below) so it
   wobbles in place instead of swinging from one edge. */
@keyframes hive-shake {
  0%, 100% { transform: rotate(0deg); }
  20%      { transform: rotate(-7deg); }
  40%      { transform: rotate(6deg); }
  60%      { transform: rotate(-5deg); }
  80%      { transform: rotate(3deg); }
}
.program-chip {
  width: 100%; height: 100%; border-radius: 50%;
  display: grid; place-items: center;
  box-shadow: 0 0 0 0 var(--pc);
  transition: box-shadow 0.4s var(--ease-out);
  animation: hive-float 3.6s ease-in-out infinite;
  animation-delay: calc(-1 * var(--float-delay, 0ms));
  transform-origin: center;
}
.program-card:hover .program-chip {
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--pc) 20%, transparent);
  animation: hive-shake 0.5s ease-in-out infinite;
}
.program-chip img { width: 100%; height: 100%; }
/* The name, shown only on hover — a caption under every icon needs a gap
   between rows for the text to sit in, which is exactly the gap packing
   them tight removes (same reason the watch grid itself labels nothing
   until you're on it). A small pill above the icon, not a plain label:
   the icon it belongs to is already unambiguous (it's the one growing
   under the pointer), so this only has to be legible, not point at
   anything. */
.program-tooltip {
  position: absolute; left: 50%; bottom: calc(100% + 10px);
  transform: translate(-50%, 4px);
  background: var(--ink); color: var(--paper);
  font-family: var(--font-display); font-size: 12.5px; font-weight: 700; letter-spacing: 0.02em;
  white-space: nowrap; padding: 6px 12px; border-radius: 999px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s var(--ease-out), transform 0.2s var(--ease-out);
  box-shadow: var(--shadow-soft);
}
.program-tooltip::after {
  content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--ink);
}
.program-card:hover .program-tooltip { opacity: 1; transform: translate(-50%, 0); }
/* Touch-only "tap to see the name, tap again to go" (site.js) — a finger
   has no :hover, so these mirror the hover rules above exactly, keyed to
   a JS-applied .is-tapped class instead. Wrapped in (hover: none) so a
   device that DOES have a mouse never picks this up even if something
   somehow applied the class — the same "gate on the media feature, not
   a device guess" rule the class is only ever added under in the first
   place. Desktop is completely untouched; nothing here has a plain
   `.program-card.is-tapped` rule outside this query. */
@media (hover: none) {
  #programGrid .program-card.is-tapped { transform: scale(1.16); z-index: 3; transition-delay: 0s; }
  .program-card.is-tapped .program-chip {
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--pc) 20%, transparent);
    animation: hive-shake 0.5s ease-in-out infinite;
  }
  .program-card.is-tapped .program-tooltip { opacity: 1; transform: translate(-50%, 0); }
}

/* ---------- events + calendar ---------- */

.events-layout { display: grid; grid-template-columns: 340px 1fr; gap: 40px; align-items: start; }

.calendar-card {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 22px;
  box-shadow: var(--shadow-soft);
}
.calendar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.cal-title { font-family: var(--font-display); font-size: 19px; font-weight: 500; letter-spacing: 0.05em; }
.cal-nav {
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line); background: none; cursor: pointer;
  color: var(--ink); transition: 0.25s;
}
.cal-nav:hover { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-dow { font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); text-align: center; padding-bottom: 6px; }
.cal-day {
  aspect-ratio: 1; display: grid; place-items: center; position: relative;
  font-size: 13px; border-radius: 9px; border: 1px solid transparent;
  transition: 0.2s;
}
.cal-day.is-out { color: color-mix(in srgb, var(--muted) 45%, transparent); }
.cal-day.has-events { cursor: pointer; font-weight: 800; }
.cal-day.has-events:hover { border-color: var(--primary); }
.cal-day.is-selected { background: var(--primary); color: var(--on-primary); }
/* --dc (site.js) is that day's category colour — the same --e-<id> token
   the matching filter chip and event card use, on request ("make the
   colors on the calendar the same as the filter one"). Falls back to
   --accent for a day holding more than one category under "All", where
   picking one colour would misrepresent the others. */
.cal-day .cal-dot { position: absolute; bottom: 5px; width: 5px; height: 5px; border-radius: 50%; background: var(--dc, var(--accent)); }
.cal-day.is-selected .cal-dot { background: var(--on-primary); }
.calendar-hint { font-size: 12px; color: var(--muted); margin-top: 14px; line-height: 1.5; }

.event-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; }
.filter-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 15px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--paper);
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); cursor: pointer; transition: 0.25s;
}
.filter-chip i { width: 9px; height: 9px; border-radius: 50%; background: var(--fc); }
.filter-chip:hover { border-color: var(--fc); color: var(--ink); }
.filter-chip.is-active { background: var(--fc); border-color: var(--fc); color: #fff; }
.filter-chip.is-active i { background: #fff; }

.events-list { display: grid; gap: 14px; }
.event-card {
  display: grid; grid-template-columns: 74px 1fr auto;
  gap: 22px; align-items: center;
  background: var(--paper); border: 1px solid var(--line);
  border-left: 5px solid var(--ec);
  border-radius: var(--radius-md); padding: 20px 22px;
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s;
}
.event-card:hover { transform: translateX(5px); box-shadow: var(--shadow-soft); }
.event-date { text-align: center; }
.event-date .ed-day { font-family: var(--font-display); font-size: 30px; font-weight: 600; line-height: 1; color: var(--ec); }
.event-date .ed-month { font-size: 10.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); margin-top: 4px; }
.event-info h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; margin-bottom: 5px; }
.event-blurb { font-size: 14px; color: var(--muted); max-width: 520px; }
.event-meta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 9px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: var(--muted); }
.event-meta .event-cat { color: var(--ec); }
.events-empty { padding: 40px; text-align: center; color: var(--muted); border: 1px dashed var(--line); border-radius: var(--radius-md); }

/* ---------- quote band ---------- */

.quote-band {
  background-color: var(--ink);
  /* The client's own repeating brand-mark outline (assets/pattern for
     website-01.svg — "01", not "02": 01 is a repeating tile, 02 is one
     big single watermark shape sized for a corner, not a texture). Its
     stroke is already a near-white cream, which is why the same file
     read as invisible on the light Programs section background (tried
     and reverted 2026-09-24) but shows up fine here — light-on-dark, not
     light-on-light. Its baked-in opacity was still lowered .23 -> .1 in
     the file itself (on request, "make it more transparent"), the same
     place the Programs attempt's colour/opacity were tuned before it —
     there's no per-layer CSS opacity to reach only the background-image
     and not the text sitting on top of it, so the file is the one place
     this can be tuned. */
  background-image: url("pattern for website-01.svg");
  background-repeat: repeat;
  /* Matches the file's own ~1.98:1 viewBox ratio (3036.34 / 1530.2 — the
     file was swapped for a new one under the same name 2026-09-27; the
     old ratio was ~2.45:1, so the tile height here changed to match and
     avoid stretching the new artwork) — width left at 420px, same scale
     the old tile was shown at. */
  background-size: 420px 212px;
  color: var(--paper); padding: clamp(56px, 7vw, 92px) 0; position: relative; overflow: hidden;
}
/* The two blurred accent/highlight circles this section used to carry
   (::before/::after) were removed on request, "remove the circles in
   the background" — the tiled pattern above is the section's texture
   now, and the two were starting to compete with each other. */
/* Centered on request — was start-aligned inside .container, reading as
   off-centre in a full-width dark band. margin:0 auto centers the block
   itself; text-align centers the quote AND its Arabic line together. */
.quote-band blockquote { max-width: 760px; margin: 0 auto; text-align: center; position: relative; z-index: 1; }
.quote-band p { font-family: var(--font-display); font-size: clamp(23px, 3.2vw, 38px); font-weight: 400; line-height: 1.3; }
.quote-band footer { margin-top: 18px; color: var(--highlight); font-size: 17px; }

/* ---------- news ---------- */

.news-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.news-card {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.news-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-soft); }
.news-thumb { aspect-ratio: 16 / 10; display: grid; place-items: center; background: var(--nc); }
.news-thumb svg { width: 46px; height: 46px; opacity: 0.9; color: #fff; }
.news-body { padding: 22px; }
.news-date { font-size: 10.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); }
.news-card h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; margin: 9px 0 8px; line-height: 1.25; }
.news-card p { font-size: 14px; color: var(--muted); }

/* ---------- auditorium ---------- */

.aud-layout { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: 56px; align-items: center; }
.stage-sketch {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 30px 26px 34px;
  box-shadow: var(--shadow-soft);
}
.stage-arc {
  height: 56px; border-radius: 999px 999px 6px 6px;
  background: linear-gradient(180deg, var(--primary), var(--primary-deep));
  display: grid; place-items: center;
  color: var(--on-primary); font-size: 11px; font-weight: 800;
  letter-spacing: 0.28em; text-transform: uppercase;
}
.seat-rows { display: grid; gap: 6px; margin-top: 24px; }
.seat-row { display: flex; gap: 5px; justify-content: center; }
.seat {
  width: 12px; height: 12px; border-radius: 3px;
  background: color-mix(in srgb, var(--primary) 22%, transparent);
}
.seat.is-vip { background: var(--accent); }
.seatmap-note { margin-top: 20px; font-size: 12.5px; color: var(--muted); display: flex; gap: 9px; align-items: center; }
.seatmap-note i { width: 10px; height: 10px; border-radius: 3px; background: var(--accent); flex: none; }

.aud-copy h3 { font-family: var(--font-display); font-size: clamp(22px, 3vw, 28px); font-weight: 700; margin-bottom: 14px; }
.aud-copy p { color: var(--muted); }
.aud-features { list-style: none; display: grid; gap: 11px; margin: 26px 0 32px; }
.aud-features li { display: flex; gap: 12px; align-items: center; font-size: 15px; }
.aud-features li::before { content: ""; width: 10px; height: 10px; border-radius: 3px; background: var(--highlight); flex: none; }

/* ---------- contact ---------- */

.contact-layout { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 40px; align-items: start; }
/* Single column by default — right for contact.html, where .contact-cards
   sits in the narrower half of .contact-layout beside the form. The
   homepage's own contact section is a short teaser now (full page moved
   to contact.html, 2026-09-22) with no form beside the cards, so it needs
   a row instead of a tall single column with nothing next to it.
   ⚠️ Scoped to .contact-teaser specifically, NOT `.contact.section` —
   contact.html's own <section> carries those same two classes (copied
   verbatim from the section this teaser was cut down from), so a bare
   `.contact.section .contact-cards` rule matched BOTH pages and crammed
   contact.html's cards into 3 columns squeezed inside .contact-layout's
   narrow 0.85fr half — 3 words per line, reported live 2026-09-22. */
.contact-cards { display: grid; gap: 14px; }
.contact-teaser .contact-cards { grid-template-columns: repeat(3, 1fr); }
.contact-card {
  background: var(--paper); border: 1px solid var(--line);
  border-left: 5px solid var(--cc, var(--primary));
  border-radius: var(--radius-md); padding: 22px 24px;
}
.contact-card h3 { font-size: 11px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase; color: var(--muted); margin-bottom: 9px; }
.contact-card p { font-size: 15.5px; }
.contact-card a:hover { color: var(--primary); }
.contact-note { font-size: 13px; color: var(--muted); margin-top: 6px; }

.contact-form {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 32px;
  box-shadow: var(--shadow-soft);
}
.contact-form h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; margin-bottom: 20px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.field { display: block; margin-bottom: 16px; }
.field span { display: block; font-size: 11px; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }
.field input, .field textarea, .field select {
  width: 100%; padding: 13px 15px;
  border: 1px solid var(--line); border-radius: var(--radius-md);
  background: var(--surface); color: var(--ink);
  font: inherit; font-size: 15px; transition: 0.22s;
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
  background: var(--paper);
}
.field textarea { resize: vertical; }
.form-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.form-status { font-size: 13px; color: var(--muted); }

/* ---------- legal pages (terms.html / privacy.html) ---------- */
/* A single readable text column — the one page type on this site that
   is plain prose rather than cards/grids, so it gets its own small
   type ramp instead of reusing a card or teaser style built for
   something else. */
.legal-doc { max-width: 720px; }
.legal-updated { color: var(--muted); font-size: 14px; margin-bottom: 40px; }
.legal-doc h2 {
  font-family: var(--font-display); font-size: 21px; font-weight: 700;
  margin: 40px 0 12px;
}
.legal-doc h2:first-of-type { margin-top: 0; }
.legal-doc p { color: var(--muted); line-height: 1.7; margin-bottom: 14px; }
.legal-doc ul { color: var(--muted); line-height: 1.7; margin: 0 0 14px 22px; }
.legal-doc li { margin-bottom: 6px; }
.legal-doc a { color: var(--primary); }
.legal-doc a:hover { text-decoration: underline; }

/* ---------- footer ---------- */

.footer { background: var(--ink); color: color-mix(in srgb, var(--paper) 78%, transparent); padding-top: 68px; }
/* .how-band and .involved-band are the two other --ink (dark navy)
   sections in this file, and on spaces/about/gallery one of them sits
   directly above the footer — same colour into same colour, with no edge
   between them at all (reported live 2026-09-22, with a line drawn on a
   screenshot marking exactly this boundary). The adjacent-sibling
   selector only matches those specific pages; academy/index/involved/news
   all have a light (--paper or --surface) section before the footer,
   which already reads as a boundary on its own and gets no extra line.
   Same subtle divider colour .footer-base already uses one section down,
   for the same "quiet line on dark" job. */
.how-band + .footer, .involved-band + .footer {
  border-top: 1px solid color-mix(in srgb, var(--paper) 14%, transparent);
}
.footer-inner { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.2fr; gap: 40px; padding-bottom: 52px; }
.footer-lockup { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
/* Icon and wordmark split apart here the same way the navbar's are, and for
   the same reason — the tiles can only animate as inline SVG. The wordmark
   is the warm-white twin (wordmark-horizontal-light.svg), since this sits on
   the dark footer. Sized to the 64px the combined lockup used to occupy. */
.footer-icon { width: 62px; height: 64px; flex: none; overflow: visible; }
/* Nudged down 3px on request. NOT an alignment fix — measured, the two
   pieces are already centred to within 0.6px of each other (icon ink sits
   0.37px above its box centre, the wordmark's 0.21px below). This is a
   deliberate optical offset, so don't "correct" it back to flush centre.
   transform rather than margin, so it shifts the letterforms without
   changing the flex row's height or the gap beside the icon. */
.footer-word { height: 38px; width: auto; flex: none; transform: translateY(3px); }
/* Matches the logo lockup's own rendered width (icon 62px + 14px gap +
   the ~88px wordmark image) so the description never runs wider than the
   brand mark sitting above it — it was set to an arbitrary 245px before,
   which let the text visibly overhang past the logo's right edge.
   margin-left: same idea, applied to the left edge. .footer-icon's SVG
   viewBox carries ~4.2px of dead space before the tiles' paint actually
   starts (measured via getBBox() against the viewBox), so the icon's own
   box (like .footer-tag's box) starts left of where its colour does — the
   text was flush with that empty box edge, sitting visibly left of the
   logo's real ink. */
.footer-tag { font-size: 14px; max-width: 200px; margin-left: 4px; text-align: left; }
.footer-col h4 { font-size: 13px; font-weight: 900; letter-spacing: 0.2em; text-transform: uppercase; color: var(--highlight); margin-bottom: 16px; }
.footer-col a, .footer-col p { display: block; font-size: 14.5px; margin-bottom: 9px; transition: color 0.2s; }
.footer-col a:hover { color: var(--paper); }
/* Just a layout wrapper now (2026-09-22) — the icons themselves are the
   exact same .nav-social-link markup the navbar uses (real SVGs, each
   platform's own logo-palette colour), not a second, plain-text-"Fb"/"Ig"
   design that used to live only here. Its own `a` styling is gone
   entirely rather than kept alongside: `.footer-social a:hover` (a class
   + type + pseudo-class) would outrank `.nav-social-facebook:hover` (two
   classes + pseudo-class) on specificity and wash every icon's hover back
   to one generic colour. */
.footer-social { display: flex; gap: 9px; margin-top: 12px; }
/* `.footer-col a { display: block }` above ALSO matches these icon links
   (they sit inside .footer-col too) and, being a class+type selector,
   outranks .nav-social-link's own `display: grid` (a single class) —
   silently falling back to block broke the place-items:center centering
   that grid was doing, and every icon's SVG rendered ~5px too high
   (ordinary inline-baseline placement instead of grid centering).
   Reported live 2026-09-22 as "the white glyph isn't centered in the
   circle" — measured as a uniform vertical offset across all four icons,
   confirming one shared cause rather than four separate ones. */
.footer-social .nav-social-link { display: grid; }
/* WhatsApp's own icon is 16px against the other three's 20px everywhere
   else (.nav-social-whatsapp svg, above) — a deliberate navbar choice
   from 2026-09-05, kept there. Here the four icons stand alone with
   nothing else beside them, so the size gap reads as uncalibrated rather
   than intentional (reported live 2026-09-22); scoped to the footer only,
   not a change to the shared rule. */
.footer-social .nav-social-whatsapp svg { width: 20px; height: 20px; }
.footer-base { border-top: 1px solid color-mix(in srgb, var(--paper) 14%, transparent); padding: 20px 0; font-size: 13px; }
.footer-base .container { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.footer-credit a { color: inherit; font-weight: 700; border-bottom: 1px solid color-mix(in srgb, var(--paper) 30%, transparent); transition: color 0.2s, border-color 0.2s; }
.footer-credit a:hover { color: var(--paper); border-color: var(--paper); }

/* ==========================================================
   SUBPAGE HEADER
   The homepage hero fills a screen; a subpage must not — the
   content is the point, so this is a compact band that states
   where you are and gets out of the way.
   ========================================================== */

.page-hero {
  position: relative; overflow: hidden;
  background: var(--hero-bg);
  border-bottom: 1px solid var(--line);
  padding: clamp(46px, 6vw, 78px) 0 clamp(40px, 5vw, 62px);
  /* the painting needs vertical room to read as a building rather than
     as a strip of masonry — below this the band is all texture, no subject */
  min-height: 380px;
  display: flex; align-items: center;
}
/* Same picture, same crop, same fade and the same opacity as the home
   hero now — every subpage runs the full-height .page-hero-fold band
   below, so there's no reason left for a separately tuned (brighter,
   flush-edged) version of the same photo. --page-hero-photo still lets a
   page hand this band its own crop instead of fighting one image's
   framing across two very different aspect ratios; position and opacity
   fall through to the shared rule's own defaults (center 38%, 0.5) —
   the exact values .hero-cover uses — rather than a second set of numbers
   living here. */
.page-hero .hero-photo {
  background-image: var(--page-hero-photo, var(--hero-photo, url("hero-building.jpg")));
}
/* Same dot/tile texture, same opacity, same fade mask as the home hero's
   .hero-cover .hero-texture — it used to run flat (no mask) at 70% of the
   home hero's opacity, which read as a visibly different, busier surface
   next to it. */
.page-hero::after {
  content: ""; position: absolute; inset: 0;
  background-image: var(--hero-texture);
  background-size: var(--hero-texture-size, 96px);
  opacity: var(--hero-texture-opacity, 0.06);
  -webkit-mask-image: radial-gradient(ellipse 92% 90% at 74% 42%, #000 0%, transparent 70%);
          mask-image: radial-gradient(ellipse 92% 90% at 74% 42%, #000 0%, transparent 70%);
  pointer-events: none;
}
.page-hero .container { position: relative; z-index: 1; }
/* Weight, letter-spacing and size cap now copy the home hero's
   .hero-cover .hero-title exactly (all three subpages run the same
   full-height .page-hero-fold band below, so there's the same room for
   the same scale) — one title treatment across the whole site instead
   of the subpages reading as a lighter-weight, smaller cousin of it. */
.page-hero-title {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(34px, min(6.6vw, 8.6vh), 84px); line-height: 1.04;
  letter-spacing: -0.01em;
  /* same construction as .hero-title: a plain text node (Gallery, Get
     Involved) is a single flex item and renders unchanged, but a title
     wrapped word-by-word in <span>s (About) stacks one word per line,
     matching the home hero's "Where / Culture / Comes to Life" pattern */
  display: flex; flex-direction: column;
  /* Big titles only, on request — was inheriting body's near-black --ink;
     now the same navy the logo itself uses, matching .hero-title. */
  color: var(--logo-ink);
}
/* Bold + full ink for the same reason the home hero's lede is: it now
   sits on the painted illustration, and muted grey at weight 400 stops
   being readable the moment it crosses a window or the tower. Size and
   spacing match .hero-cover .hero-lede. */
.page-hero-lede {
  /* 520px is .hero-lede's own measure — the subpage lede now wraps on the
     same line length the home hero's does instead of running 100px wider */
  margin-top: 26px; max-width: 520px;
  font-size: 18px; font-weight: 700; color: var(--ink);
}
/* `text-align:start` under dir="rtl" means the RIGHT edge — relevant even
   though the chip below is inline-block (so it hugs its own text rather
   than filling the copy column): it decides which side of that column the
   chip sits against, matching the home hero's .hero-ar. Font size matches
   too (it used to carry its own 620px cap and a 17px size, from before the
   copy column was bounded — both retired once the two aligned).
   Same dark-chip background as .hero-cover .hero-ar, and for the same
   reason: full ink text alone didn't hold up against every part of the
   photo, so this sits on a solid pill instead of betting on what's behind
   it. */
.page-hero-ar {
  margin-top: 34px;
  display: inline-block;
  font-family: var(--font-ar); font-weight: 600;
  font-size: 19px; text-align: start;
  color: var(--paper);
  background: color-mix(in srgb, var(--ink) 90%, transparent);
  padding: 10px 18px;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-soft);
}

/* Fold-pinned variant, applied to every -01 subpage hero (About, Gallery,
   Get Involved). Same construction as .hero-cover above, and for the same
   reason: fixed paddings can't put the marquee on the bottom edge of the
   FIRST SCREEN on every laptop, because that edge moves with the viewport —
   a 14" panel at 1536x864 and a 15.6" at 1920x1080 don't share one pixel
   height, and neither does the same laptop with the browser chrome shown
   or hidden. svh reads the actual viewport rather than a screen/device
   guess, so it tracks whatever the window turns out to be with no JS and
   no breakpoint table to maintain. */
.page-hero.page-hero-fold {
  display: flex; flex-direction: column;
  align-items: stretch;                 /* base .page-hero centers a row; reset for column */
  min-height: calc(100svh - var(--nav-h) - 1px);
  /* Was a fixed 56px, matching .hero-cover .hero-inner exactly — nudged
     down further on request, so .hero-cover (the homepage) is deliberately
     NOT touched here; only this subpage band moved. */
  padding-top: 84px;
  padding-bottom: 0;                    /* the strip supplies the bottom edge now */
}
.page-hero-fold .container {
  flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center;
  /* .container carries margin:0 auto, and an auto cross-axis margin beats
     `stretch` in a flex column — without an explicit width this collapses to
     fit-content and then centres itself, which parked every subpage's copy
     ~375px in from the left with a wide empty band beside it. Same fix, same
     reason as .hero-cover .hero-inner above; the home hero already had it. */
  width: 100%;
  align-items: flex-start;
  /* Back to `justify-content: center` (2026-09-28), reversing the
     2026-09-xx change noted below — on request, with side-by-side
     screenshots: the home hero's copy re-centres itself as the window is
     resized, while a pinned-to-top subpage hero stayed put and only grew
     empty space underneath, which read as "fixed" and inconsistent with
     the home page right next to it. Matching .hero-cover .hero-inner's own
     behaviour was the explicit ask, so every subpage hero now floats with
     the viewport's height exactly like the home hero does.
     [Superseded reasoning, kept for context: this was `flex-start` because
     centering made the gap above "← Back to home" grow with viewport
     height, so every subpage looked different depending on the visitor's
     screen. That's the trade-off being made again here, deliberately —
     visually matching the home hero now outweighs a fixed gap.] */
  padding-bottom: clamp(40px, 5vw, 62px);   /* moved off .page-hero's own padding */
}
.page-hero-fold .hero-strip { position: relative; z-index: 2; flex: none; }
/* Now that the title shares the home hero's up-to-84px cap, a short
   laptop window is exactly as tight here as it is on .hero-cover — same
   taper, same two breakpoints and the same padding values, so the title
   never outgrows the band. */
@media (max-height: 880px) {
  .page-hero.page-hero-fold { padding-top: 52px; }
  .page-hero-lede { margin-top: 18px; font-size: 16.5px; }
  .page-hero-fold .hero-actions { margin-top: 26px; }
  .page-hero-ar { margin-top: 22px; }
}
@media (max-height: 780px) {
  .page-hero.page-hero-fold { padding-top: 38px; }
  .page-hero-lede { margin-top: 14px; font-size: 16px; }
  .page-hero-fold .hero-actions { margin-top: 20px; }
  .page-hero-ar { margin-top: 16px; }
}

/* breadcrumb back to the homepage — also on the painting, so it carries
   its own weight rather than the usual muted grey. Home has no equivalent
   (it IS home), so this supplies its own gap down to the kicker below it
   rather than borrowing the title's margin the way it used to. */
.crumb {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12.5px; font-weight: 700; color: var(--primary);
  margin-bottom: 20px;
}
.crumb:hover { color: var(--accent); }

/* ---------- section head with an action on the right ---------- */

.section-head-row {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; margin-bottom: 40px;
}
.section-head-row .section-head { margin-bottom: 0; }

/* ---------- events: show more ---------- */

/* The count is inside the button on purpose: "Show more" on its own
   hides whether one event is left or twenty. */
.events-more { display: flex; justify-content: center; padding-top: 6px; }
.em-count {
  margin-inline-start: 9px; padding: 2px 9px; border-radius: 999px;
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  font-size: 10px; letter-spacing: 0.1em;
}
.btn-ghost:hover .em-count { background: color-mix(in srgb, #fff 22%, transparent); }

/* ==========================================================
   GALLERY
   Frames are EMPTY on purpose — see data.js. A dense grid with
   three cell shapes so it reads as a gallery rather than a
   product listing; `dense` lets a tall cell backfill the gap a
   wide one leaves.
   ========================================================== */

.gallery-grid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 190px; grid-auto-flow: dense; gap: 18px;
}
.shot { display: flex; flex-direction: column; min-width: 0; }
.shot.is-wide { grid-column: span 2; }
.shot.is-tall { grid-row: span 2; }

/* The homepage TEASER drops the spans. Mixed shapes are what makes the
   full gallery read as a gallery, but on a fixed slice of 8 they leave a
   ragged bottom edge — which looks like a bug in a teaser rather than
   like composition. An even 4x2 block reads as deliberate. */
.gallery-grid.is-even .shot.is-wide { grid-column: span 1; }
.gallery-grid.is-even .shot.is-tall { grid-row: span 1; }

.shot-frame {
  flex: 1; position: relative; overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--line);
  /* a flat grey would look like a broken image; a faint diagonal weave
     reads as a deliberately empty plate waiting for a photograph */
  background:
    repeating-linear-gradient(45deg,
      color-mix(in srgb, var(--ink) 4%, transparent) 0 10px,
      transparent 10px 20px),
    var(--surface-2);
  display: grid; place-items: center;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s, border-color 0.3s;
}
.shot:hover .shot-frame { transform: translateY(-4px); box-shadow: var(--shadow-soft); border-color: var(--sc); }
.shot-ph { opacity: 0.24; color: var(--ink); }
.shot-ph svg { width: 40px; height: 40px; display: block; }

/* The photo sits ON TOP of the placeholder weave, which stays put as the
   backdrop — so a slow connection shows the empty-frame look until the
   image decodes, and a missing file degrades to it rather than to a
   broken-image icon. */
.shot-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  display: block;
  transition: transform 0.5s var(--ease-out);
}
/* A gentle push-in on hover — the frame lifts, the picture inside it
   moves a little more, which is what reads as depth rather than as the
   whole tile sliding. overflow:hidden on .shot-frame does the cropping. */
.shot:hover .shot-img { transform: scale(1.05); }
/* the programme colour rides as a corner tab, not a fill — the frame has
   to stay neutral or it stops reading as an empty photo slot */
.shot-frame::after {
  content: ""; position: absolute; inset-inline-start: 0; top: 0;
  width: 4px; height: 44px; background: var(--sc);
  border-start-start-radius: var(--radius-md);
}
.shot-cap { padding: 10px 2px 0; display: flex; flex-direction: column; gap: 2px; }
.shot-title { font-family: var(--font-display); font-size: 15px; font-weight: 500; }
.shot-meta { font-size: 11.5px; color: var(--muted); }

/* ==========================================================
   TEAM
   Grouped by area, because a flat wall of faces says nothing
   about who does what.
   ========================================================== */

.team-group + .team-group { margin-top: 54px; }
.team-group-name {
  font-size: 11px; font-weight: 800; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 22px;
  padding-bottom: 10px; border-bottom: 1px solid var(--line);
}
.team-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px; }
.person { min-width: 0; }
.person-photo {
  aspect-ratio: 1; width: 100%; max-width: 132px; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--line);
  display: grid; place-items: center; margin-bottom: 16px;
  transition: transform 0.4s var(--ease-out), border-color 0.3s;
}
.person:hover .person-photo { transform: translateY(-4px); border-color: var(--primary); }
.person-photo:has(.person-img) { overflow: hidden; }
.person-img { width: 100%; height: 100%; object-fit: cover; }
.person-photo span {
  font-family: var(--font-display); font-size: 26px; font-weight: 500;
  letter-spacing: 0.06em; color: var(--primary); opacity: 0.65;
}
.person-role { font-family: var(--font-display); font-size: 17px; font-weight: 500; line-height: 1.25; }
.person-name { font-size: 12px; letter-spacing: 0.06em; color: var(--muted); margin: 4px 0 9px; }
.person-blurb { font-size: 13.5px; color: var(--muted); line-height: 1.55; }

/* ---------- facilities ---------- */

.facility-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.facility {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 24px 22px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.facility:hover { transform: translateY(-5px); box-shadow: var(--shadow-soft); }
.facility h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.facility-meta {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--highlight); margin: 6px 0 10px;
}
.facility p:last-child { font-size: 13.5px; color: var(--muted); line-height: 1.6; }

/* ---------- timeline ---------- */

.timeline { list-style: none; position: relative; padding-inline-start: 0; }
.timeline::before {                      /* the spine */
  content: ""; position: absolute; top: 8px; bottom: 8px;
  inset-inline-start: 86px; width: 2px;
  background: linear-gradient(180deg, var(--highlight), var(--line));
}
.milestone { position: relative; display: grid; grid-template-columns: 86px 1fr; gap: 34px; padding-bottom: 34px; }
.milestone:last-child { padding-bottom: 0; }
.ms-year {
  font-family: var(--font-display); font-size: 21px; font-weight: 600;
  color: var(--primary); text-align: end; padding-top: 1px;
}
.ms-body { position: relative; }
.ms-body::before {                       /* the node on the spine */
  content: ""; position: absolute; inset-inline-start: -41px; top: 8px;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--accent);
}
.ms-body h3 { font-family: var(--font-display); font-size: 19px; font-weight: 500; margin-bottom: 6px; }
.ms-body p { font-size: 14.5px; color: var(--muted); line-height: 1.6; max-width: 560px; }

/* ==========================================================
   GET INVOLVED — volunteer roles, internship tracks, connect
   ========================================================== */

.role-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.role-card {
  position: relative; overflow: hidden;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 26px 24px 28px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.role-card::before {
  content: ""; position: absolute; top: 0; inset-inline: 0; height: 3px;
  background: var(--rc); transform: scaleX(0); transform-origin: left;
  transition: transform 0.45s var(--ease-out);
}
.role-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-soft); }
.role-card:hover::before { transform: scaleX(1); }
.role-chip {
  width: 52px; height: 52px; border-radius: var(--chip-radius);
  display: grid; place-items: center; margin-bottom: 16px;
  background: color-mix(in srgb, var(--rc) 14%, transparent); color: var(--rc);
  transition: transform 0.4s var(--ease-out);
}
.role-chip svg { width: 30px; height: 30px; }
.role-card:hover .role-chip { transform: scale(1.07); }
.role-card h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.role-commit {
  display: inline-block; margin: 8px 0 11px; padding: 3px 11px;
  border-radius: 999px; background: var(--surface);
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--muted);
}
.role-card > p:last-child { font-size: 14px; color: var(--muted); line-height: 1.6; }

/* internship tracks — a list, not cards: they're read in order and
   compared on term length, which a row makes easy and a grid doesn't */
.track-list { display: flex; flex-direction: column; gap: 14px; }
.track {
  background: var(--paper); border: 1px solid var(--line);
  border-inline-start: 3px solid var(--primary);
  border-radius: var(--radius-md); padding: 20px 24px;
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s;
}
.track:hover { transform: translateX(5px); box-shadow: var(--shadow-soft); }
.track-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.track-head h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.track-term {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
}
.track > p { font-size: 14px; color: var(--muted); margin-top: 7px; line-height: 1.6; }

.offer-list { list-style: none; display: flex; flex-direction: column; gap: 13px; }
.offer-list li { position: relative; padding-inline-start: 30px; font-size: 15px; line-height: 1.55; }
.offer-list li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 7px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--highlight); box-shadow: 0 0 0 4px color-mix(in srgb, var(--highlight) 22%, transparent);
}

/* ---------- connect ---------- */

.connect-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.connect-card {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 28px 26px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.connect-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-soft); }
.connect-card h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.connect-card p { font-size: 14px; color: var(--muted); line-height: 1.6; flex: 1; }

/* newsletter — one field and a button, deliberately the shortest form
   on the site; anything more is a reason not to subscribe */
.subscribe { display: flex; gap: 12px; flex-wrap: wrap; max-width: 520px; margin-top: 22px; }
.subscribe input {
  flex: 1; min-width: 220px; padding: 15px 18px;
  border: 1px solid var(--line); border-radius: var(--btn-radius);
  background: var(--canvas-bg); font-size: 15px; color: var(--ink);
}
.subscribe input:focus { outline: none; border-color: var(--primary); }

/* ---------- membership tiers ---------- */

.tier-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; align-items: start; }
.tier {
  position: relative;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 30px 26px 28px;
  display: flex; flex-direction: column; gap: 12px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.tier:hover { transform: translateY(-6px); box-shadow: var(--shadow-soft); }
.tier.is-featured { border-color: var(--primary); box-shadow: var(--shadow-soft); }
.tier-flag {
  position: absolute; top: -11px; inset-inline-start: 26px;
  padding: 4px 12px; border-radius: 999px;
  background: var(--primary); color: var(--on-primary);
  font-size: 9.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
}
.tier h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; }
.tier-price { font-family: var(--font-display); font-size: 28px; font-weight: 600; color: var(--primary); }
.tier-perks { list-style: none; display: flex; flex-direction: column; gap: 9px; flex: 1; margin: 4px 0 8px; }
.tier-perks li { position: relative; padding-inline-start: 22px; font-size: 14px; color: var(--muted); line-height: 1.5; }
.tier-perks li::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 6px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--highlight);
}
.tier .btn { align-self: stretch; justify-content: center; }

/* ==========================================================
   HOMEPAGE: "get involved" band
   Three doors out to the Get Involved page. On the dark ink
   ground so it reads as a break in the page, not a fourth
   card grid.
   ========================================================== */

/* The "02" watermark pattern was tried here (top-right, then bottom-right
   to match a circled screenshot) and dropped on request, "i dont like
   it" — back to a plain --ink band, no background-image. */
.involved-band { background: var(--ink); color: var(--paper); }
.involved-band .kicker { color: var(--highlight); }
.involved-band .kicker::before { background: var(--highlight); }
.involved-band .section-title { color: var(--paper); }
.involved-band .section-sub { color: color-mix(in srgb, var(--paper) 72%, transparent); }
.involved-band .connect-card {
  background: color-mix(in srgb, var(--paper) 7%, transparent);
  border-color: color-mix(in srgb, var(--paper) 16%, transparent);
}
.involved-band .connect-card h3 { color: var(--paper); }
.involved-band .connect-card p { color: color-mix(in srgb, var(--paper) 70%, transparent); }
.involved-band .btn-ghost { color: var(--highlight); border-color: var(--highlight); }
.involved-band .btn-ghost:hover { background: var(--highlight); color: var(--ink); }

/* ==========================================================
   ACADEMY — course catalogue, class album, enrollment
   ==========================================================
   academy.html is the one page here reading REAL data (published
   Supabase courses), so every class name below is a contract with
   academy.js rather than something free to rename. --cc is the
   course's discipline colour, set inline per card from the
   category's own palette token.

   The old design expressed the album as rotated diamond tiles
   cut from the previous mosaic logo. That shape vocabulary went
   with the rebrand, so the album is re-cut here as soft-cornered
   photo tiles — the same treatment the gallery's frames use. */

/* academy.html inlines its category glyphs as a <defs> sheet; it must
   occupy no space. Not `display:none`, which in some engines stops the
   symbols resolving through <use>. */
.svg-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* auto-fit, not a fixed repeat(3,1fr): a fixed 3 columns meant a filtered
   view with 1-2 results (exactly what a program-card deep link produces —
   most disciplines have only one or two courses) stretched that lone card
   to a third of the row and left the rest as dead blank space. auto-fit
   with minmax collapses the columns nothing occupies, so the count of
   columns responds to both viewport width AND how many cards there are —
   300px keeps 3 columns fitting at the container's own 1180px width
   (3*300 + 2*26 gap = 952px), and it naturally drops to 2 then 1 column as
   the viewport narrows, replacing the two width-breakpoint overrides that
   used to force a column COUNT regardless of card count.
   justify-items: start, NOT the grid default of stretch — otherwise the
   fix above over-corrects: when auto-fit collapses the unused columns of a
   1-2 result row, their freed space doesn't vanish, it gets handed to the
   1fr track(s) that remain, so a single result stretched edge-to-edge into
   one giant card (reported live 2026-09-22 — a real screenshot, not the
   pane's own width-emulation artifact this session hit testing it).
   .course-card's own max-width is the actual fix; justify-items:start is
   what lets a card SMALLER than its (still-widened) track sit at that
   natural size instead of being forced to fill it. */
.course-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); justify-items: start; gap: 26px; }

.course-card {
  display: flex; flex-direction: column;
  width: 100%; max-width: 400px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 5px solid var(--cc);
  border-radius: var(--radius-md);
  padding: 24px 24px 22px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.course-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-soft); }

.course-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; }
.course-cat {
  font-size: 10px; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase;
  color: #fff; background: var(--cc); padding: 5px 11px; border-radius: 999px;
}
.course-level { font-size: 12px; font-weight: 600; color: var(--muted); }

.course-title { font-family: var(--font-display); font-size: 21px; font-weight: 700; line-height: 1.2; color: var(--ink); }
.course-title-ar { font-size: 14.5px; color: var(--muted); margin-top: 3px; }
.course-instructor { font-size: 13px; font-weight: 600; color: var(--cc); margin-top: 9px; }
.course-blurb { font-size: 14px; color: var(--muted); margin-top: 12px; }

.course-facts { list-style: none; margin: 18px 0 20px; border-top: 1px solid var(--line); }
.course-facts li {
  display: flex; justify-content: space-between; gap: 14px;
  padding: 9px 0; border-bottom: 1px solid var(--line); font-size: 13.5px;
}
.course-facts li span { color: var(--muted); }
.course-facts li strong { color: var(--ink); font-weight: 600; text-align: end; }

.course-foot { margin-top: auto; display: flex; flex-direction: column; gap: 14px; }
.course-seats { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--ink); }
.course-seats .diamond-bullet { width: 10px; height: 10px; color: var(--cc); flex: none; }
.course-seats.is-full,
.course-seats.is-full .diamond-bullet { color: var(--muted); }
.course-enroll { width: 100%; justify-content: center; }
.course-enroll[disabled] { opacity: 0.55; cursor: not-allowed; }
.course-enroll[disabled]:hover { transform: none; box-shadow: none; }

/* ---------- the class album, teaser on the card ---------- */

.course-album {
  display: flex; align-items: center; gap: 15px;
  width: 100%; margin: 16px 0 2px; padding: 8px 4px;
  background: none; border: 0; cursor: pointer; text-align: start;
  font-family: var(--font-text); color: inherit;
}
.course-album-tiles {
  position: relative; width: 60px; height: 48px; flex: none;
  transition: transform 0.45s var(--ease-out);
}
.course-album:hover .course-album-tiles { transform: scale(1.08) rotate(-3deg); }
.course-album-tile {
  position: absolute; width: 30px; height: 30px;
  border-radius: var(--radius);
  background: var(--cc);
  background-size: cover; background-position: center;
  box-shadow: 0 8px 18px -12px rgba(40, 70, 84, 0.6);
  display: grid; place-items: center;
}
/* A loose stagger rather than a grid: four snapshots dropped on a desk.
   Each tile is a different depth so the cluster reads as a stack. */
.course-album-tile.a1 { left: 0;    top: 9px;  transform: rotate(-7deg);  opacity: 0.45; }
.course-album-tile.a2 { left: 11px; top: 0;    transform: rotate(4deg);   opacity: 0.65; }
.course-album-tile.a3 { left: 22px; top: 14px; transform: rotate(-3deg);  opacity: 0.8; }
.course-album-tile.a4 { left: 30px; top: 3px;  transform: rotate(6deg); }
.course-album-tile svg { width: 16px; height: 16px; }

/* The discipline glyphs are <symbol>s nested inside a <g fill="none"
   stroke="currentColor"> in academy.html's defs sheet. <use> clones the
   SYMBOL, not that wrapper, so those two attributes never reach the copy and
   the strokes fall back to the page's ink — a near-black glyph on a coloured
   tile. Restating them here on the referencing <svg> is what carries them
   across, since inherited properties do cross into a use element's shadow
   tree. Inner dots keep their own fill="currentColor": a presentation
   attribute on the element itself outranks an inherited value. */
.course-album-tile svg,
.album-tile-lg.icon svg { fill: none; stroke: currentColor; color: #fff; }

.course-album-label { display: flex; flex-direction: column; gap: 3px; }
.course-album-kicker {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--muted);
}
.course-album-cta {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 600; color: var(--cc);
}
.course-album-cta svg { transition: transform 0.3s var(--ease-out); }
.course-album:hover .course-album-cta svg { transform: translateX(3px); }

/* ---------- the class album, open ---------- */

body.modal-open { overflow: hidden; }

.modal-overlay {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center; padding: 24px;
  background: color-mix(in srgb, var(--ink) 72%, transparent);
  opacity: 0; visibility: hidden;
  transition: opacity 0.35s var(--ease-out), visibility 0.35s;
}
.modal-overlay.is-open { opacity: 1; visibility: visible; }

.album-panel {
  position: relative;
  display: grid; grid-template-columns: 1.05fr 1fr;
  width: min(940px, 100%); max-height: min(88vh, 720px);
  background: var(--paper);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 40px 90px -30px rgba(40, 70, 84, 0.55);
  transform: translateY(18px) scale(0.98);
  transition: transform 0.4s var(--ease-out);
}
.modal-overlay.is-open .album-panel { transform: none; }

.modal-close {
  position: absolute; top: 14px; inset-inline-end: 14px; z-index: 2;
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: color-mix(in srgb, var(--paper) 85%, transparent);
  color: var(--ink); transition: 0.25s;
}
.modal-close:hover { background: var(--ink); color: var(--paper); }

/* Eight tiles on a 4x4 grid — positions are assigned by academy.js from a
   hash of the course id, so two classes never build the same mosaic. */
.album-mosaic {
  display: grid; gap: 6px; padding: 18px;
  grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr);
  background: var(--surface);
}
.album-tile-lg {
  border-radius: var(--radius); overflow: hidden;
  background: var(--cc); background-size: cover; background-position: center;
  display: grid; place-items: center;
  animation: albumTileIn 0.5s var(--ease-out) both;
}
.album-tile-lg.pos0 { grid-area: 1 / 1 / 3 / 3; animation-delay: 0.02s; }
.album-tile-lg.pos1 { grid-area: 1 / 3 / 2 / 4; animation-delay: 0.06s; }
.album-tile-lg.pos2 { grid-area: 1 / 4 / 2 / 5; animation-delay: 0.10s; }
.album-tile-lg.pos3 { grid-area: 2 / 3 / 3 / 5; animation-delay: 0.14s; }
.album-tile-lg.pos4 { grid-area: 3 / 1 / 4 / 2; animation-delay: 0.18s; }
.album-tile-lg.pos5 { grid-area: 3 / 2 / 5 / 4; animation-delay: 0.22s; }
.album-tile-lg.pos6 { grid-area: 3 / 4 / 5 / 5; animation-delay: 0.26s; }
.album-tile-lg.pos7 { grid-area: 4 / 1 / 5 / 2; animation-delay: 0.30s; }
.album-tile-lg.flip { transform: scaleX(-1); }
.album-tile-lg.textured { opacity: 0.55; }
.album-tile-lg.icon svg { width: 34%; max-width: 46px; }
/* the "level" stamp — the one tile carrying a word rather than a picture */
.album-tile-lg.quote { background: var(--ink); }
/* academy.js picks the flipped tile and the stamped tile from two independent
   slices of the same hash, so for some course ids they are the SAME tile — it
   only guards the stamp against colliding with the icon. Mirroring decorative
   texture was harmless in the old design; mirroring a word renders it
   backwards ("ᴙƎᴎᴎIƆƎᗺ"), which is how this surfaced. Text wins over flourish. */
.album-tile-lg.flip.quote { transform: none; }
/* The stamp lands on whichever cell the hash picks, and the narrow ones are a
   single grid cell — 68px at phone width. "BEGINNER" set at a fixed 11px is
   81px and spilled out of it (measured). The type scales down with the
   viewport, and `anywhere` is the backstop for the levels that still can't
   fit on one line there — "INTERMEDIATE" is a single unbreakable word, so
   without it the break can't happen at all and the tile overflows again. */
.album-tile-lg.quote span {
  font-size: clamp(8px, 1.5vw, 11px); font-weight: 700; letter-spacing: 0.1em;
  line-height: 1.25; overflow-wrap: anywhere;
  text-transform: uppercase; color: var(--paper); text-align: center; padding: 5px;
}
@keyframes albumTileIn { from { opacity: 0; transform: scale(0.82); } to { opacity: 1; } }

.album-info { padding: 40px 36px 34px; overflow-y: auto; }
.album-cat {
  display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: 0.15em;
  text-transform: uppercase; color: #fff; background: var(--cc);
  padding: 5px 12px; border-radius: 999px;
}
.album-title { font-family: var(--font-display); font-size: 27px; font-weight: 700; line-height: 1.15; margin-top: 16px; color: var(--ink); }
.album-title-ar { font-size: 16px; color: var(--muted); margin-top: 4px; }
.album-instructor { font-size: 14px; font-weight: 600; color: var(--cc); margin-top: 12px; }
.album-blurb { font-size: 15px; color: var(--muted); margin-top: 14px; }
.album-facts { list-style: none; margin: 22px 0 26px; border-top: 1px solid var(--line); }
.album-facts li {
  display: flex; justify-content: space-between; gap: 14px;
  padding: 10px 0; border-bottom: 1px solid var(--line); font-size: 13.5px;
}
.album-facts li span { color: var(--muted); }
.album-facts li strong { color: var(--ink); font-weight: 600; text-align: end; }

/* ---------- enrollment form ---------- */

/* The other -01 forms sit in a grid column that bounds them; this one is
   alone in its section, so it carries its own measure. */
.enroll-form { max-width: 720px; }
.field-small { max-width: 160px; }

/* ---------- responsive ---------- */

@media (max-width: 860px) {
  /* Side by side, the mosaic would take half a phone-width panel and leave
     the text in a gutter; stacked, it becomes the header image it reads as. */
  .album-panel { grid-template-columns: 1fr; max-height: 92vh; overflow-y: auto; }
  .album-mosaic { grid-template-rows: repeat(4, 54px); }
  .album-info { padding: 28px 24px 30px; overflow: visible; }
}
@media (max-width: 620px) {
  .field-small { max-width: none; }
}

/* Form outcome lines. academy.js and spaces.js both set .ok / .err on
   .form-status, and nothing in this file styled either — a refused request
   read in the same muted grey as "Sending…". */
.form-status.ok { color: var(--primary); font-weight: 600; }
.form-status.err { color: var(--accent); font-weight: 600; }

/* ==========================================================
   SPACES — venue catalogue, availability calendar, request
   ==========================================================
   spaces.html reads REAL data (public space categories and a
   room-level availability RPC), so the ids and the classes
   spaces.js generates are a contract, not free to rename.

   The old design gave every space type its own colour band.
   That is exactly the decorative colour rotation the client
   rejected for this site (see the palette notes), and a space
   type is not a discipline the way an Academy category is — so
   every card here is the one structural navy, with the accent
   kept for the two things that are information: capacity, and
   "this day is already booked". */

/* Several rules below set `display`, which outranks the UA's [hidden]. */
.space-grid[hidden], .space-empty[hidden], .avail-widget[hidden], .attendees-warning[hidden] { display: none; }

.space-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
.space-empty {
  padding: 40px; text-align: center; color: var(--muted);
  border: 1px dashed var(--line); border-radius: var(--radius-md);
}

.space-card {
  display: flex; flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 24px 24px 22px;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s;
}
.space-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-soft); }

.space-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 18px; }
.space-icon {
  width: 52px; height: 52px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--chip-radius);
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  color: var(--primary);
}
.space-icon svg { width: 30px; height: 30px; }
.space-type {
  font-size: 10.5px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); background: var(--surface);
  padding: 4px 11px; border-radius: 999px;
}

.space-name { font-family: var(--font-display); font-size: 22px; font-weight: 700; line-height: 1.2; color: var(--ink); }
.space-name-ar { font-size: 14.5px; color: var(--muted); margin-top: 3px; }
.space-blurb { font-size: 14px; color: var(--muted); margin-top: 12px; }
.space-features { list-style: none; display: flex; flex-wrap: wrap; gap: 7px; margin-top: 16px; }
.space-features li {
  font-size: 12px; font-weight: 600; color: var(--muted);
  background: var(--surface); padding: 3px 11px; border-radius: 999px;
}

.space-foot { margin-top: auto; padding-top: 20px; display: flex; flex-direction: column; gap: 14px; }
.space-capacity {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13.5px; font-weight: 700; color: var(--ink);
  padding-top: 14px; border-top: 1px solid var(--line);
}
.space-capacity i { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex: none; }
.space-reserve { width: 100%; justify-content: center; }

/* ---------- how it works ----------
   Same dark ink ground as the homepage's get-involved band, and for the
   same reason: it is a break in the page, not another card grid. Ochre
   carries the numerals because it can only carry text on a dark ground. */

.how-band { background: var(--ink); color: var(--paper); }
.how-band .kicker { color: var(--highlight); }
.how-band .kicker::before { background: var(--highlight); }
.how-band .section-title { color: var(--paper); }
.how-band .section-sub { color: color-mix(in srgb, var(--paper) 72%, transparent); }
.how-steps { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.how-step {
  background: color-mix(in srgb, var(--paper) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--paper) 16%, transparent);
  border-radius: var(--radius-lg); padding: 28px 26px;
}
.how-num {
  display: grid; place-items: center; width: 46px; height: 46px; margin-bottom: 18px;
  border: 1.5px solid color-mix(in srgb, var(--highlight) 60%, transparent);
  border-radius: var(--chip-radius);
  font-family: var(--font-display); font-size: 20px; font-weight: 700; line-height: 1;
  color: var(--highlight);
}
.how-step h3 { font-family: var(--font-display); font-size: 22px; font-weight: 700; color: var(--paper); margin-bottom: 8px; }
.how-step p { font-size: 14.5px; line-height: 1.6; color: color-mix(in srgb, var(--paper) 72%, transparent); }

/* ---------- the reservation modal ----------
   Shares .modal-overlay and .modal-close with the Academy's class album;
   only the panel is its own, since this one is a long form that scrolls. */

.space-modal-panel {
  position: relative;
  width: min(680px, 100%); max-height: min(90vh, 940px);
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--paper);
  border-radius: var(--radius-lg);
  padding: 40px 38px 34px;
  box-shadow: 0 40px 90px -30px rgba(40, 70, 84, 0.55);
  transform: translateY(18px) scale(0.98);
  transition: transform 0.4s var(--ease-out);
}
.modal-overlay.is-open .space-modal-panel { transform: none; }

.space-modal-type {
  display: inline-block; font-size: 10px; font-weight: 700; letter-spacing: 0.15em;
  text-transform: uppercase; color: var(--on-primary); background: var(--primary);
  padding: 5px 12px; border-radius: 999px;
}
/* padding clears the close button, which sits over this corner */
.space-modal-title {
  font-family: var(--font-display); font-size: 27px; font-weight: 700; line-height: 1.15;
  color: var(--ink); margin-top: 14px; padding-inline-end: 40px;
}
.space-modal-blurb { font-size: 15px; color: var(--muted); margin-top: 10px; }
.space-modal-blurb:empty { display: none; }
.space-modal-capacity {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 13.5px; font-weight: 700; color: var(--ink);
  margin: 14px 0 22px;
}
.space-modal-capacity::before { content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex: none; }
.space-modal-capacity:empty { display: none; }
.space-modal-form .form-note { font-size: 13px; color: var(--muted); margin-bottom: 18px; }

/* ---------- availability calendar ---------- */

.avail-widget {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: 18px 18px 16px; margin-bottom: 24px;
}
.avail-head { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 14px; }
.avail-month { font-family: var(--font-display); font-size: 17px; font-weight: 700; color: var(--ink); min-width: 160px; text-align: center; }
.avail-nav {
  width: 34px; height: 34px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--paper); color: var(--primary);
  font-size: 18px; line-height: 1; cursor: pointer; transition: 0.2s;
}
.avail-nav:hover { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.avail-loading { font-size: 13.5px; color: var(--muted); text-align: center; padding: 10px 0; }

.avail-room-picker { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 14px; }
.avail-room-picker span { font-size: 11px; font-weight: 800; letter-spacing: 0.15em; text-transform: uppercase; color: var(--muted); }
.avail-room-picker select {
  font: inherit; font-size: 14px; color: var(--ink);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: 8px 12px; max-width: 100%;
}

.avail-dow, .avail-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.avail-dow span { text-align: center; font-size: 10px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); padding-bottom: 6px; }
.avail-cell {
  position: relative; display: grid; place-items: center;
  min-height: 44px; width: 100%;
  background: var(--paper); border: 1px solid transparent; border-radius: 9px;
  font: inherit; color: var(--ink); cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.avail-cell:not(:disabled):hover { border-color: var(--primary); }
.avail-cell.avail-pad { background: transparent; cursor: default; }
.avail-cell.is-past { background: transparent; color: color-mix(in srgb, var(--muted) 45%, transparent); cursor: default; }
.avail-cell.is-selected { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
/* Booked days read like a hotel/professional booking widget now, on
   request — greyed out, a short diagonal strike through the day number,
   and genuinely unselectable (the button carries `disabled`, not just
   this styling) rather than the earlier plain accent dot, which visitors
   could still click straight through. */
.avail-cell.is-booked {
  background: var(--surface); color: color-mix(in srgb, var(--muted) 55%, transparent);
  cursor: not-allowed;
}
.avail-cell.is-booked .avail-daynum { position: relative; }
.avail-cell.is-booked .avail-daynum::after {
  content: ""; position: absolute; left: -4px; right: -4px; top: 50%;
  height: 1px; background: currentColor; transform: translateY(-50%) rotate(-14deg);
}
.avail-daynum { font-size: 13px; font-weight: 600; }
.avail-hint { font-size: 12px; line-height: 1.5; color: var(--muted); text-align: center; margin-top: 12px; }

.attendees-warning {
  font-size: 13px; font-weight: 600;
  color: color-mix(in srgb, var(--accent) 80%, black);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  border-radius: var(--radius-md); padding: 9px 13px; margin: -4px 0 18px;
}

/* The chips sit inside a .field, whose `.field span` rule would otherwise
   turn each one into a block-level, uppercase field label — hence the
   two-class selectors restating display, case and colour. */
.date-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.date-chips-empty { font-size: 13.5px; color: var(--muted); }
.date-chips .date-chip {
  display: inline-flex; align-items: center; gap: 6px; margin: 0;
  font-size: 13px; font-weight: 600; letter-spacing: normal; text-transform: none; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 999px; padding: 4px 5px 4px 12px;
}
.date-chip-remove {
  width: 22px; height: 22px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: none;
  color: var(--muted); font-size: 15px; line-height: 1; cursor: pointer; transition: 0.2s;
}
.date-chip-remove:hover { background: var(--accent); color: var(--on-accent); }

@media (max-width: 1080px) {
  .space-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 860px) {
  .how-steps { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .space-grid { grid-template-columns: 1fr; }
  .space-modal { padding: 10px; }
  .space-modal-panel { padding: 30px 18px 24px; max-height: 94vh; }
  .space-modal-title { font-size: 23px; }
  .avail-widget { padding: 14px 10px 12px; }
  .avail-month { min-width: 0; }
  .avail-cell { min-height: 38px; }
  .avail-daynum { font-size: 12px; }
}

/* ---------- reveal ---------- */

.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out); }
.reveal.is-in { opacity: 1; transform: none; }

/* ---------- responsive ---------- */

@media (max-width: 1040px) {
  .hero-inner { grid-template-columns: 1fr; }
  .hero-canvas { max-width: 460px; }
  .about-grid, .aud-layout, .contact-layout { grid-template-columns: 1fr; }
  .events-layout { grid-template-columns: 1fr; }
  .contact-teaser .contact-cards { grid-template-columns: repeat(2, 1fr); }
  .news-grid { grid-template-columns: 1fr 1fr; }
  .footer-inner { grid-template-columns: 1fr 1fr; }
  .facility-grid, .team-row { grid-template-columns: repeat(2, 1fr); }
  .role-grid, .connect-grid, .tier-grid { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}

/* Was 860px. Measured, not guessed: logo + 8 nav links + 4 social
   icons genuinely doesn't fit in one row until ~1210-1220px — below
   that, "Get Involved" wraps to two lines (Range.getClientRects()
   confirms 2 line boxes down to 1180px) and, worse, below ~1000px
   the social icons overflow the viewport entirely. overflow-x:hidden
   on body (further up this file) was hiding that as silent clipping
   rather than a visible scrollbar, so the icons were just gone with
   no sign anything was wrong. Moving the breakpoint here to 1240px
   (a safety margin above the measured 1220px) means there is no
   width where the nav is stuck half-fitting: it's either the full
   row with real room to spare, or this hamburger version, never the
   overflowing middle. Confirmed clean at 1220px and broken again by
   860px before this change — the numbers below are what to re-check
   if any nav content (a longer link label, a fifth social icon)
   changes and this ever needs re-measuring. */
@media (max-width: 1240px) {
  .nav-links {
    position: absolute; top: 76px; left: 0; right: 0;
    flex-direction: column; gap: 0;
    background: var(--paper); border-bottom: 1px solid var(--line);
    padding: 8px 28px 20px; display: none;
  }
  .nav-links.is-open { display: flex; }
  .nav-link { padding: 13px 0; border-bottom: 1px solid var(--line); }
  .nav-link-involved-mobile { display: block; }
  .nav-toggle { display: block; margin-left: auto; }
  .nav-cta { display: none; }
  .nav-social { display: none; }
}

@media (max-width: 640px) {
  .news-grid, .footer-inner, .form-row, .contact-teaser .contact-cards { grid-template-columns: 1fr; }
  .event-card { grid-template-columns: 60px 1fr; }
  .event-card .btn { grid-column: 1 / -1; justify-self: start; }
  .about-stats { gap: 26px; }

  .facility-grid, .role-grid, .connect-grid, .tier-grid { grid-template-columns: 1fr; }
  .team-row { grid-template-columns: repeat(2, 1fr); gap: 20px; }
  .person-photo { max-width: 96px; }

  .program-carousel { --door-size: clamp(68px, 20vw, 96px); gap: 8px; }
  .carousel-arrow { width: 38px; height: 38px; font-size: 21px; }
  .carousel-viewport {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(to right, transparent, #000 28px, #000 calc(100% - 28px), transparent);
  }

  /* Two columns, and the wide/tall spans are dropped: at this width a
     cell spanning 2 of 2 columns is a full-width band, which breaks the
     gallery rhythm instead of creating it. */
  .gallery-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 150px; gap: 12px; }
  .shot.is-wide { grid-column: span 1; }
  .shot.is-tall { grid-row: span 1; }

  /* The timeline's 86px year gutter plus a 34px gap leaves too little
     for the text, so the year moves above its entry and the spine goes. */
  .timeline::before { display: none; }
  .milestone { grid-template-columns: 1fr; gap: 6px; }
  .ms-year { text-align: start; }
  .ms-body::before { display: none; }

  /* The widest seat row is 24 seats; at the desktop size that is ~403px and it
     pushed the whole PAGE into a horizontal scroll. Shrink the seats instead of
     letting the sketch scroll — the seat map is a diagram, not something to read. */
  .stage-sketch { padding: 22px 16px 26px; }
  .seat { width: 7px; height: 7px; border-radius: 2px; }
  .seat-row { gap: 3px; }
  .seat-rows { gap: 4px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  .reveal { opacity: 1; transform: none; }
}
