/*
Theme Name: Spellbound
Theme URI: https://spellbound.example/
Author: Spellbound
Description: A custom WordPress theme for Spellbound — a charity programme helping people find their voice. Hand-built from designer-supplied HTML/CSS, with editorial typography, layered orb illustrations, and warm earth-tone palette.
Version: 1.0.0
Requires at least: 6.0
Tested up to: 6.5
Requires PHP: 7.4
License: GPL v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: spellbound
Tags: custom-theme, charity, editorial, full-width
*/

/* ── FONTS ── */

@font-face {
      font-family: 'AlteHaasGrotesk';
      src: url('assets/fonts/AlteHaasGrotesk.woff2') format('woff2'),
           url('assets/fonts/AlteHaasGrotesk.woff') format('woff');
      font-weight: 400;
      font-style: normal;
    }

@font-face {
      font-family: 'AlteHaasGrotesk';
      src: url('assets/fonts/AlteHaasGrotesk_Bold.woff2') format('woff2'),
           url('assets/fonts/AlteHaasGrotesk_Bold.woff') format('woff');
      font-weight: 700;
      font-style: normal;
    }

@font-face {
      font-family: 'PerfectlyNineties';
      src: url('assets/fonts/PerfectlyNineties-Regular.woff') format('woff');
      font-weight: 400;
      font-style: normal;
    }

@font-face {
      font-family: 'PerfectlyNineties';
      src: url('assets/fonts/PerfectlyNineties-Italic.woff') format('woff');
      font-weight: 400;
      font-style: italic;
    }

/* ── RESET ── */

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

/* ── VARIABLES ── */

:root {
      --bark:          #331C12;
      --olive:         #4D5E3C;
      --dusk:          #59748B;
      --dawn:          #FFF4AE;
      --spell:         #DDD7FF;
      --parchment:     #F7F7F7;
      --ink:           #1D1E1B;
      --white:         #ffffff;
      --green:         #4D5E3C;
      --container-max: 1300px;
      --pad-desktop:   60px;
      --pad-tablet:    30px;
      --pad-mobile:    20px;
      --nav-gap:       40px;
      --logo-height:   63px;
      --border-color:  #ffffff;
      --dot-size:      10px;
    }

/* ── BASE ── */

/* Clip horizontal overflow at the root as well as the body. body alone
   isn't enough because <html> is the page's scroll container, so a few
   wide/decorative elements could still stretch the page sideways. No
   position:sticky is used anywhere, so this is side-effect free; nested
   horizontal scrollers (alumni, trustees, photo-scroll) keep their own
   overflow and are unaffected. */
html {
      overflow-x: hidden;
    }

body {
      background: #111;
      font-family: 'AlteHaasGrotesk', sans-serif;
      overflow-x: hidden;
    }

/* Smooth scrolling for in-page anchor links — the homepage intro
   CTA's #choose jump, the Programme partnership pills (For
   Organisations / For Communities), the skip-to-content link, and
   any future ACF Link field the editor sets to `#section-id` all
   inherit smooth behaviour from this single declaration on <html>.

   The existing JS handler in assets/js/main.js (the `.subnav a`
   click listener) still wraps sub-nav anchors so their hash
   changes use history.replaceState instead of pushState — that's
   the one bit of behaviour CSS scroll-behavior can't replicate,
   and we keep it so repeated sub-nav clicks don't pile up history
   entries that would noisify the back button.

   Wrapped in `prefers-reduced-motion: no-preference` so visitors
   who've configured their OS for reduced motion (vestibular
   sensitivity, motion-triggered nausea, etc.) get the instant
   jump back. WCAG Success Criterion 2.3.3 / Apple HIG both
   identify this guard as the expected accessible shape — without
   it, we'd be overriding a user's explicit OS-level preference. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* ── PAPER TEXTURE OVERLAY ──────────────────────────────────────────
   Repeating paper-scan texture sits over the entire viewport via a
   fixed pseudo-element on <body>. The texture supplies the subtle
   creased-paper grain the design specifies; mix-blend-mode:multiply
   darkens content slightly wherever the texture has darker patches
   so the effect reads as "site printed on paper" rather than "site
   covered by paper".

   Critical rules:

   • pointer-events: none — the overlay sits ABOVE the entire page
     in the visual stack but is invisible to hit-testing, so every
     button / link / form field beneath it stays clickable. Without
     this rule the overlay would swallow every click.

   • z-index: 500 — high enough to overlay the site header (z:130),
     mobile menu (z:110), and all page content; low enough to sit
     BELOW the modal stack (z:9999–10000) so apply-now and
     watch-video modals render cleanly over the texture rather than
     getting tinted by it. The modal's own dark backdrop replaces
     the texture in the visible portion of the viewport while the
     modal is open.

   • mix-blend-mode: multiply — the texture is mostly white with
     darker fibre / crease patches. Multiply lets the white areas
     pass through (multiply by 1 = no change) while the darker
     patches darken the content beneath. The net effect is the
     grain of the paper bleeding into the page colours, not a
     "white veil" obscuring them.

   • opacity: 0.5 — tunes the strength of the effect. Lower = more
     subtle, higher = more obvious paper. Adjust to taste.

   • background-size: 400px — matches the source image's native
     width so the texture tiles at 1:1 without scaling artefacts.

   The film grain layer (body::before below) stacks ON TOP of this
   paper layer via a higher z-index, producing the "printed paper
   with film-grain shimmer" effect the design specifies.

   Note on performance: a fixed full-viewport element with mix-blend-
   mode triggers a compositor repaint of the whole viewport on
   scroll. On low-end hardware this can soften the scroll feel
   slightly. If profiling shows a measurable hit, the texture can be
   moved into the body's `background-image` with
   `background-blend-mode:multiply` instead — cheaper on the GPU
   but only visible in places where no section bg covers the body
   (so it wouldn't reach .post-content, .alumni-cards, etc).
   ───────────────────────────────────────────────────────────────── */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 500;
  pointer-events: none;
  background-image: url('assets/images/paper-texture.jpg');
  background-repeat: repeat;
  background-size: 400px auto;
  mix-blend-mode: multiply;
  opacity: 0.5;
}

/* ── FILM GRAIN OVERLAY ─────────────────────────────────────────────
   Fine-grain noise layer stacked above the paper texture via a
   second fixed pseudo-element (body::before). Same click-pass-
   through + sub-modal stacking pattern as the paper overlay; the
   only differences are:

   • z-index: 501 — exactly one step above body::after so the
     grain renders ON TOP of the paper, not under it. (Both sit
     well below the modal stack at 9999+ so modals stay clean.)

   • mix-blend-mode: soft-light — the standard choice for film
     grain. Soft-light brightens content where the grain is light
     and darkens it where the grain is dark, but with a much
     gentler curve than overlay — so the grain reads as shimmer
     across the surface rather than as a posterised stipple. The
     grain image has black + white speckles on a neutral mid
     ground, so soft-light produces the classic film-photograph
     shimmer.

   • background-size: 60px — the image's native size, giving a
     fine pixel-dense grain. Random noise hides its own repeat
     even at this small tile.

   • opacity: 0.08 — film grain wants to be felt more than seen.
     The very low alpha means the grain is barely-there shimmer
     rather than visible noise; push higher (0.15–0.25) for a
     more obviously photographic feel.
   ───────────────────────────────────────────────────────────────── */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 501;
  pointer-events: none;
  background-image: url('assets/images/film-grain.png');
  background-repeat: repeat;
  background-size: 60px auto;
  mix-blend-mode: soft-light;
  opacity: 0.08;
}

/* ── CONTAINER ── */

.container {
      max-width: var(--container-max);
      margin: 0 auto;
      padding-left: var(--pad-desktop);
      padding-right: var(--pad-desktop);
    }

@media (max-width: 1024px) {
      .container {
        padding-left: var(--pad-tablet);
        padding-right: var(--pad-tablet);
      }
    }

@media (max-width: 767px) {
      .container {
        padding-left: var(--pad-mobile);
        padding-right: var(--pad-mobile);
      }
    }

/* ═══════════════════════════════════════════════
       HEADER
    ═══════════════════════════════════════════════ */

#site-header {
      background-color: var(--green);
      width: 100%;
      position: relative;
      z-index: 130; /* above .mobile-menu (110) so burger + logo stay interactive */
      display: flex;
      align-items: stretch;
    }

/* Logo — flush to left page edge */

.header-logo {
      display: flex;
      align-items: center;
      flex-shrink: 0;
      position: relative;
      z-index: 1;
    }

.header-logo img {
      height: var(--logo-height);
      width: auto;
      display: block;
    }

/* Nav wrapper — fills remaining space, right-aligned */

.header-nav-wrapper {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: flex-end;
      padding-right: var(--pad-desktop);
      position: relative;
      z-index: 1;
    }

nav ul {
      list-style: none;
      display: flex;
      align-items: center;
      gap: 40px;
    }

nav ul li a {
      display: block;
      color: var(--white);
      text-decoration: none;
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      white-space: nowrap;
      padding: 22px 0;
    }

nav ul li a:hover {
      opacity: 0.75;
    }

/* Bottom border line */

.header-border-line {
      position: absolute;
      bottom: -1px;
      right: 0;
      height: 2px;
      background: var(--border-color);
      pointer-events: none;
      z-index: 2;
      /* width injected by JS */
    }

/* Default-template / unlisted pages (Privacy Policy, search, 404…) have
   no active primary-nav item, so the JS positioner falls back to the
   first link (Home) and the line would wrongly read as "homepage".
   Hide it entirely on those pages. */
body.spellbound-no-active-nav .header-border-line {
      display: none;
    }

.header-border-dot {
      position: absolute;
      top: 50%;
      left: 0;
      width: var(--dot-size);
      height: var(--dot-size);
      border-radius: 50%;
      background: var(--white);
      transform: translate(-50%, -50%);
    }

/* Burger — three spans that transform into a cross when open.
       The spans stay in the same three-span vertical layout; CSS
       rotates the top and bottom into an X and fades the middle. */

.burger {
      display: none;
      flex-direction: column;
      justify-content: center;
      gap: 5px;
      cursor: pointer;
      padding: 0 var(--pad-tablet);
      margin-left: auto;
      background: none;
      border: none;
      flex-shrink: 0;
      position: relative;
      /* z-index not needed — the whole header sits above the
         mobile menu via its own stacking context at z-index: 130 */
    }

.burger span {
      display: block;
      width: 24px;
      height: 2px;
      background: var(--white);
      transition: transform 0.3s ease, opacity 0.2s ease;
      transform-origin: center;
    }

/* Open state — animate into a cross.
       Each bar is 2px tall with 5px gap → centre-to-centre distance
       between top and bottom bars is 7px. To meet at the centre,
       the top bar translates +7px down and the bottom bar -7px up,
       then both rotate 45°. Middle bar simply fades out. */

.burger.is-open span:nth-child(1) {
      transform: translateY(7px) rotate(45deg);
    }

.burger.is-open span:nth-child(2) {
      opacity: 0;
    }

.burger.is-open span:nth-child(3) {
      transform: translateY(-7px) rotate(-45deg);
    }

/* Mobile menu — full-screen olive overlay with vertically
       stacked, centred nav links. Hidden by default; revealed
       when .is-open is toggled on the element. */

.mobile-menu {
      display: none; /* only ever shown at <=1024px via media query */
      position: fixed;
      inset: 0;
      background-color: var(--olive);
      z-index: 110;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.3s ease;
    }

.mobile-menu.is-open {
      opacity: 1;
      pointer-events: auto;
    }

.mobile-menu nav {
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }

.mobile-menu ul {
      list-style: none;
      margin: 0;
      padding: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 28px;
      text-align: center;
    }

.mobile-menu li {
      margin: 0;
      padding: 0;
    }

.mobile-menu a {
      display: block;
      padding: 0;
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 21px;
      letter-spacing: 0.02em;
      color: var(--parchment);
      text-decoration: none;
    }

.mobile-menu a:hover {
      opacity: 0.7;
    }

/* Header responsive */

@media (max-width: 1024px) {
      .header-nav-wrapper { display: none; }
      .header-border-line  { display: none; }
      .burger              { display: flex; }
      .mobile-menu         { display: block; }

      /* Sub-nav is a desktop-only construct. At the burger breakpoint the
         primary nav is hidden, so its anchor-X positioning has nothing to
         line up with — and the design has no separate mobile placement
         for it. Hide everywhere so it can't appear on touch. */
      .subnav-wrap         { display: none; }
    }

@media (max-width: 767px) {
      .burger { padding: 0 var(--pad-mobile); }
    }

@media (max-width: 450px) {
      :root { --logo-height: 43px; }
      .burger span { width: 20px; }
    }

/* ═══════════════════════════════════════════════
       HERO
    ═══════════════════════════════════════════════ */

.hero {
      position: relative;
      width: 100%;
      height: calc(100vh - var(--logo-height));
      overflow: hidden;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
    }

/* Background image */

.hero-bg {
      position: absolute;
      inset: 0;
      background: url('assets/images/hero-bg.jpg') center center / cover no-repeat; background-image: image-set(url('assets/images/hero-bg.webp') type('image/webp'), url('assets/images/hero-bg.jpg') type('image/jpeg'));
      z-index: 0;
    }

/* Orb — above bg */

.hero-orb {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      z-index: 1;
      line-height: 0;
    }

@media (min-width: 1025px) {
      .hero-orb { bottom: -50%; }
    }

.hero-orb img {
      width: 100%;
      display: block;
    }

/* Gradient fade — above orb, behind text.
       Covers bottom 35% and bleeds into the green section below */

.hero-fade {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 100%;
      height: 35%;
      background: linear-gradient(to top, var(--olive) 0%, rgba(77, 94, 60, 0) 100%);
      pointer-events: none;
      z-index: 2;
    }

/* Content — above everything */

.hero-content {
      position: relative;
      z-index: 3;
    }

/* Heading + logo wrapper */

.hero-text-logo {
      position: relative;
      width: 100%;
    }

@media (max-width: 1024px) {
      .hero-text-logo {
        display: flex;
        flex-direction: column;
        align-items: center;
      }
    }

/* Heading — absolutely positioned above the logo.
       On desktop, centred between 'b' and 'd' of the wordmark
       (approx midpoint ~76% from left). */

.hero-heading {
      position: absolute;
      bottom: 100%;
      left: 72.5%;
      transform: translateX(-50%);
      white-space: nowrap;
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-style: normal;
      font-size: 38px;
      color: var(--white);
      line-height: 1.2;
      padding-bottom: 4px;
    }

.hero-heading em {
      font-style: italic;
    }

/* Full-width wordmark */

.hero-logo {
      display: block;
      width: 100%;
      height: auto;
    }

/* Hero responsive */

@media (max-width: 1024px) {
      .hero-heading {
        position: relative;
        bottom: auto;
        left: auto;
        transform: none;
        font-size: 28px;
        text-align: center;
        margin-bottom: 30px;
      }
    }

@media (max-width: 767px) {
      .hero-heading {
        font-size: 22px;
        white-space: normal;
      }
    }

/* ── CONTAINER NARROW ── */

.container-narrow {
      max-width: 1100px;
      margin: 0 auto;
      padding-left: var(--pad-desktop);
      padding-right: var(--pad-desktop);
    }

@media (max-width: 1024px) {
      .container-narrow {
        padding-left: var(--pad-tablet);
        padding-right: var(--pad-tablet);
      }
    }

@media (max-width: 767px) {
      .container-narrow {
        padding-left: var(--pad-mobile);
        padding-right: var(--pad-mobile);
      }
    }

/* ═══════════════════════════════════════════════
       INTRO SECTION
    ═══════════════════════════════════════════════ */

.intro {
      position: relative;
      background-color: var(--green);
      overflow: hidden;
      padding: 270px 0 315px;
      /* Establish a local stacking context so the orb (z-index 1)
         and the content (z-index 2) inside this section stack
         predictably against each other, isolated from the rest of
         the page. Without this, the orb's transform — applied by
         the mouse-follow JS — creates its own stacking context
         that can paint above the content depending on what other
         stacking contexts surround the section. */
      isolation: isolate;
    }

/* Floating orb — JS moves this, offset left of centre */

.intro-orb {
      position: absolute;
      top: 50%;
      left: 85%;
      width: 320px;
      height: 320px;
      pointer-events: none;
      z-index: 1;
      will-change: transform;
      transition: transform 0.08s linear;
    }

.intro-orb img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

/* Content sits above orb */

.intro-content {
      position: relative;
      z-index: 2;
    }

/* Heading */

.intro-heading {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-style: normal;
      font-size: 38px;
      color: var(--white);
      line-height: 1.35;
      text-align: center;
      /* margin-bottom reduced from 150px in v1.26.2 — the CTA button
         added in this round sits between the heading and the stats
         row, so the heading no longer needs to carry the full
         heading-to-stats rhythm by itself. .intro-cta below supplies
         the bottom half of the gap. */
      margin-bottom: 50px;
    }

/* Intro CTA — the primary pill button between the intro paragraph
   and the stats row. Centred horizontally; its margin-bottom (plus
   the intro-heading's reduced margin-bottom above) recreates the
   original ~150px rhythm with the new element inserted. */
.intro-cta {
      display: flex;
      justify-content: center;
      margin: 0 0 90px;
    }

/* The intro-heading was originally a single <p class="intro-heading">
   element. The wysiwyg ACF field that now drives this content wraps
   its output in <p> via wpautop, so the live markup is
   <div class="intro-heading"><p>…</p></div>. The inner paragraph
   inherits typography from .intro-heading fine, but contributes its
   own default top/bottom margin which adds unwanted whitespace
   inside the wrapper. Reset to zero so the visual matches the
   original single-paragraph layout. */

.intro-heading p {
      margin: 0;
    }

.intro-heading em {
      font-style: italic;
    }

/* Stats row */

.intro-stats {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 40px;
      text-align: center;
    }

.stat-label {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--white);
      margin-bottom: 12px;
      display: block;
    }

.stat-number {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 120px;
      color: var(--white);
      line-height: 1;
      display: block;
    }

/* Intro responsive */

@media (max-width: 1024px) {
      .intro { padding: 120px 0 150px; }
      .intro-heading { font-size: 30px; margin-bottom: 40px; }
      .intro-cta { margin-bottom: 65px; }
      .intro-orb { width: 320px; height: 320px; }
      .stat-number { font-size: 90px; }
    }

@media (max-width: 767px) {
      .intro { padding: 90px 0 120px; }
      .intro-heading { font-size: 24px; margin-bottom: 30px; }
      .intro-cta { margin-bottom: 45px; }
      .intro-stats { gap: 20px; }
      .intro-orb { width: 220px; height: 220px; left: 85%; }
      .stat-number { font-size: 63px; }
      .stat-label { font-size: 10px; }
    }

/* ═══════════════════════════════════════════════
       SESSIONS SECTION
    ═══════════════════════════════════════════════ */

.sessions {
      position: relative;
      background-color: var(--green);
      padding: 0 0 180px;
    }

.sessions-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      background: var(--parchment);
      padding: 5px;
      gap: 0;
      border-radius: 4px;
    }

.session-card {
      padding: 48px 40px;
      display: flex;
      flex-direction: column;
      border: 1px solid var(--ink);
    }

/* No double borders — remove left border on cards 2 and 3 */

.session-card:not(:first-child) {
      border-left: none;
    }

/* Corner radii — desktop: card 1 left corners, card 3 right corners */

.session-card:first-child {
      border-radius: 2px 0 0 2px;
    }

.session-card:last-child {
      border-radius: 0 2px 2px 0;
    }

.session-title {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 32px;
      color: var(--ink);
      text-align: center;
      line-height: 1.2;
      margin-bottom: 24px;
    }

/* Session body styles target the paragraphs inside .session-card
   directly, not a `.session-body` class. The original markup put
   class="session-body" on each <p>, but the wysiwyg field that
   now drives this content emits bare <p> tags. Selecting via the
   parent .session-card matches any paragraph or <mark> the editor
   produces, regardless of class. The legacy `.session-body`
   selector is kept alongside so any older markup still works.

   text-indent: 15px gives the book-style indent that distinguishes
   consecutive paragraphs without needing blank lines between them
   (margin: 0). */

.session-card p,
.session-body {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 12px;
      color: var(--ink);
      line-height: 1.7;
      text-align: justify;
      text-indent: 15px;
      margin: 0;
    }

.session-btn {
      display: block;
      margin-top: 24px;
      padding: 16px 24px;
      border: 1px solid var(--ink);
      background: none;
      color: var(--ink);
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      text-align: center;
      text-decoration: none;
      cursor: pointer;
      transition: background 0.2s, color 0.2s;
    }

.session-btn:hover {
      background: var(--dawn);
      color: var(--ink);
    }

/* Dawn text highlight — inline marker effect. Same parent-scoped
   selector as the body rule above so it applies regardless of
   what classes the wysiwyg output uses. */

.session-card mark,
.session-body mark {
      background: var(--dawn);
      color: inherit;
      padding: 6px 3px 12px;
    }

/* Sessions responsive */

@media (max-width: 1024px) {
      .sessions { padding-bottom: 80px; }
      .session-card { padding: 36px 28px; }
      .session-title { font-size: 26px; }
    }

@media (max-width: 767px) {
      .sessions { padding-bottom: 60px; }
      .sessions-grid { grid-template-columns: 1fr; }
      .session-card {
        border-left: 1px solid var(--ink);
        border-top: none;
        border-radius: 0;
        padding: 32px 24px;
      }
      /* Restore left border on cards 2+ that was removed for desktop */
      .session-card:not(:first-child) {
        border-left: 1px solid var(--ink);
      }
      /* Mobile: top-left and top-right radius on first card */
      .session-card:first-child {
        border-top: 1px solid var(--ink);
        border-radius: 2px 2px 0 0;
      }
      /* Mobile: bottom-left and bottom-right radius on last card */
      .session-card:last-child {
        border-radius: 0 0 2px 2px;
      }
      .session-title { font-size: 28px; }
    }

/* ═══════════════════════════════════════════════
       TESTIMONIAL SECTION
    ═══════════════════════════════════════════════ */

.testimonial {
      position: relative;
      background-color: var(--olive);
      overflow: visible;
    }

/* Dusk-to-transparent gradient overlay — bottom upward */

.testimonial-gradient {
      position: absolute;
      inset: 0;
      background: linear-gradient(to top, var(--dusk) 0%, rgba(89, 116, 139, 0) 55%);
      pointer-events: none;
      z-index: 0;
    }

/* ── SECTION BOUNDARY ORB ──
       Zero-height element placed BETWEEN two sections in the DOM.
       Takes no space; the img is absolutely centred on the boundary line.
       z-index: 2 in root context sits above all section backgrounds
       (block elements, step 3) but below section content at z:3. */

.orb-divider {
      position: relative;
      height: 0;
      overflow: visible;
      pointer-events: none;
      z-index: 2;
      line-height: 0;
    }

.orb-divider img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      transform: translateY(-50%);
      display: block;
    }

/* Two-column inner layout — explicit 50/50 */

.testimonial-inner {
      position: relative;
      z-index: 3;
      display: grid;
      grid-template-columns: 50% 50%;
      align-items: stretch;
      min-height: 80vh;
      gap: 40px;
    }

/* Left — image sits naturally, no stretching */

.testimonial-left {
      display: flex;
      align-items: center;
      justify-content: center;
    }

.testimonial-image {
      display: block;
      max-width: 100%;
      height: auto;
    }

/* Right — layout column, no background here */

.testimonial-right {
      padding: 90px 0;
      display: flex;
      flex-direction: column;
      gap: 24px;
      /* justify-content: center — was flex-end before v1.26.2.
         Centring puts the quote + body + CTA stack vertically
         in the middle of the photo's height instead of dropping
         it against the bottom edge, matching the redesigned
         testimonial layout where the quote is no longer
         visually anchored to the ELOISE.png background-art that
         used to occupy the top half. */
      justify-content: center;
    }

/* Blockquote — ELOISE.png as background, quote padded clear of graphic */

.testimonial-quote {
      /* Background ELOISE.png art + its associated padding offsets
         were removed in v1.26.2 — the quote no longer composites
         over a portrait image, so the position:relative anchor is
         also gone (nothing left to layer against). The natural
         flow now positions the text directly inside its flex
         column. */
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 46px;
      color: var(--parchment);
      line-height: 1.25;
    }

.testimonial-quote-wrap {
      display: flex;
      flex-direction: column;
      gap: 16px;
    }

.testimonial-meta {
      display: flex;
      flex-direction: column;
      gap: 4px;
    }

.testimonial-meta-row {
      display: flex;
      gap: 24px;
      align-items: baseline;
    }

.meta-label {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 12px;
      color: var(--parchment);
      opacity: 0.7;
      min-width: 48px;
    }

.meta-value {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      color: var(--parchment);
      letter-spacing: 0.04em;
      text-transform: uppercase;
    }

.testimonial-body {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 12px;
      color: var(--parchment);
      line-height: 1.7;
      margin: 0;
      max-width: 60%;
    }

/* ── TESTIMONIAL CONTAINER ── */

.container-testimonial {
      max-width: 1200px;
      margin: 0 auto;
      padding-left: var(--pad-desktop);
      padding-right: var(--pad-desktop);
    }

/* Stacked testimonials (home_testimonials repeater): space each
   duplicated block apart. Single-testimonial pages are unaffected
   (no adjacent sibling); each block keeps its min-height: 80vh from
   .testimonial-inner. */
.container-testimonial + .container-testimonial {
      margin-top: 100px;
    }

@media (max-width: 1024px) {
      .container-testimonial {
        padding-left: var(--pad-tablet);
        padding-right: var(--pad-tablet);
      }
    }

@media (max-width: 767px) {
      .container-testimonial {
        padding-left: var(--pad-mobile);
        padding-right: var(--pad-mobile);
      }
    }

.testimonial-cta {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 20px;
      max-width: 60%;
    }

/* ═══════════════════════════════════════════════
       PILL BUTTON  (shared across CTAs and filter pills)
       Background artwork comes from a <img class="pill-bg">
       dropped in as a child of the button. Colour modifier
       (.pill--<colour>) only sets the text colour.
    ═══════════════════════════════════════════════ */

.pill {
      position: relative;
      display: inline-block;
      /* Consistent pill sizing across the theme. 240px fits the widest
         label currently in use ("Corporate Training", measured at ~235px
         rendered at 12px / 700 / 0.08em letter-spacing including the
         40px+40px horizontal padding) with a small buffer for kerning
         variance. min-width (rather than fixed width) lets a future
         longer label grow gracefully instead of clipping or wrapping —
         white-space: nowrap below would otherwise force overflow. */
      min-width: 240px;
      padding: 30px 40px;
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      text-decoration: none;
      white-space: nowrap;
      text-align: center;
      border: none;
      background: transparent;
      cursor: pointer;
    }

.pill-bg {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 125%;
      height: 125%;
      object-fit: contain;
      pointer-events: none;
      z-index: 0;
      /* Source artwork is a glow-on-black plate (uniform 572x216). Screen
         blend drops the black to transparent and additively brightens the
         underlying section with the centre colour, which is what gives the
         pill its luminous halo on parchment/olive/bark backdrops. */
      mix-blend-mode: screen;
    }

.pill > span {
      position: relative;
      z-index: 1;
    }

/* Text colour per image variant */

.pill--dawn,
    .pill--spell  { color: var(--ink); }

.pill--dusk,
    .pill--olive,
    .pill--bark   { color: var(--parchment); }

/* Tighter padding for the testimonial CTA */

.testimonial-cta .pill { padding: 30px; }

.testimonial-arrow {
      height: 12px;
      width: auto;
      display: block;
      flex-shrink: 0;
    }

/* Testimonial responsive */

@media (max-width: 1024px) {
      .testimonial-inner {
        grid-template-columns: 1fr 1fr;
        gap: 30px;
      }
      .testimonial-quote {
        /* Base rule (v1.26.2+) no longer carries a background or
           padding; only the responsive font-size step-down remains
           here. */
        font-size: 28px;
      }
      .testimonial-body  { max-width: 100%; }
      .testimonial-cta   { max-width: 100%; }
    }

@media (max-width: 767px) {
      .testimonial {
        padding-top: 90px;
      }
      .testimonial-inner {
        grid-template-columns: 1fr;
        min-height: auto;
        gap: 40px;
      }
      .testimonial-left { height: auto; }
      .testimonial-image { width: 100%; height: auto; max-width: 100%; }
      .testimonial-right { padding: 0 0 60px; }
      .testimonial-quote { font-size: 24px; }
      .testimonial-body  { max-width: 100%; }
      .testimonial-cta   { max-width: 100%; }
    }

/* ═══════════════════════════════════════════════
       PARTNERS SECTION
    ═══════════════════════════════════════════════ */

.partners {
      background-color: var(--dusk);
      padding: 120px 0;
      overflow: hidden;
      position: relative;
    }

.partners-row {
      overflow: hidden;
      margin-bottom: 40px;
      position: relative;
      z-index: 3;
    }

.partners-row:last-child {
      margin-bottom: 0;
    }

/* The track holds two identical sets of logos back-to-back.
       Animating to -50% (left) or from -50% to 0 (right) loops seamlessly. */

.partners-track {
      display: flex;
      align-items: center;
      width: max-content;
      gap: 100px;
    }

.partners-track--left {
      animation: marquee-left 42s linear infinite;
    }

.partners-track--right {
      animation: marquee-right 42s linear infinite;
    }

.partners-track:hover {
      animation-play-state: paused;
    }

.partner-logo {
      height: 30px;
      width: auto;
      display: block;
      flex-shrink: 0;
      opacity: 0.85;
    }

@keyframes marquee-left {
      from { transform: translateX(0); }
      to   { transform: translateX(-25%); }
    }

@keyframes marquee-right {
      from { transform: translateX(-25%); }
      to   { transform: translateX(0); }
    }

@media (max-width: 767px) {
      .partners { padding: 60px 0; }
      .partners-row { margin-bottom: 28px; }
    }

/* ═══════════════════════════════════════════════
       STORIES SECTION
    ═══════════════════════════════════════════════ */

/* NO z-index on .stories — avoids creating a stacking context that
       traps children. Orb and grid participate directly in root context. */

.stories {
      background-color: var(--dusk);
      padding: 120px 0 315px;
      position: relative;
      overflow: visible;
    }

/* Outer wrapper removed — z-index moved to stories-grid directly */

.stories-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 60px;
      position: relative;
      z-index: 3;
    }

/* ───────────────────────────────────────────────────────────────
   .story-card — homepage + archive card.

   Layout mirrors .resource-card on Learn:

     • Top half: featured image (aspect-ratio 572/228) with a
       CATEGORY (top-left) + date (top-right) overlay in parchment.
     • Bottom half: colour-bg body with serif title, optional
       description paragraph, and a CTA button pinned to the bottom
       (left-aligned). The CTA uses inherit/currentColor so it
       picks up the card's text colour automatically.

   The 5px outer padding + 1px inner frame creates the inset-border
   treatment used elsewhere on the site (.event-card, .alumni-card).
   ─────────────────────────────────────────────────────────────── */

.story-card {
  position: relative;
  border-radius: 4px;
  padding: 5px;
  display: flex;
  flex-direction: column;
}

/* 5 colour variants — applied to BOTH posts and videos.
   Variant for a given post is deterministic from its ID via
   spellbound_post_card_variant_for_id() in inc/post-card.php. */
.story-card--dusk  { background: var(--dusk);  color: var(--parchment); }
.story-card--spell { background: var(--spell); color: var(--ink); }
.story-card--dawn  { background: var(--dawn);  color: var(--ink); }
.story-card--olive { background: var(--olive); color: var(--parchment); }
.story-card--bark  { background: var(--bark);  color: var(--parchment); }
/* Parchment — added in v1.25.2 for the homepage's fixed
   (parchment/spell/olive/dawn) 4-card colour sequence. Treated as
   a "light card" like spell + dawn for hover purposes, so the
   button flip target below also lists it. The variant is NOT
   added to spellbound_post_card_variant_for_id's ID-based cycle
   in inc/post-card.php — that cycle stays at 5 colours so archive
   page cards keep their existing distribution. Parchment is
   reached only when a caller passes a fixed variant sequence
   (currently just the homepage's stories grid). */
.story-card--parchment { background: var(--parchment); color: var(--ink); }

/* Inner bordered rectangle — currentColor picks up the card's
   text colour so the frame reads parchment on dark cards
   (dusk / olive / bark) and ink on light cards (spell / dawn). */
.story-card-inner {
  border: 1px solid currentColor;
  border-radius: 2px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* Image area — top half. Fixed aspect-ratio so the card's image
   half is consistent regardless of the source image's dimensions.
   Dark fallback bg means cards without a featured image still
   render a deliberate surface for the CATEGORY/date overlay. */
.story-card-image {
  position: relative;
  aspect-ratio: 572 / 228;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--ink);
  border-bottom: 1px solid currentColor;
}

.story-card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* CATEGORY (left) + date (right) overlay sits at the top of the
   image. Always parchment colour because the overlay reads over
   a (presumed) dark/photographic image surface — not over the
   card body's variant colour. The .story-card-category PHP
   helper sends the category in its natural case; the uppercase
   transform is applied at output via strtoupper() in the
   markup helper (so screen readers get the natural case where
   available). */
.story-card-meta {
  position: absolute;
  top: 20px;
  left: 25px;
  right: 25px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  z-index: 2;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--parchment);
}

/* Body — bottom half. flex:1 + flex-direction:column lets the CTA
   pin to the bottom via margin-top:auto on .story-card-btn while
   the title and description sit naturally at the top of the body
   area. */
.story-card-body {
  padding: 25px 25px 28px;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.story-card-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(28px, 2.5vw, 36px);
  line-height: 1.15;
  margin: 0 0 14px;
  letter-spacing: -0.005em;
  color: inherit;
}
.story-card-title em { font-style: italic; }

.story-card-description {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  text-align: justify;
  margin: 0 0 24px;
  color: inherit;
}

/* CTA button — pinned to the bottom of the body via margin-top:auto.
   align-self:flex-start keeps it left-aligned at its natural width
   instead of stretching across the full body. Border + text pick
   up currentColor so the button matches the card's text colour;
   hover flips bg/text per variant via the rules below. */
.story-card-btn {
  display: inline-block;
  align-self: flex-start;
  margin-top: auto;
  padding: 12px 24px;
  border: 1px solid currentColor;
  background: transparent;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: inherit;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

/* Hover — flip to a contrasting fill per variant. Light cards
   (parchment, spell, dawn) flip to ink-bg/parchment-text; dark
   cards (dusk, olive, bark) flip to parchment-bg/ink-text. */
.story-card--parchment .story-card-btn:hover,
.story-card--spell .story-card-btn:hover,
.story-card--dawn .story-card-btn:hover {
  background: var(--ink);
  color: var(--parchment);
}
.story-card--dusk .story-card-btn:hover,
.story-card--olive .story-card-btn:hover,
.story-card--bark .story-card-btn:hover {
  background: var(--parchment);
  color: var(--ink);
}


/* ─── STORY CARD: COMPACT VARIANT ─────────────────────────────────
   Homepage-only no-image variant per Figma 1050:606.
   Applied via the `story-card--compact` class added by
   spellbound_render_story_card_markup when its $compact arg is
   true (currently passed only by the homepage stories grid).

   Visual differences from the standard imaged card:
     - Featured image is removed entirely (the <div class="story-card-image">
       wrapper is not rendered — saving a thumbnail fetch + the
       overlay markup).
     - Date moves from the image-overlay header into the top of
       the body section. Renders bold, ink-on-card-bg, with mild
       letter-spacing — no longer uppercase like the overlay
       variant (the Figma shows the natural "10.02.26" date format).
     - Title is larger (42px Perfectly Nineties at desktop scale)
       so it carries more visual weight as the card's primary
       hook. Italic emphasis via <em> in the headline is rendered
       in Perfectly Nineties' italic cut and is the design's
       trademark editorial flourish for this card.
     - Body description is justified with a 20px first-line indent
       — Figma's deliberate book-set treatment. Capped at 5 lines
       via -webkit-line-clamp as a visual safety net for any
       excerpt the editor pastes that's longer than the 35-word
       trim spellbound_render_story_card_post/_video already apply.
     - CTA button is full-width with its own bordered rectangle —
       reads as the card's primary action instead of a corner CTA.

   The 1px ink/currentColor frame inside .story-card-inner and the
   per-variant background/text colours are inherited unchanged
   from the base .story-card rules above. */

.story-card--compact .story-card-image {
  /* Image block isn't rendered when compact, but this guard
     ensures nothing weird shows up if a third party / future
     caller hands us a story-card--compact element that still
     contains an image child. */
  display: none;
}

.story-card--compact .story-card-body {
  /* Figma padding: 25px sides, 20–25px top/bottom inside the inner
     bordered rectangle. The image-on-top variant's 25/25/28 was
     fine; compact carries a touch more breathing room because
     the card is busier vertically without the image break. */
  padding: 26px 30px;
  /* Body owns the entire inner card height (no image taking
     half), so it needs flex column with the .story-card-btn
     pinned to the bottom via margin-top:auto. Already inherited
     from the base .story-card-body rule, but re-asserting here
     keeps the intent explicit and survives any future base
     tweaks. */
  display: flex;
  flex-direction: column;
}

/* Date — moved from the image overlay into the top of the body.
   The .story-card-meta rule above only applies inside .story-card-image
   (because of the <header class="story-card-meta"> wrapper),
   so the date here is unstyled by that rule and we set it
   from scratch. Uses `inherit` for colour so the date reads
   ink on light cards (parchment/spell/dawn) and parchment on
   dark cards (olive) — matches the body text it sits with. */
.story-card--compact .story-card-date {
  display: block;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.04em;
  text-transform: none;
  color: inherit;
  margin: 0 0 18px;
}

/* Title — Perfectly Nineties at 42px per Figma. clamp gives
   responsive shrinkage on narrow viewports while keeping the
   42px ceiling. */
.story-card--compact .story-card-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(28px, 3.2vw, 42px);
  line-height: 1.17;
  letter-spacing: -0.01em;
  margin: 0 0 24px;
}
.story-card--compact .story-card-title em {
  font-style: italic;
}

/* Body — Alte Haas Grotesk Regular (not Bold like the overlay
   meta), 14px / 22px line-height, justified, with a 20px first-
   line indent for the book-set look Figma calls for. Line-clamped
   to 5 to keep the card height stable across posts of varying
   excerpt length — combined with the 35-word PHP trim, this gives
   a reliable visual envelope. */
.story-card--compact .story-card-description {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: 14px;
  line-height: 22px;
  text-align: justify;
  text-indent: 20px;
  color: inherit;
  margin: 0 0 24px;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* CTA — full-width bordered button at the bottom of the card.
   Overrides the base inline-block / align-self:flex-start / small-
   padding pattern to render as the design's prominent block CTA.
   margin-top:auto (inherited from the base) pushes the button to
   the bottom when the description is shorter than the line-clamp
   ceiling. Hover/focus colours are inherited from the existing
   light-card / dark-card hover rules above. */
.story-card--compact .story-card-btn {
  display: block;
  align-self: stretch;
  width: 100%;
  padding: 19px 24px;
  font-size: 14px;
  letter-spacing: 0.06em;
}


/* ───────────────────────────────────────────────────────────────
   .story-card--video-bg — applied (in compact mode only) to video
   items in the homepage stories grid, so they read as photographic
   features alongside the solid-colour post cards.

   Composition (v1.28.4):
     • The cover image lives on the OUTER .story-card, so it
       extends edge-to-edge — including the 5px ring that's
       otherwise the variant-colour padding. The variant colour
       is hidden underneath the image (background-image paints
       over background-color in CSS).
     • A ::before pseudo-element on the OUTER .story-card holds
       an 80%-opaque parchment wash, covering the whole card to
       its rounded corners (border-radius: inherit). This includes
       both the 5px ring AND the inner content area — a uniform
       tonal pass across the whole card.
     • The 1px inner border on .story-card-inner sits 5px inset
       from the card edge, painted ABOVE the overlay via the
       inner element's z-index: 1, drawn over the image+overlay
       like a thin decorative frame line. So the border reads as
       a single 5px-inset rectangle on top of a uniformly
       parchment-overlay'd image.
     • Text + button border colours are forced to ink — the
       parchment overlay would clash with the variant-specific
       parchment text used on dark variants like olive/bark/dusk,
       and ink reads cleanly against parchment regardless of
       what's underneath.
     • The button hover state mirrors the parchment-variant card's
       hover (ink fill, parchment text), keeping a consistent
       contrast invert across all video-bg cards regardless of
       which variant colour they happen to also carry.

   URL delivery: PHP sets the cover URL via a CSS custom property
   `--video-bg-image` on the inline style attribute (rather than
   writing the url() directly into a background-image inline style)
   so this rule stays in CSS and only the URL value travels inline.
   ─────────────────────────────────────────────────────────────── */

.story-card--video-bg {
  /* Cover image fills the outer card edge-to-edge. The base
     .story-card already has position:relative + 5px padding +
     border-radius:4px; we just add the bg-image atop the variant
     colour (which is then visually hidden by the image cover). */
  background-image: var(--video-bg-image);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.story-card--video-bg .story-card-inner {
  /* z-index: 1 (v1.28.4) — promotes the inner element to its own
     stacking context above the ::before overlay below, so the 1px
     border on this element paints OVER the overlay rather than
     under it. Without this, the overlay (a positioned descendant
     with z-index: 0) would paint after the inner element's
     non-positioned border in CSS stacking order and obscure it. */
  position: relative;
  z-index: 1;
  color: var(--ink);
}

.story-card--video-bg::before {
  /* Parchment overlay on the OUTER .story-card (v1.28.4 — was on
     .story-card-inner through v1.28.3). Covers the whole card,
     including the 5px ring of cover image that's otherwise visible
     outside the inner element's border. The 1px inner border still
     reads as a decorative inset frame line because .story-card-inner
     above is lifted to z-index: 1, painting the border above this
     overlay.

     `border-radius: inherit` picks up the parent's `border-radius:
     4px` so the overlay matches the card's rounded corners rather
     than rendering as a sharp-cornered rectangle bleeding past them.
     Required because absolutely-positioned children aren't clipped
     by their parent's border-radius unless overflow:hidden is set
     on the parent (which would have side effects on other behaviour). */
  content: '';
  position: absolute;
  inset: 0;
  background: var(--parchment);
  opacity: 0.8;
  border-radius: inherit;
  pointer-events: none;
  z-index: 0;
}

/* Body content above the overlay. Redundant now that
   .story-card-inner above carries z-index: 1 (the body sits inside
   the inner's own stacking context, so it paints above the overlay
   regardless), but kept as explicit documentation of intent and as
   defence against any future change that removes the inner's
   z-index. */
.story-card--video-bg .story-card-body {
  position: relative;
  z-index: 1;
}

/* Force ink text + meta across all interior elements. The
   compact variant uses `color: inherit` on each of these so this
   single override on the wrapper cascades down. Listed explicitly
   for two reasons: defence against any future child-element rule
   that resets the inherit chain, and so the override is visible
   when grepping the file for any of these selectors. */
.story-card--video-bg .story-card-title,
.story-card--video-bg .story-card-description,
.story-card--video-bg .story-card-date {
  color: var(--ink);
}

/* Button hover invert — matches the parchment-variant card hover
   pattern (ink fill, parchment text). Specificity 0,2,0 equals the
   existing per-variant hover rules; later source order wins, so
   make sure this rule stays AFTER the variant hover block above. */
.story-card--video-bg .story-card-btn:hover {
  background: var(--ink);
  color: var(--parchment);
}


/* ─── Equal-height cards in the homepage stories grid (v1.28.3) ────
   Scoped to .stories-grid--compact so only the homepage instance is
   affected; the Stories archive grid (.stories-grid--archive) stays
   on the original auto-row sizing.

   Two halves to this fix:

   1. The CSS half (here): `flex: 1` on the body so the body stretches
      to fill the inner card vertically. Without this, the body sizes
      to its content height and any extra vertical space sits as gap
      at the bottom of the card — so even when the card's outer
      box is forced to a taller min-height (by the JS below), the
      body content (title + description + CTA) would clump at the top
      with empty space below. With it, the body fills the available
      space and the existing `margin-top: auto` on the CTA button
      bottom-pins it within that stretched body — titles top-align,
      descriptions sit in the middle, CTA buttons bottom-align
      cleanly across all four cards.

   2. The JS half (in assets/js/main.js, initEqualHeightStoriesGrid):
      measures each card's natural height and sets min-height on
      every card to the tallest. Re-runs on viewport resize.

      Why JS rather than pure CSS: CSS Grid's `align-items: stretch`
      only equalises cards WITHIN A ROW, and `grid-auto-rows: 1fr`
      doesn't actually equalise across rows when the grid container
      has no explicit height (the spec falls back to per-row max-
      content sizing in that case). So for a 2-column / 2-row
      layout where row 1 and row 2 can have different tallest cells,
      JS is the only reliable way to equalise all four to the global
      max.
   ─────────────────────────────────────────────────────────────── */

.stories-grid--compact .story-card-body {
  flex: 1;
}


@media (max-width: 1024px) {
  .stories-grid { gap: 40px; }
  .stories { padding-bottom: 225px; }
}

@media (max-width: 768px) {
  .stories { padding: 60px 0 120px; }
  .stories-grid { grid-template-columns: 1fr; gap: 24px; }
  .story-card-body { padding: 20px; }
  .story-card-meta { top: 16px; left: 18px; right: 18px; font-size: 13px; }
}

/* ═══════════════════════════════════════════════
       CHOOSE SECTION
    ═══════════════════════════════════════════════ */

.choose {
      position: relative;
      background-color: var(--bark);
      padding: 270px 0 150px;
      overflow: visible;
      z-index: 5;
    }

/* Seven white circles straddling the top border.
       Container extends 10px beyond each viewport edge so the
       first and last circles are exactly half off-screen. */

.choose-circles {
      position: absolute;
      top: 0;
      left: -10px;
      right: -10px;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      align-items: center;
      pointer-events: none;
      z-index: 3;
    }

.choose-circle {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: var(--white);
      flex-shrink: 0;
    }

/* Floating orb — inverted mouse/scroll movement */

.choose-orb {
      position: absolute;
      top: 50%;
      left: 20%;
      width: 320px;
      height: 320px;
      pointer-events: none;
      z-index: 1;
      will-change: transform;
      transition: transform 0.08s linear;
      line-height: 0;
    }

.choose-orb img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

/* Content above orb */

.choose-content {
      position: relative;
      z-index: 2;
    }

.choose-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
    }

/* Choose responsive */

@media (max-width: 1024px) {
      .choose { padding-top: 120px; }
      .choose-heading { font-size: 120px; }
      .choose-orb { width: 240px; height: 240px; }
    }

@media (max-width: 767px) {
      .choose { padding: 90px 0 80px; }
      .choose-heading { font-size: 72px; }
      .choose-orb { width: 180px; height: 180px; left: 15%; }
      .choose-circles { left: -10px; right: -10px; }
    }

/* ═══════════════════════════════════════════════
       CHOOSE IMAGES SECTION
    ═══════════════════════════════════════════════ */

.choose-images {
      background-color: var(--bark);
      padding: 0 0 0;
      position: relative;
      z-index: 5;
    }

.choose-images img {
      display: block;
      width: 100%;
      height: auto;
    }

/* ═══════════════════════════════════════════════
       PATHS MARQUEE SECTION
    ═══════════════════════════════════════════════ */

.paths {
      background-color: var(--bark);
      padding: 120px 0;
      overflow: hidden;
      position: relative;
      z-index: 5;
    }

.paths-track {
      /* Note (v1.27.4): the inter-item spacing now lives as a
       * `margin-right: 80px` on each `.path-item` below, not as
       * `gap: 80px` here. The reason is purely about marquee
       * seam math.
       *
       * With N items per pass duplicated to 2 passes (so 2N items
       * total), `gap` adds spacing BETWEEN items but not AFTER
       * the last item — so the track's natural width comes out to
       *   2N × item + (2N − 1) × gap
       * which is gap/2 shorter than two full passes laid head-to-
       * tail. The animation translates `0 → −50%`, but −50% of the
       * above width lands HALFWAY THROUGH the gap that should sit
       * between pass 1 and pass 2 — not at the start of pass 2.
       *
       * When the animation snaps back to 0 each cycle that gap/2
       * misalignment shows up as a visible 40px backwards jump in
       * the marquee. Replacing `gap` with per-item `margin-right`
       * makes the track width exactly 2N × (item + 80) so −50%
       * lands precisely on the seam between passes, and the loop
       * reads as truly seamless. */
      display: flex;
      align-items: center;
      width: max-content;
      animation: paths-left 54s linear infinite;
    }

.paths-track:hover {
      animation-play-state: paused;
    }

@keyframes paths-left {
      from { transform: translateX(0); }
      to   { transform: translateX(-50%); }
    }

/* Each heading/button pair */

.path-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 20px;
      flex-shrink: 0;
      /* Inter-item horizontal spacing — see the long note on
         .paths-track above for why this is margin-right per item
         rather than `gap` on the track. The trailing margin on
         the very last item of pass 2 doesn't show visually
         because the section has overflow:hidden; it exists to
         make the animation translate distance match the seam. */
      margin-right: 80px;
    }

/* Static mode reset: when the marquee isn't animating (1 or 2
   items, centred in the track via .paths--static .paths-track's
   `justify-content: center`), the last item's trailing
   margin-right would shift the centred group ~40px to the left
   of true centre. Clearing it on the static last child keeps
   the centring visually balanced. The animated case wants the
   trailing margin and so isn't affected — those tracks have 4+
   items and use the animation, not the static override. */
.paths--static .path-item:last-child {
      margin-right: 0;
    }

.path-heading {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 38px;
      color: var(--parchment);
      white-space: nowrap;
      line-height: 1;
    }

.path-heading em {
      font-style: italic;
    }

@media (max-width: 767px) {
      .paths { padding: 60px 0; }
      .path-heading { font-size: 28px; }
    }

/* ═══════════════════════════════════════════════
       FIND YOUR VOICE SECTION
    ═══════════════════════════════════════════════ */

.voice {
      position: relative;
      background-color: var(--bark);
      padding: 270px 0;
      overflow: hidden;
      z-index: 5;
    }

/* Voice section colour modifier. The base .voice rule defaults to
   bark (homepage); pages where the design calls for a different
   colour add the modifier class. About uses olive so the section
   reads as a soft sunset finale after the dusk-coloured values band. */
.voice--olive {
      background-color: var(--olive);
    }

/* Orb — bottom aligned, full width, above bg, behind content */

.voice-orb {
      position: absolute;
      bottom: -200px;
      left: 0;
      width: 100%;
      z-index: 1;
      line-height: 0;
      pointer-events: none;
    }

.voice-orb img {
      width: 100%;
      display: block;
    }

.voice-content {
      position: relative;
      z-index: 2;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 48px;
    }

.voice-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
    }

@media (max-width: 1024px) {
      .voice { padding: 80px 0 150px; }
      .voice-heading { font-size: 100px; }
      .voice-orb { bottom: 0; }
    }

@media (max-width: 767px) {
      .voice { padding: 60px 0 120px; }
      .voice-heading { font-size: 60px; }
    }


/* ─── VOICE SUBSCRIBE ─────────────────────────────────────────────
   A smaller "Stay up to date" heading + email-subscribe form
   sitting below the main voice CTA pill, still inside .voice.
   Reuses the work-with-us-form aesthetic (parchment field cell
   with uppercase ink text, dusk-on-hover submit) but in a single-
   row two-cell card — slim and self-contained so it doesn't
   compete visually with the giant voice headline above.

   The form posts to admin-post.php?action=spellbound_subscribe;
   the handler in inc/form-handler.php saves a Submission CPT post
   with form_type='subscribe' and emails the configured inbox. */

.voice-subscribe {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  margin-top: 24px;
  width: 100%;
  /* Cap the form width so the email field stays readable on wide
     screens; centred under the CTA via .voice-content's align-items. */
  max-width: 560px;
}

.voice-subscribe-heading {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 24px;
  line-height: 1.2;
  color: var(--parchment);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: center;
  margin: 0;
}

.subscribe-form {
  width: 100%;
}

/* Inset-border treatment matching the work-with-us-form-card:
   outer parchment band carries the 5px frame, inner card has the
   1px ink border. The light parchment card reads well against the
   dark bark .voice ground. */
.subscribe-form-card {
  background: var(--parchment);
  padding: 5px;
  border-radius: 4px;
}

.subscribe-form-row {
  display: flex;
  border: 1px solid var(--ink);
  min-height: 59px;
}

.subscribe-form-cell {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
}

.subscribe-form-cell--email {
  flex: 1 1 auto;
}

.subscribe-form-cell--submit {
  flex: 0 0 auto;
  /* The submit cell is intrinsically sized to the button text +
     padding; the email field's flex grows to fill the rest. The
     left border draws the divider between the two cells. */
  border-left: 1px solid var(--ink);
  min-width: 160px;
}

.subscribe-form-email {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 18px 24px;
  background: transparent;
  border: 0;
  outline: 0;
  border-radius: 0;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  -webkit-appearance: none;
  appearance: none;
}

.subscribe-form-email::placeholder {
  color: var(--ink);
  opacity: 0.5;
}

.subscribe-form-email:focus-visible {
  outline: 2px solid var(--dusk);
  outline-offset: -2px;
}

.subscribe-form-submit {
  width: 100%;
  height: 100%;
  min-height: 59px;
  margin: 0;
  padding: 0 24px;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  transition: background 0.25s ease, color 0.25s ease;
  -webkit-appearance: none;
  appearance: none;
}

.subscribe-form-submit:hover,
.subscribe-form-submit:focus-visible {
  background: var(--dusk);
  color: var(--parchment);
}

@media (max-width: 600px) {
  /* On narrow viewports the inline row layout puts both cells too
     tight to be tappable; stack into two rows with the divider
     becoming a top-border on the submit cell. */
  .subscribe-form-row {
    flex-direction: column;
    min-height: 0;
  }
  .subscribe-form-cell--email {
    min-height: 59px;
  }
  .subscribe-form-cell--submit {
    border-left: 0;
    border-top: 1px solid var(--ink);
    min-width: 0;
  }
  .voice-subscribe-heading {
    font-size: 20px;
  }
}


/* ═══════════════════════════════════════════════
       FOOTER
    ═══════════════════════════════════════════════ */

.footer {
      background-color: var(--olive);
      position: relative;
      overflow: visible;
      z-index: 5;
    }

/* 7 circles at the top — same pattern as choose section */

.footer-circles {
      position: absolute;
      top: 0;
      left: -10px;
      right: -10px;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      align-items: center;
      pointer-events: none;
      z-index: 3;
    }

.footer-circle {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: var(--white);
      flex-shrink: 0;
    }

/* Info bar — full width, own side padding, no max-width */

.footer-info {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 40px var(--pad-desktop) 0;
      width: 100%;
    }

.footer-left,
    .footer-right {
      display: flex;
      gap: 32px;
      align-items: center;
    }

.footer-left span,
    .footer-left a,
    .footer-right a {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--parchment);
      text-decoration: none;
      white-space: nowrap;
    }

.footer-left a:hover,
    .footer-right a:hover {
      opacity: 0.7;
    }

/* Logo — full width, pulled up 15px to overlap the info row.
   .footer-logo-wrap sits after .footer-info in the DOM, so the
   full-width wordmark paints over that 15px overlap strip. The
   artwork's top edge is transparent (nothing visible covers the
   text), but the <img> box would still swallow clicks on the
   Privacy Policy + social links beneath it. pointer-events: none
   lets those clicks pass through — the logo is decorative, not a
   link, so nothing here needs to be interactive. */

.footer-logo-wrap {
      margin-top: -15px;
      line-height: 0;
      pointer-events: none;
    }

.footer-logo-wrap img {
      display: block;
      width: 100%;
      height: auto;
    }

/* Footer responsive — stack at tablet and below */

@media (max-width: 1024px) {
      .footer-info {
        flex-direction: column;
        align-items: center;
        gap: 48px;
        padding: 50px var(--pad-tablet) 0;
        text-align: center;
      }
      .footer-left {
        flex-direction: column;
        gap: 8px;
        align-items: center;
      }
      .footer-right {
        flex-wrap: wrap;
        justify-content: center;
        gap: 16px;
      }
      .footer-logo-wrap { margin-top: 40px; }
    }

@media (max-width: 767px) {
      .footer-info { padding-left: var(--pad-mobile); padding-right: var(--pad-mobile); }
    }

/* ═══════════════════════════════════════════════
       FADE-IN REVEAL
    ═══════════════════════════════════════════════ */

.fade-up {
      opacity: 0;
      transition: opacity 0.7s ease;
    }

.fade-up.in {
      opacity: 1;
    }

/* ═══════════════════════════════════════════════
     ABOUT PAGE STYLES
     ═══════════════════════════════════════════════ */

/* ── FONTS ── */

/* ── RESET ── */

/* ── VARIABLES ── */

/* ── BASE ── */

/* ── CONTAINER ── */

/* ═══════════════════════════════════════════════
       HEADER  (same structure as homepage)
    ═══════════════════════════════════════════════ */

/* Bottom border line + dot (anchored to active nav item) */

/* Burger — three spans that transform into a cross when open.
       The spans stay in the same three-span vertical layout; CSS
       rotates the top and bottom into an X and fades the middle. */

/* Open state — animate into a cross.
       Each bar is 2px tall with 5px gap → centre-to-centre distance
       between top and bottom bars is 7px. To meet at the centre,
       the top bar translates +7px down and the bottom bar -7px up,
       then both rotate 45°. Middle bar simply fades out. */

/* Mobile menu — full-screen olive overlay with vertically
       stacked, centred nav links. Hidden by default; revealed
       when .is-open is toggled on the element. */

/* ═══════════════════════════════════════════════
       SUB-NAV (per-page in-page section links)

       The wrap is rendered on EVERY page (header.php emits it as a
       sibling of <header> inside .header-stack; programme renders its
       own wrap inline so the orb-divider can sit visually behind the
       pill). It contains ALL registered subnavs as siblings, with only
       the active page's `hidden` attribute removed — JS toggles those
       attributes on hover to swap which subnav is visible.

       Layout depends on whether the active page has a default subnav:

       • body.has-subnav (About, Programme, Learn) — wrap is part of the
         page layout: a 50px green strip below the header that pushes
         content down. Matches the original design.

       • everywhere else (Home, Stories, Watch …) — wrap collapses to
         height:0 with a transparent background, taking NO layout space.
         The .subnav child (position:absolute; top:0) renders at the
         wrap's top, which is right below the header — so on hover the
         pill appears as a floating dropdown without shifting the page
         content beneath. When the visitor moves away, JS re-applies the
         `hidden` attribute and the dropdown disappears with no layout
         change. This is the "highly styled dropdown menu" behaviour.

       Positioned so its LEFT edge aligns with the centre of the
       hovered/active nav link. JS sets the .subnav child's `left`.
    ═══════════════════════════════════════════════ */

/* The .header-stack wraps <header> and the always-rendered .subnav-wrap.
   Currently it adds no visual styling — the two children flow normally
   and the subnav-wrap's `height: 0` trick on no-default pages doesn't
   need a positioning context. The class exists structurally so the two
   can be scoped together for potential future shared behaviour (e.g.
   making the whole chrome sticky) without affecting the rest of the
   document. */
.header-stack {
      position: relative;
    }

.subnav-wrap {
      background-color: var(--green);
      position: relative;
      width: 100%;
      height: 50px;
      z-index: 50;
    }

/* Overlay mode (Home, Stories, Watch — any page without a current-page
   subnav). The wrap is rendered into the DOM on every page so that
   hover-preview works globally, but on pages without their own subnav
   it collapses to zero height and goes transparent — taking no layout
   space until the visitor hovers a primary-nav item that has a
   registered subnav. The .subnav child is already position:absolute
   (top:0 inside the wrap), so when un-hidden it appears as a 50px-tall
   strip floating immediately below the header, OVER the page hero
   rather than pushing it down. This avoids a layout shift on hover. */

body:not(.has-subnav) .subnav-wrap {
      height: 0;
      background-color: transparent;
      overflow: visible;
    }

.subnav {
      position: absolute;
      top: 0;
      /* left set by JS on desktop to align with header dot */
      height: 50px;
      display: inline-flex;
      align-items: center;
      gap: 40px;
      padding: 0 30px;
      background: var(--green);
      border-left: 1px solid var(--parchment);
      border-right: 1px solid var(--parchment);
      border-bottom: 1px solid var(--parchment);
      white-space: nowrap;
    }

/* Native HTML5 `hidden` attribute would normally apply `display: none`,
   but we override it with `display: none !important` to ensure it wins
   regardless of source-order or specificity quirks. The JS toggles the
   attribute directly (subnav.hidden = true/false) which is the most
   accessible approach — screen readers honour the attribute. */

.subnav[hidden] { display: none; }

.subnav a {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--white);
      text-decoration: none;
      transition: opacity 0.2s;
    }

.subnav a:hover { opacity: 0.75; }

/* Tablet & below: main nav is hidden, so subnav becomes
       a simple centered row below the burger header. */

/* ═══════════════════════════════════════════════
       STORY HERO
    ═══════════════════════════════════════════════ */

.story {
      position: relative;
      background-color: var(--olive);
      padding: 120px 0 80px;
      overflow: hidden;
    }

/* ── STORY / HERO ORB DIVIDER ──
       Placed BEFORE <section class="story"> in the DOM (mirrors
       homepage .orb-divider pattern). Zero-height, takes no
       layout space; image absolutely positioned so its TOP edge
       sits at the story section's top edge. Image extends
       downward by its natural height into the story section
       (and beyond if it's tall enough).

       z-index: 2 sits above section backgrounds (default 0)
       but below anything with z-index: 3+ (content). */

.story-orb-divider {
      position: relative;
      height: 0;
      overflow: visible;
      pointer-events: none;
      z-index: 2;
      line-height: 0;
    }

.story-orb-divider img {
      position: absolute;
      top: 0;
      left: 50%;
      width: 100vw;
      transform: translateX(-50%);
      display: block;
    }

.story-content {
      position: relative;
      z-index: 3;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 60px;
      align-items: end;
    }

.story-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
    }

.story-copy {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 36px;
      line-height: 1.25;
      color: var(--parchment);
      padding-bottom: 12px; /* nudge off absolute bottom of heading descender line */
    }

/* ═══════════════════════════════════════════════
       TIMELINE  (horizontal carousel)

       Layout anatomy (per item):

         [ DATE ]
         ●  A Moment For A Key
         │  Milestone Achieved
       ──┼────────────────────── ← full-page-width axis,
         │                         midway between title
         │  Timeline body text…    block and body
         ●  ← bottom dot at end of body (variable height)

       • Arrows align with the container edges.
       • Axis spans full viewport width, rendered on the
         .timeline section (NOT the viewport/track).
       • Items use align-items: flex-start so bottom dots
         fall at each item's own natural content end.
    ═══════════════════════════════════════════════ */

.timeline {
      position: relative;
      background-color: var(--olive);
      padding: 30px 0 140px;
    }

/* Full-width horizontal axis, rendered on the section.
       Position is tuned to sit between title-block and body.
       z-index: 3 matches the timeline's other positioned children
       so the axis stays above the hero orb (z-index: 2) that can
       bleed through from above. */

.timeline-axis {
      position: absolute;
      top: 255px;
      left: 0;
      right: 0;
      height: 1px;
      background: var(--parchment);
      z-index: 3;
      pointer-events: none;
    }

/* Arrows — align with the container edges, above content */

.timeline-arrows {
      position: relative;
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 var(--pad-desktop);
      height: 20px;
      margin-bottom: 60px;
      z-index: 3;
    }

.timeline-arrow {
      position: absolute;
      top: 0;
      width: 40px;
      height: 20px;
      background: none;
      border: none;
      cursor: pointer;
      color: var(--parchment);
      padding: 0;
      z-index: 4;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.25s ease;
    }

.timeline-arrow-prev { left: var(--pad-desktop); }

.timeline-arrow-next { right: var(--pad-desktop); }

.timeline-arrow svg {
      display: block;
      width: 40px;
      height: 10px;
      overflow: visible;
    }

/* Stroke weight for the prev/next arrow lines. Set here rather than on
   each inline <line> so the triplicated arrow SVG (about, stories,
   partners) has one source of truth; CSS stroke-width overrides the
   presentation attribute on the <line> elements. */
.timeline-arrow svg line {
      stroke-width: 2;
    }

.timeline-arrow:hover { opacity: 0.7; }

.timeline-arrow[hidden],
    .timeline-arrow:disabled {
      opacity: 0;
      pointer-events: none;
    }

/* Viewport — clips the track horizontally.
       Left/right padding matches the container so items align with it. */

.timeline-viewport {
      position: relative;
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 var(--pad-desktop);
      overflow: hidden;
      z-index: 3;
    }

/* Track — holds all items, slides horizontally on arrow click */

.timeline-track {
      display: flex;
      align-items: flex-start; /* keep items naturally sized */
      gap: 60px;
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
      will-change: transform;
    }

/* Each item — fixed proportional width, stem on left,
       body extends as tall as it needs to. */

.timeline-item {
      flex: 0 0 calc((100% - 120px) / 3); /* 3 visible + 2 gaps */
      position: relative;
      padding-left: 32px;
      color: var(--parchment);
      box-sizing: border-box;
    }

/* Top dot — aligned with the DATE row, not the title.
       The date is 12px uppercase; the 11px dot sits with its
       top edge ≈ the date's cap-top so the dot-centre aligns
       with the date's vertical midpoint. */

.timeline-top-dot {
      position: absolute;
      top: 3px;
      left: 0;
      width: 11px;
      height: 11px;
      border-radius: 50%;
      background: var(--parchment);
      z-index: 2;
    }

/* Stem — from just below top dot (next to the date)
       all the way to bottom dot (end of body). */

.timeline-stem {
      position: absolute;
      top: 14px;
      bottom: 5.5px;
      left: 5px;
      width: 1px;
      background: var(--parchment);
      z-index: 2;
    }

/* Bottom dot — sits at the end of the body text.
       Left: -0.5px nudges the 11px circle so its centre column
       aligns with the 1px stem's column. */

.timeline-bottom-dot {
      position: absolute;
      bottom: 0;
      left: 0;
      width: 11px;
      height: 11px;
      border-radius: 50%;
      background: var(--parchment);
      z-index: 2;
    }

.timeline-date {
      display: block;
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--parchment);
      margin-bottom: 14px;
    }

.timeline-title {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 32px;
      line-height: 1.2;
      color: var(--parchment);
      margin-bottom: 64px; /* breathing room above the axis */
    }

.timeline-title em { font-style: italic; }

.timeline-body {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.7;
      color: var(--parchment);
      text-align: justify;
      margin: 0;
      padding-top: 20px; /* breathing room below the axis */
    }

/* Tablet: 2 visible items */
@media (max-width: 1024px) {
  .timeline { padding: 20px 0 100px; }
  .timeline-axis { top: 225px; }
  .timeline-arrows {
    padding: 0 var(--pad-tablet);
    margin-bottom: 50px;
  }
  .timeline-arrow-prev { left: var(--pad-tablet); }
  .timeline-arrow-next { right: var(--pad-tablet); }
  .timeline-viewport { padding: 0 var(--pad-tablet); }
  .timeline-item { flex: 0 0 calc((100% - 60px) / 2); }
  .timeline-title { font-size: 26px; margin-bottom: 50px; }
}

/* Mobile: 1 visible item */
@media (max-width: 767px) {
  .timeline { padding: 20px 0 80px; }
  .timeline-axis { top: 215px; }
  .timeline-arrows {
    padding: 0 var(--pad-mobile);
    margin-bottom: 40px;
  }
  .timeline-arrow-prev { left: var(--pad-mobile); }
  .timeline-arrow-next { right: var(--pad-mobile); }
  .timeline-viewport { padding: 0 var(--pad-mobile); }
  .timeline-item { flex: 0 0 100%; }
  .timeline-title { font-size: 24px; margin-bottom: 64px; }
  .timeline-body { padding-top: 30px; }
}

/* ═══════════════════════════════════════════════
       PHOTO SCROLL
       Controlled by the timeline arrows — moves one photo
       left/right when a timeline arrow is pressed.
       JS wires .photo-track's transform to the same index
       as .timeline-track (see initTimeline below).
    ═══════════════════════════════════════════════ */

.photo-scroll {
      background-color: var(--olive);
      padding: 60px 0 120px;
      overflow: hidden;
      position: relative;
    }

/* Viewport — same overflow clipping approach as timeline */

.photo-viewport {
      position: relative;
      overflow: hidden;
      /* Negative margin lets tilted cards overflow the viewport
         top/bottom without being clipped by overflow:hidden — we
         only want horizontal clipping. So use clip-path for x-only. */
      clip-path: inset(-200px 0 -200px 0);
      z-index: 3;
    }

/* Track — holds all photo cards, transform set by JS */

.photo-track {
      display: flex;
      align-items: center;
      gap: 0;
      padding: 60px 0; /* vertical room for tilted cards */
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
      will-change: transform;
    }

/* Cards overlap slightly so the row reads as a "stack of polaroids"
   rather than a row of separate frames. -40px right-margin on every
   card except the last creates a 40px overlap with the next card.
   The JS that drives this carousel reads marginRight via
   getComputedStyle and includes it in the per-step offset, so the
   scroll math stays accurate per click. */
.photo-card + .photo-card {
      margin-left: -40px;
    }

.photo-card {
      width: 360px;
      height: 460px;
      flex-shrink: 0;
      padding: 5px;
      background: var(--parchment);
      border-radius: 2px;
      position: relative;
    }

.photo-card-inner {
      position: relative;
      width: 100%;
      height: 100%;
      border: 1px solid var(--ink);
      border-radius: 2px;
      overflow: hidden;
      background-color: var(--ink);
    }

.photo-card-inner img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

/* Dot motif overlay — the visible dot grid is now gated by the
   per-section `data-show-dots="1"` attribute applied by the
   About template when the editor toggles "Show dot overlay" on.
   The selector rule itself lives further down with the shared
   image-card dot rule (search for `.image-card[data-show-dots`),
   so the photo scroll and the homepage galleries get a single,
   consistent dot pattern (7-column grid, parchment, anchored to
   edges) when enabled. */

/* Staggered rotations for scrapbook feel */

.photo-card:nth-child(5n+1) { transform: rotate(-6deg)  translateY(20px); }

.photo-card:nth-child(5n+2) { transform: rotate(4deg)   translateY(-15px); }

.photo-card:nth-child(5n+3) { transform: rotate(-3deg)  translateY(25px); }

.photo-card:nth-child(5n+4) { transform: rotate(8deg)   translateY(-5px); }

.photo-card:nth-child(5n+5) { transform: rotate(-8deg)  translateY(10px); }

/* ═══════════════════════════════════════════════
       IMPACT (bark gradient starts here)
    ═══════════════════════════════════════════════ */

.impact {
      position: relative;
      background-color: var(--olive);
      padding: 180px 0 120px;
      overflow: hidden;
      /* Local stacking context — see the matching comment on
         `.intro` for the why. Without this, the impact orb's
         transform (mouse-follow JS) can paint above the stats
         despite the explicit z-index ordering on its siblings. */
      isolation: isolate;
    }

/* Floating orb — mirrors homepage .intro-orb:
       anchored right-of-centre, mouse-follow (desktop) +
       scroll-drift (touch) driven by JS. Non-inverted
       movement (same direction as cursor / scroll). */

.impact-orb {
      position: absolute;
      top: 50%;
      left: 85%;
      width: 320px;
      height: 320px;
      pointer-events: none;
      z-index: 1;
      will-change: transform;
      transition: transform 0.08s linear;
    }

.impact-orb img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

.impact-content {
      position: relative;
      z-index: 2;
    }

.impact-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
      margin-bottom: 100px;
    }

.impact-stats {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 40px;
      text-align: center;
    }

.impact-stat-label {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--parchment);
      margin-bottom: 12px;
      display: block;
    }

.impact-stat-number {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 120px;
      color: var(--parchment);
      line-height: 1;
      display: block;
    }

/* ═══════════════════════════════════════════════
       QUOTE CAROUSEL
       Same arrow pattern as the timeline. On desktop,
       arrows sit vertically centred at the page edges.
       On mobile, arrows sit above the content (same as
       the timeline's mobile treatment).
    ═══════════════════════════════════════════════ */

.quote {
      background-color: var(--olive);
      padding: 80px 0 120px;
      position: relative;
    }

/* Desktop arrow wrapper — constrains arrows to container
       width, matching the timeline. Vertically centred on
       the section via absolute positioning + translate. */

.quote-arrows-desktop {
      position: absolute;
      top: 50%;
      left: 0;
      right: 0;
      transform: translateY(-50%);
      max-width: var(--container-max);
      margin: 0 auto;
      padding: 0 var(--pad-desktop);
      height: 20px;
      pointer-events: none; /* wrapper shouldn't block clicks */
      z-index: 4;
    }

.quote-arrow {
      position: absolute;
      top: 0;
      width: 40px;
      height: 20px;
      background: none;
      border: none;
      cursor: pointer;
      color: var(--parchment);
      padding: 0;
      pointer-events: auto; /* re-enable on the buttons themselves */
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.25s ease;
    }

.quote-arrow-prev { left: var(--pad-desktop); }

.quote-arrow-next { right: var(--pad-desktop); }

.quote-arrow svg {
      display: block;
      width: 40px;
      height: 10px;
      overflow: visible;
    }

.quote-arrow:hover { opacity: 0.7; }

.quote-arrow[hidden],
    .quote-arrow:disabled {
      opacity: 0;
      pointer-events: none;
    }

/* Viewport — clips horizontal scrolling of the track */

.quote-viewport {
      position: relative;
      z-index: 3;
      max-width: 900px;
      margin: 0 auto;
      overflow: hidden;
    }

.quote-track {
      display: flex;
      align-items: flex-start;
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
      will-change: transform;
    }

/* Each testimonial takes 100% of the viewport width */

.quote-slide {
      flex: 0 0 100%;
      text-align: center;
      padding: 0 20px;
      box-sizing: border-box;
    }

.quote-text {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 36px;
      line-height: 1.2;
      color: var(--parchment);
      margin: 0 auto 40px;
    }

.quote-text em { font-style: italic; }

.quote-meta {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      letter-spacing: 0.06em;
      color: var(--parchment);
      text-align: center;
    }

.quote-meta-name {
      font-weight: 700;
      text-transform: uppercase;
      display: block;
      margin-bottom: 4px;
    }

.quote-meta-cohort {
      font-weight: 400;
    }

/* Arrow wrapper for mobile — hidden on desktop, shown on mobile */

.quote-arrows-mobile {
      display: none;
    }

/* ═══════════════════════════════════════════════
       MEET OUR TEAM
    ═══════════════════════════════════════════════ */

.team {
      position: relative;
      background: linear-gradient(to bottom, var(--olive) 0%, var(--olive) 60%, var(--bark) 100%);
      padding: 80px 0 120px;
      /* overflow removed so team-orb-divider can extend up/down
         into neighbouring sections */
    }

.team-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
      margin-bottom: 80px;
      position: relative;
      z-index: 3;
    }

/* ── TEAM ORB DIVIDER ──
       Placed BETWEEN <section class="team"> and <section
       class="trustees"> in the DOM (mirrors homepage
       .orb-divider pattern). Zero-height, takes no layout
       space; image absolutely centred on the element's top
       edge (the section boundary). JS then translates the
       whole divider upward so the image lands visually
       between the first and second rows of the team grid.

       z-index: 2 sits above section backgrounds (default 0)
       but below anything in sections with z-index: 3+. */

.team-orb-divider {
      position: relative;
      height: 0;
      overflow: visible;
      pointer-events: none;
      z-index: 2;
      line-height: 0;
      will-change: transform;
    }

.team-orb-divider img {
      position: absolute;
      top: 0;
      left: 50%;
      width: 100vw;
      transform: translate(-50%, -50%);
      display: block;
    }

.team-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 40px;
      position: relative;
      z-index: 3;
    }

/* Individual team card — horizontal layout: photo left, text right */

.team-card {
      display: flex;
      padding: 5px;
      background: var(--parchment);
      border-radius: 4px;
      min-height: 380px;
    }

.team-card-photo {
      width: 40%;
      flex-shrink: 0;
      border: 1px solid var(--ink);
      border-radius: 2px 0 0 2px;
      overflow: hidden;
      background: var(--ink);
    }

.team-card-photo img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

.team-card-body {
      flex: 1;
      border: 1px solid var(--ink);
      border-left: none;
      border-radius: 0 2px 2px 0;
      padding: 30px;
      display: flex;
      flex-direction: column;
    }

.team-card-name {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 32px;
      line-height: 1.15;
      color: var(--ink);
      margin-bottom: 14px;
    }

.team-card-name em { font-style: italic; }

.team-card-title {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--ink);
      margin-bottom: 24px;
      display: block;
    }

/* Team / trustee card body — typography for paragraphs.
   The original markup put `class="team-card-copy"` on each <p>.
   The wysiwyg ACF field that now drives this bio content emits
   bare <p> tags, so the CSS selects on the parent `.team-card-body`
   to catch any paragraph, regardless of class. The legacy
   `.team-card-copy` selector is kept alongside for backwards
   compatibility with any older markup. Same pattern as the
   sessions / intro fix. */

.team-card-body p,
.team-card-copy {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.7;
      color: var(--ink);
      text-align: justify;
      text-indent: 15px;
      margin: 0 0 14px;
    }

.team-card-body p:last-child,
.team-card-copy:last-child {
      margin-bottom: 0;
    }

.team-card-body strong,
.team-card-copy strong {
      font-weight: 700;
    }

.team-card-body mark,
.team-card-copy mark {
      background: var(--dawn);
      color: inherit;
      padding: 2px 3px;
    }

/* ═══════════════════════════════════════════════
       MEET OUR TRUSTEES
    ═══════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════
       MEET OUR TRUSTEES
       Arrow-controlled carousel. The active card sits
       centered in the viewport; neighbour cards peek in
       from either side. Arrow pattern mirrors timeline
       (edges on desktop, above content on mobile).
    ═══════════════════════════════════════════════ */

.trustees {
      background-color: var(--bark);
      padding: 80px 0 160px;
      overflow: hidden;
      position: relative;
    }

.trustees-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
      margin-bottom: 60px;
      position: relative;
      z-index: 3;
    }

/* Arrows wrapper — container-aligned, like timeline */

.trustees-arrows {
      position: relative;
      max-width: var(--container-max);
      margin: 0 auto 60px;
      padding: 0 var(--pad-desktop);
      height: 20px;
      z-index: 3;
    }

.trustees-arrow {
      position: absolute;
      top: 0;
      width: 40px;
      height: 20px;
      background: none;
      border: none;
      cursor: pointer;
      color: var(--parchment);
      padding: 0;
      z-index: 4;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: opacity 0.25s ease;
    }

.trustees-arrow-prev { left: var(--pad-desktop); }

.trustees-arrow-next { right: var(--pad-desktop); }

.trustees-arrow svg {
      display: block;
      width: 40px;
      height: 10px;
      overflow: visible;
    }

.trustees-arrow:hover { opacity: 0.7; }

.trustees-arrow[hidden],
    .trustees-arrow:disabled {
      opacity: 0;
      pointer-events: none;
    }

/* Viewport spans full page width so off-centre cards
       can peek past the container edges. */

.trustees-viewport {
      position: relative;
      width: 100%;
      overflow: hidden;
      z-index: 3;
    }

.trustees-track {
      display: flex;
      align-items: stretch;
      gap: 40px;
      transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
      will-change: transform;
    }

.trustee-card {
      flex-shrink: 0;
      width: 582px;
      min-height: 380px;
    }

/* ═══════════════════════════════════════════════
       VALUES SECTION (dusk blue)
       Layout on desktop: zig-zag in a 2-column grid
         Row 1: Courage       — left col
         Row 2: Trust         — right col
         Row 3: Vulnerability — left col, right-edge-at-centre,
                                stem on the RIGHT side
       Stems run full height of each item so bottom dot
       aligns with the end of the paragraph.
    ═══════════════════════════════════════════════ */

.values {
      position: relative;
      background-color: var(--dusk);
      padding: 270px 0 200px;
      /* overflow removed so top circles aren't clipped */
      z-index: 5;
    }

/* Floating orb — mouse-follow (desktop) / scroll-drift (touch).
       Same mechanics as homepage .choose-orb: JS sets a translate3d
       transform that centres the orb around its anchor point (top/left)
       and applies an offset (dx, dy) for movement. */

.values-orb {
      position: absolute;
      top: 50%;
      left: 50%;
      width: 1000px;
      height: 1000px;
      pointer-events: none;
      z-index: 1;
      will-change: transform;
      transition: transform 0.08s linear;
      line-height: 0;
    }

.values-orb img,
    .values-orb picture {
      width: 100%;
      height: 100%;
      object-fit: contain;
      display: block;
    }

/* 7 circles straddling the top border */

.values-circles {
      position: absolute;
      top: 0;
      left: -10px;
      right: -10px;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      align-items: center;
      pointer-events: none;
      z-index: 10;
    }

.values-circle {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: var(--white);
      flex-shrink: 0;
    }

.values-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      color: var(--parchment);
      text-transform: uppercase;
      letter-spacing: -0.01em;
      text-align: center;
      margin-bottom: 120px;
      position: relative;
      z-index: 2;
    }

/* 2-column grid; items placed via grid-column to create the
       zig-zag. Row gap provides vertical breathing room. */

.values-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      row-gap: 40px;
      column-gap: 0;
      max-width: 1000px;
      margin: 0 auto;
      position: relative;
      z-index: 2;
    }

.values-item {
      position: relative;
      color: var(--parchment);
      padding-left: 40px;
      max-width: 340px;
    }

/* Placement: each value gets its own row.
       Row 1: Courage       → left column, stem on the RIGHT
       Row 2: Trust         → right column, stem on the LEFT
       Row 3: Vulnerability → left column, right-edge-at-centre,
                              stem on the RIGHT */

.values-item:nth-child(1) {
      grid-column: 1;
      grid-row: 1;
      justify-self: start;
      padding-left: 0;
      padding-right: 40px;       /* stem on the RIGHT */
    }

.values-item:nth-child(2) { grid-column: 2; grid-row: 2; justify-self: end; }

.values-item:nth-child(3) {
      grid-column: 1;
      grid-row: 3;
      justify-self: end;         /* right edge at centre of grid */
      padding-left: 0;
      padding-right: 40px;        /* stem on the RIGHT instead */
    }

/* Stem on the RIGHT for items 1 and 3 */

.values-item:nth-child(1)::before,
    .values-item:nth-child(3)::before {
      left: auto;
      right: 8px;
    }

.values-item:nth-child(1)::after,
    .values-item:nth-child(3)::after {
      left: auto;
      right: 3px;
    }

.values-item:nth-child(1) .values-dot-bottom,
    .values-item:nth-child(3) .values-dot-bottom {
      left: auto;
      right: 3px;
    }

/* Stem — runs from top dot down to bottom dot,
       stretched between absolute top/bottom offsets so
       its length follows each item's natural height. */

.values-item::before {
      content: '';
      position: absolute;
      top: 8px;
      bottom: 8px;
      left: 8px;
      width: 1px;
      background: var(--parchment);
    }

/* Top dot */

.values-item::after {
      content: '';
      position: absolute;
      top: 3px;
      left: 3px;
      width: 11px;
      height: 11px;
      border-radius: 50%;
      background: var(--parchment);
    }

/* Bottom dot — at natural end of content */

.values-item .values-dot-bottom {
      position: absolute;
      bottom: 3px;
      left: 3px;
      width: 11px;
      height: 11px;
      border-radius: 50%;
      background: var(--parchment);
    }

.values-name {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 42px;
      line-height: 1.15;
      color: var(--parchment);
      margin-bottom: 20px;
    }

.values-copy {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.7;
      color: var(--parchment);
      text-align: justify;
      margin: 0;
    }

/* Tablet & mobile: all three stacked, left-aligned,
       stem on the left (matches the mobile Figma design). */

/* ═══════════════════════════════════════════════
       FEATURE CARD (big parchment card with photo)
    ═══════════════════════════════════════════════ */

.feature {
      position: relative;
      background-color: var(--olive);
      padding: 150px 0 150px;
      /* overflow removed so top circles aren't clipped */
      z-index: 5;
    }

/* 7 circles at top */

.feature-circles {
      position: absolute;
      top: 0;
      left: -10px;
      right: -10px;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      align-items: center;
      pointer-events: none;
      z-index: 10;
    }

.feature-circle {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: var(--white);
      flex-shrink: 0;
    }

.feature-card {
      background: var(--parchment);
      padding: 5px;
      border-radius: 4px;
      position: relative;
    }

.feature-card-inner {
      position: relative;
      border: 1px solid var(--ink);
      border-radius: 2px;
      overflow: hidden;
      min-height: 600px;
      background-color: var(--ink);
    }

.feature-card-inner img {
      width: 100%;
      height: 100%;
      min-height: 600px;
      object-fit: cover;
      display: block;
    }

/* Horizontal rule with dot on the card */

.feature-card-rule {
      position: absolute;
      left: 50px;
      right: 50px;
      bottom: 54px;
      height: 1px;
      background: var(--parchment);
      z-index: 2;
    }

.feature-card-rule::before {
      content: '';
      position: absolute;
      top: 50%;
      left: 80px;
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: var(--parchment);
      transform: translateY(-50%);
    }


/* Feature video — inline player variant of .feature-card.
   The cover image fills the card initially with a centred play
   button overlay; clicking either triggers JS to inject a native
   <video> (self-hosted) or <iframe> (YouTube/Vimeo) and toggles
   .is-playing on the inner element to hide the cover overlay. */

.feature-card-inner--video {
      cursor: pointer;
    }

.feature-video-cover {
      width: 100%;
      height: 100%;
      min-height: 600px;
      object-fit: cover;
      display: block;
    }

.feature-video-play {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 88px;
      height: 88px;
      padding: 0;
      border: 0;
      background: transparent;
      color: var(--parchment);
      cursor: pointer;
      /* Drop shadow keeps the icon legible on light covers — without
         it a parchment circle over a pale photograph reads as blurred. */
      filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.5));
      transition: transform 0.2s ease;
      z-index: 3;
    }

.feature-video-play:hover,
.feature-video-play:focus-visible {
      transform: translate(-50%, -50%) scale(1.06);
    }

.feature-video-play svg {
      width: 100%;
      height: 100%;
      display: block;
    }

/* Once the player is injected, hide the cover + button + decorative
   rule so the video has the full card to itself. The rule would
   otherwise sit over the bottom of the native video controls and
   look like a smudge across the timeline scrubber. */

.feature-card-inner--video.is-playing .feature-video-cover,
.feature-card-inner--video.is-playing .feature-video-play,
.feature-card-inner--video.is-playing .feature-card-rule {
      display: none;
    }

.feature-video-player {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      display: block;
      border: 0;
      background: #000;
      z-index: 1;
    }


/* ═══════════════════════════════════════════════
   ABOUT PAGE — RESPONSIVE
   Mirrors the original about.html responsive spec verbatim. The
   v1.7.7 block this replaces was an approximation that didn't
   match the design's exact values for headings, orb sizes, story
   alignment, values-grid stacking on tablet, photo-card mobile
   size, the quote section's desktop ↔ mobile arrow toggle, or the
   team-card-body border treatment when it stacks vertically.
   ═══════════════════════════════════════════════ */

@media (max-width: 1024px) {
  .story { padding: 80px 0 60px; }
  .story-content { grid-template-columns: 1fr; gap: 30px; align-items: center; }
  .story-heading { font-size: 100px; text-align: center; }
  .story-copy { font-size: 26px; text-align: center; padding-bottom: 0; }
}
@media (max-width: 767px) {
  .story { padding: 60px 0 40px; }
  .story-heading { font-size: 64px; }
  .story-copy { font-size: 20px; }
}

@media (max-width: 767px) {
  .photo-scroll { padding: 40px 0 80px; }
  .photo-card { width: 240px; height: 320px; }
}

@media (max-width: 1024px) {
  .impact { padding: 120px 0 80px; }
  .impact-heading { font-size: 100px; margin-bottom: 70px; }
  .impact-stat-number { font-size: 90px; }
}
@media (max-width: 767px) {
  .impact { padding: 90px 0 60px; }
  .impact-heading { font-size: 60px; margin-bottom: 50px; }
  .impact-stat-number { font-size: 56px; }
  .impact-stat-label { font-size: 10px; }
  .impact-stats { gap: 20px; }
  .impact-orb { width: 220px; height: 220px; }
}

@media (max-width: 1024px) {
  .quote { padding: 60px 0 90px; }
  .quote-text { font-size: 26px; }
  .quote-arrows-desktop { padding: 0 var(--pad-tablet); }
  .quote-arrows-desktop .quote-arrow-prev { left: var(--pad-tablet); }
  .quote-arrows-desktop .quote-arrow-next { right: var(--pad-tablet); }
}
@media (max-width: 767px) {
  .quote { padding: 40px 0 70px; }
  .quote-text { font-size: 20px; }

  /* Centered desktop arrow wrapper hidden on mobile; the
     above-content variant takes over so arrows don't overlap
     the smaller mobile quote text. */
  .quote-arrows-desktop { display: none; }
  .quote-arrows-mobile {
    display: block;
    position: relative;
    z-index: 3;
    padding: 0 var(--pad-mobile);
    height: 20px;
    margin-bottom: 40px;
  }
  .quote-arrows-mobile .quote-arrow {
    position: absolute;
    top: 0;
    transform: none;
  }
  .quote-arrows-mobile .quote-arrow-prev { left: var(--pad-mobile); }
  .quote-arrows-mobile .quote-arrow-next { right: var(--pad-mobile); }
}

@media (max-width: 1024px) {
  .team { padding: 60px 0 80px; }
  .team-heading { font-size: 100px; margin-bottom: 60px; }
  .team-card { min-height: 320px; }
  .team-card-body { padding: 24px; }
  .team-card-name { font-size: 26px; }
}
@media (max-width: 767px) {
  .team { padding: 40px 0 60px; }
  .team-heading { font-size: 60px; margin-bottom: 40px; }
  .team-grid { grid-template-columns: 1fr; gap: 24px; }
  .team-card {
    flex-direction: column;
    min-height: auto;
  }
  .team-card-photo {
    width: 100%;
    height: 260px;
    border-radius: 2px 2px 0 0;
  }
  .team-card-body {
    border-left: 1px solid var(--ink);
    border-top: none;
    border-radius: 0 0 2px 2px;
    padding: 24px;
  }
  .team-card-name { font-size: 24px; }
}

@media (max-width: 1024px) {
  .trustees { padding: 60px 0 120px; }
  .trustees-heading { font-size: 100px; margin-bottom: 50px; }
  .trustees-arrows {
    padding: 0 var(--pad-tablet);
    margin-bottom: 50px;
  }
  .trustees-arrow-prev { left: var(--pad-tablet); }
  .trustees-arrow-next { right: var(--pad-tablet); }
  .trustee-card { width: 480px; min-height: 320px; }
}
@media (max-width: 767px) {
  .trustees { padding: 40px 0 80px; }
  .trustees-heading { font-size: 60px; margin-bottom: 40px; }
  .trustees-arrows {
    padding: 0 var(--pad-mobile);
    margin-bottom: 40px;
  }
  .trustees-arrow-prev { left: var(--pad-mobile); }
  .trustees-arrow-next { right: var(--pad-mobile); }
  .trustees-track { gap: 24px; }
  .trustee-card { width: 320px; min-height: auto; }
}

@media (max-width: 1024px) {
  .values { padding: 180px 0 140px; }
  .values-heading { font-size: 100px; margin-bottom: 80px; }
  .values-orb { width: 600px; height: 600px; }
  .values-grid {
    grid-template-columns: 1fr;
    row-gap: 80px;
  }
  /* Items 1 and 3 had stems on the right and were placed in the
     left column with right-side justification on desktop. When
     the grid collapses to one column, reset their layout AND
     flip the stem/dot positions back to the left side so all
     three items present consistently. */
  .values-item,
  .values-item:nth-child(1),
  .values-item:nth-child(2),
  .values-item:nth-child(3) {
    grid-column: 1;
    grid-row: auto;
    justify-self: center;
    max-width: 340px;
    width: 100%;
    padding-left: 40px;
    padding-right: 0;
  }
  .values-item:nth-child(1)::before,
  .values-item:nth-child(3)::before { left: 8px; right: auto; }
  .values-item:nth-child(1)::after,
  .values-item:nth-child(3)::after  { left: 3px; right: auto; }
  .values-item:nth-child(1) .values-dot-bottom,
  .values-item:nth-child(3) .values-dot-bottom { left: 3px; right: auto; }
  .values-name { font-size: 36px; }
}
@media (max-width: 767px) {
  .values { padding: 120px 0 90px; }
  .values-heading { font-size: 60px; margin-bottom: 60px; }
  .values-orb { width: 400px; height: 400px; }
  .values-grid { row-gap: 60px; }
  .values-name { font-size: 30px; }
}

@media (max-width: 1024px) {
  .feature { padding: 100px 0; }
  .feature-card-inner { min-height: 460px; }
  .feature-card-inner img { min-height: 460px; }
}
@media (max-width: 767px) {
  .feature { padding: 80px 0; }
  .feature-card-inner { min-height: 320px; }
  .feature-card-inner img { min-height: 320px; }
  .feature-card-rule { left: 20px; right: 20px; bottom: 30px; }
  .feature-card-rule::before { left: 30px; }
}


/* ═══════════════════════════════════════════════
       FIND YOUR VOICE (same as homepage)
    ═══════════════════════════════════════════════ */

/* Orb — bottom aligned, full width, above bg, behind content.
       Mirrors the homepage voice-orb pattern. */

/* ═══════════════════════════════════════════════
       PILL BUTTON  (shared across CTAs and filter pills)
       Background artwork comes from a <img class="pill-bg">
       dropped in as a child of the button. Colour modifier
       (.pill--<colour>) only sets the text colour.
    ═══════════════════════════════════════════════ */

/* Text colour per image variant */

/* ═══════════════════════════════════════════════
       FOOTER (same as homepage)
    ═══════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════
       FADE-IN REVEAL
    ═══════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════
     PROGRAMME PAGE STYLES
     ═══════════════════════════════════════════════ */

/* ── FONTS ── */

/* ── RESET ── */

/* ── VARIABLES ── */

/* ── BASE ── */

/* ── CONTAINER ── */

/* ═══════════════════════════════════════════════
       HEADER
    ═══════════════════════════════════════════════ */

nav a {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--parchment);
      text-decoration: none;
    }

/* Burger */

/* Mobile menu — bark on this page */

/* ═══════════════════════════════════════════════
       SUB-NAV (Programme in-page section links)
       Positioned so its LEFT edge aligns with the header
       dot under the active nav item. JS sets `left` on .subnav.
    ═══════════════════════════════════════════════ */

/* Tablet & below: main nav is hidden, so subnav becomes
       a simple centered row below the burger header. */

/* ═══════════════════════════════════════════════
       HOW WE WORK  (bark hero)
    ═══════════════════════════════════════════════ */

.how-we-work {
      position: relative;
      background-color: var(--bark);
      padding: 160px 0 120px;
      overflow: hidden;
    }

/* Hero content paints above the orb (orb sits at z-index 2). */

.how-we-work > .container {
      position: relative;
      z-index: 3;
    }

/* Orb pinned to the top of the hero. Zero-height container
       places it; the image extends downward and is eventually
       covered by the next section (.events-section has z-index 3).
       Pattern mirrors .story-orb-divider on the about page. */

.programme-orb-divider {
      position: relative;
      height: 0;
      overflow: visible;
      pointer-events: none;
      z-index: 2;
      line-height: 0;
    }

.programme-orb-divider img {
      position: absolute;
      top: 0;
      left: 50%;
      width: 100vw;
      transform: translateX(-50%);
      display: block;
    }

.how-we-work-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      letter-spacing: -0.01em;
      color: var(--parchment);
      text-transform: uppercase;
      text-align: center;
      margin-bottom: 40px;
    }

.how-we-work-copy {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 36px;
      line-height: 1.2;
      color: var(--parchment);
      text-align: center;
      max-width: 920px;
      margin: 0 auto 60px;
    }

.how-we-work-copy em { font-style: italic; }

/* Pill buttons container */

.pill-row {
      display: flex;
      justify-content: center;
      flex-wrap: wrap;
      gap: 24px;
      margin-bottom: 100px;
    }

/* ═══════════════════════════════════════════════
       PILL BUTTON  (shared with category filters)
       Same img-child pattern as the homepage's .path-btn —
       colour/background comes from a <img class="pill-bg">
       placed inside the button with its own coloured
       artwork. Colour class just sets the text colour.
    ═══════════════════════════════════════════════ */

/* Colour variants set TEXT colour only — background
       is provided by the <img> dropped in by the client. */

/* ═══════════════════════════════════════════════
       SESSIONS  (3-column card — same as homepage,
       with spell highlight + spell hover instead of dawn)
    ═══════════════════════════════════════════════ */

/* No double borders — remove left border on cards 2 and 3 */

/* Corner radii — desktop: card 1 left corners, card 3 right corners */

/* Spell-purple text highlight — inline marker effect */

/* Sessions responsive */

/* ═══════════════════════════════════════════════
       SECTION DIVIDER CIRCLES
       Same pattern as about.html — 7 white dots
       straddling a section boundary.
    ═══════════════════════════════════════════════ */

.section-divider {
      position: relative;
      height: 0;
      z-index: 10;
    }

.section-divider-inner {
      position: absolute;
      top: 0;
      left: -10px;
      right: -10px;
      transform: translateY(-50%);
      display: flex;
      justify-content: space-between;
      align-items: center;
      pointer-events: none;
    }

.section-divider-inner .dot {
      width: 20px;
      height: 20px;
      border-radius: 50%;
      background: var(--white);
      flex-shrink: 0;
    }

/* ═══════════════════════════════════════════════
       EVENT CARDS
       Structure: colored "matte" padding wraps a thin
       parchment/ink border which frames both the image
       (top 50%) and content (bottom 50%). Category +
       title are overlaid on the image at bottom-left.
       Date/time and description flow together with no
       gap between them. Apply Now button sits flush to
       the bottom with its own bordered rectangle.
    ═══════════════════════════════════════════════ */

.events-section {
      background: var(--parchment);
      padding: 100px 0 140px;
      position: relative;
      z-index: 3;
    }

/* Filter-pill marquee above the event grid. Two identical
       tracks side-by-side animate translateX(0) → translateX(-50%)
       so the loop is seamless. Hovering anywhere over the filter
       pauses the animation so the pill under the cursor can be
       clicked reliably. */

.events-filter {
      position: relative;
      width: 100%;
      overflow: hidden;
      padding: 25px 0;
      margin: 0 auto 60px;
    }

.events-filter-scroller {
      display: flex;
      width: max-content;
      animation: events-filter-scroll 55s linear infinite;
      will-change: transform;
    }

.events-filter:hover .events-filter-scroller {
      animation-play-state: paused;
    }

.events-filter-track {
      display: flex;
      flex-shrink: 0;
      gap: 50px;
      padding-right: 50px;   /* match gap so the seam flows evenly */
    }

@keyframes events-filter-scroll {
      from { transform: translateX(0); }
      to   { transform: translateX(-50%); }
    }

.events-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 50px 80px;
      max-width: 1240px;
      margin: 0 auto;
    }

.event-card {
      position: relative;
      border-radius: 4px;
      padding: 5px;
      display: flex;
      flex-direction: column;
      transition: transform 0.35s ease;
    }

/* Playful 6° tilt on hover, per designer note */

.event-card:hover {
      transform: rotate(6deg);
      z-index: 2;
    }

.event-card--dusk  { background: var(--dusk);  color: var(--parchment); }

.event-card--spell { background: var(--spell); color: var(--ink); }

.event-card--dawn  { background: var(--dawn);  color: var(--ink); }

.event-card--olive { background: var(--olive); color: var(--parchment); }

.event-card--bark  { background: var(--bark);  color: var(--parchment); }

/* Inner frame border — parchment on dark cards, ink on light cards */

.event-card-inner {
      border: 1px solid currentColor;
      border-radius: 2px;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      flex: 1;
    }

/* Image area (top 50%) with overlaid category + title */

.event-card-image {
      position: relative;
      aspect-ratio: 572 / 219;
      flex-shrink: 0;
      overflow: hidden;
      background: var(--ink);
      border-bottom: 1px solid currentColor;
    }

.event-card-image img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

/* Dark gradient in the lower half of the image, so overlaid
       text remains legible regardless of the photo content. */

.event-card-image::after {
      content: '';
      position: absolute;
      inset: 0;
      background: linear-gradient(
        to bottom,
        transparent 30%,
        rgba(0, 0, 0, 0.55) 100%
      );
      pointer-events: none;
    }

.event-card-overlay {
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      padding: 24px 28px;
      color: var(--parchment);
      z-index: 1;
    }

.event-card-category {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      display: block;
      margin-bottom: 6px;
    }

.event-card-title {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 36px;
      line-height: 1.1;
      color: inherit;
    }

.event-card-title em { font-style: italic; }

/* Body area (bottom 50%) — meta + description flow tight;
       button pinned to the bottom. */

.event-card-body {
      padding: 24px 28px 24px;
      display: flex;
      flex-direction: column;
      flex: 1;
    }

.event-card-text { flex: 1; }

.event-card-meta {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.65;
    }

.event-card-meta-row {
      display: flex;
    }

.event-card-meta-label {
      font-weight: 700;
      min-width: 52px;
      flex-shrink: 0;
    }

.event-card-meta-value { font-weight: 400; }

.event-card-description {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.65;
      margin: 0;  /* flush to meta — no gap */
    }

.event-card-btn {
      display: block;
      width: 100%;
      margin-top: 24px;
      padding: 16px;
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 700;
      font-size: 12px;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: inherit;
      text-decoration: none;
      text-align: center;
      background: transparent;
      border: 1px solid currentColor;
      border-radius: 2px;
      cursor: pointer;
      transition: background 0.2s ease, color 0.2s ease;
      /* Reset native <button> chrome so <button> elements (the new
         modal triggers on Programme + Sign Up) render identically
         to the <a> rendering used elsewhere. width:100% replaces
         the implicit full-width anchors had under display:block;
         bg:transparent + border explicit + appearance:none + box-
         sizing:border-box neutralise the browser's default button
         pill so all the typographic + border properties above land
         on a clean canvas. */
      -webkit-appearance: none;
      appearance: none;
      box-sizing: border-box;
    }

/* Hover: invert — dark cards fill with parchment, light
       cards fill with ink. */

.event-card--dusk  .event-card-btn:hover,
    .event-card--olive .event-card-btn:hover,
    .event-card--bark  .event-card-btn:hover {
      background: var(--parchment);
      color: var(--ink);
    }

.event-card--spell .event-card-btn:hover,
    .event-card--dawn  .event-card-btn:hover {
      background: var(--ink);
      color: var(--parchment);
    }

/* Disabled state — applied when the editor has marked the event as
   fully booked via the Events meta box "Allow applications" toggle.
   The button text reads "Fully Booked", the native `disabled` attr
   blocks activation, and these styles communicate the inert state
   visually:
     - 50% opacity dims the variant's fg/bg colours uniformly without
       needing per-variant overrides (works on every event-card colour
       since opacity scales whatever's underneath)
     - cursor:not-allowed is the standard visual cue for an inert
       interactive control
     - The :hover inversion rules above don't match (since the
       hover-base selectors don't include --disabled), so the button
       stays visually static on hover instead of suggesting it might
       still respond to a click
   Specificity note: this rule needs to override the base
   .event-card-btn { cursor: pointer } above, so we lean on the
   double-class selector .event-card-btn.event-card-btn--disabled
   for predictable cascade order. */
.event-card-btn.event-card-btn--disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }


/* ─── FEATURED EVENTS BLOCK (Programme — inside .how-we-work) ───
   The .programme-featured-events wrapper sits inside .how-we-work
   .container, holding a vertical stack of .golden-story-card-styled
   featured event cards (one per event the editor selects in the
   Featured events ACF Relationship field). Each card is the same
   shared .golden-story-card / .golden-story-card-inner markup used
   on Stories + Partners, so the bordered-frame visuals, the
   responsive single-column collapse at ≤1024px, and the photo
   rotation all come free from those rules.

   This block adds:
     - The vertical stack: margin-top for separation from the sessions
       grid above; gap between cards.
     - A contextual hover rule for the in-card .event-card-btn — the
       per-variant hover invert rules don't fire here because the
       button isn't inside an .event-card--{variant} ancestor, so we
       supply a generic ink-fill hover (parchment ground → ink fill
       → parchment text).
     - A width tweak so the Apply Now button doesn't stretch to fill
       the card's content column (the base .event-card-btn defaults
       to display:block + width:100% for the event-card grid layout
       where that's right; in this editorial card it reads better at
       natural width). */

.programme-featured-events {
      margin-top: 80px;
      display: flex;
      flex-direction: column;
      gap: 60px;
    }

/* Contextual hover for the Apply Now button when it sits inside a
   featured-event card. Specificity 0,2,0 (descendant of single class)
   beats the base .event-card-btn rule (0,1,0). The :disabled state's
   compound .event-card-btn.event-card-btn--disabled selector wins via
   cascade order against this generic hover, which is correct — a
   disabled "Fully Booked" button should never hover-invert. */
.programme-featured-events .event-card-btn:hover {
      background: var(--ink);
      color: var(--parchment);
    }

/* The Apply Now button defaults to display:block + width:100% from
   the .event-card-btn base, which is right for the event-card grid
   but stretches too much in the wider .golden-story-content column.
   Restored to inline-block at natural width here, with margin-right:
   auto left-aligning it within the block-formatting context. */
.programme-featured-events .event-card-btn {
      display: inline-block;
      width: auto;
      margin-right: auto;
      padding: 16px 32px;
    }

/* ─── PROGRAMME-SPECIFIC OVERRIDES on .golden-story-card visuals ────
   Scoped to .programme-featured-events so Stories' and Partners'
   featured-story cards keep their original treatment.

   1. align-items: flex-start on the inner grid — top-aligns the
      image column with the content column so the heading sits
      level with the photo's top edge. The shared base rule
      centres them vertically, which suits a single short
      paragraph (the Stories featured story) but reads as
      misaligned on longer event descriptions where the body can
      run several paragraphs deep.

   2. Per-paragraph treatment in the body — base rule gives every
      <p> a 21px text-indent and zero margin-bottom (continuous
      editorial setting). For event descriptions we want classic
      paragraph blocks: no first-line indent on any paragraph
      (text-indent rule applies to every <p>), and 1em of spacing
      between consecutive paragraphs (margin rule scoped to
      :not(:last-of-type) so the final paragraph doesn't leave a
      hanging gap below the card content).

   3. List styling — base rule doesn't address <ul>/<ol> at all,
      so they inherit the browser default 40px left padding plus
      whatever vertical margins user-agent CSS applies. Tightens
      to 15px left padding + 1em bottom margin for visual
      coherence with the surrounding paragraph rhythm.
   ───────────────────────────────────────────────────────────────── */

.programme-featured-events .golden-story-card-inner {
      align-items: flex-start;
    }

.programme-featured-events .golden-story-body p {
      text-indent: 0;
    }

.programme-featured-events .golden-story-body p:not(:last-of-type) {
      margin-bottom: 1em;
    }

.programme-featured-events .golden-story-body ul,
.programme-featured-events .golden-story-body ol {
      padding-left: 15px;
      margin-bottom: 1em;
    }


/* ═══════════════════════════════════════════════
       WHO'S IT FOR?  (dusk section)
    ═══════════════════════════════════════════════ */

.whos-it-for {
      position: relative;
      background-color: var(--dusk);
      padding: 100px 0 40px;
      z-index: 3;
    }

/* Heading + audiences pulled above the orb (z-index 2
       within .whos-it-for's stacking context). */

.whos-it-for-heading,
    .audiences {
      position: relative;
      z-index: 3;
    }

.whos-it-for-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      letter-spacing: -0.01em;
      color: var(--parchment);
      text-transform: uppercase;
      text-align: center;
      margin-bottom: 80px;
    }

/* ── 5 audiences on an axis ──
       Full-width marquee: the scroller contains two
       identical tracks side by side and animates
       translateX(0) → translateX(-50%) infinitely,
       so when one track scrolls off-screen the other
       is already in its place — seamless loop.
       Pauses on hover. */

.audiences {
      position: relative;
      width: 100%;
      overflow: hidden;
      padding: 80px 0;
    }

/* Horizontal axis runs full screen width */

.audiences-axis {
      position: absolute;
      left: 0;
      right: 0;
      top: 50%;
      height: 1px;
      background: var(--parchment);
      transform: translateY(-0.5px);
      z-index: 1;
    }

.audiences-scroller {
      display: flex;
      width: max-content;
      animation: audiences-scroll 110s linear infinite;
      will-change: transform;
    }

.audiences:hover .audiences-scroller {
      animation-play-state: paused;
    }

/* Static fallback for 1-2 audience items: animation off, single
       centred row. The 5-item design assumes a long-enough track
       to read as a continuous scroll; ≤2 items would scroll into
       too much empty space before wrapping. Mirrors .paths--static
       on the homepage. */
.audiences--static .audiences-scroller {
      animation: none;
      width: 100%;
      justify-content: center;
    }

.audiences-track {
      display: flex;
      flex-shrink: 0;
    }

@keyframes audiences-scroll {
      from { transform: translateX(0); }
      to   { transform: translateX(-50%); }
    }

/* Each audience: fixed width so the marquee can loop
       seamlessly. Stem height = item height = consistent
       vertical bounds for the title. */

.audience {
      position: relative;
      width: 320px;
      height: 200px;
      flex-shrink: 0;
      padding-left: 40px;
    }

/* Top dot */

.audience::before {
      content: '';
      position: absolute;
      top: 0;
      left: 12px;
      width: 15px;
      height: 15px;
      border-radius: 50%;
      background: var(--parchment);
      z-index: 3;
    }

/* Bottom dot */

.audience::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 12px;
      width: 15px;
      height: 15px;
      border-radius: 50%;
      background: var(--parchment);
      z-index: 3;
    }

/* Stem between the two dots */

.audience-stem {
      position: absolute;
      top: 15px;
      bottom: 15px;
      left: 19px;
      width: 1px;
      background: var(--parchment);
      z-index: 2;
    }

/* Title sits WITHIN the vertical bounds of the stem.
       Alternation is driven by position within the track:
       odd children sit at the bottom, even at the top.
       With an even item count per track, alternation is
       preserved when the track is duplicated for looping. */

.audience-title {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 36px;
      line-height: 1.1;
      color: var(--parchment);
      white-space: nowrap;
      position: absolute;
      left: 40px;
      right: 0;
    }

.audience-title em { font-style: italic; }

/* Odd items → title at the bottom of the stem */

.audience:nth-child(odd) .audience-title {
      bottom: 15px;
    }

/* Even items → title at the top of the stem */

.audience:nth-child(even) .audience-title {
      top: 15px;
    }

/* ── For Organizations / For Communities ── */

.partnership {
      padding: 80px 0 120px;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 0;
      position: relative;
      z-index: 3;
    }

/* Partnership-centred orb — same pattern as
       .team-orb-divider on the about page: zero-height
       container, image centred on it via translate(-50%,-50%),
       JS applies translateY to land the image centre on the
       vertical midpoint of .partnership. */

.partnership-orb-divider {
      position: relative;
      height: 0;
      overflow: visible;
      pointer-events: none;
      z-index: 2;
      line-height: 0;
      will-change: transform;
    }

.partnership-orb-divider img {
      position: absolute;
      top: 0;
      left: 50%;
      width: 100vw;
      transform: translate(-50%, -50%);
      display: block;
    }

.partnership-column {
      padding: 0 60px;
      position: relative;
    }

.partnership-column-heading {
      font-family: 'PerfectlyNineties', serif;
      font-weight: 400;
      font-size: 42px;
      line-height: 1.2;
      color: var(--parchment);
      text-align: center;
      margin-bottom: 40px;
    }

.partnership-column-heading em { font-style: italic; }

.partnership-column-copy {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-size: 12px;
      line-height: 1.7;
      color: var(--parchment);
      text-align: justify;
    }

.partnership-column-copy strong { font-weight: 700; }

.partnership-column-copy p + p { margin-top: 1.7em; }

/* Dividing stem between the two partnership columns */

.partnership-divider {
      position: absolute;
      top: 10px;
      bottom: 10px;
      left: 50%;
      width: 1px;
      background: var(--parchment);
      transform: translateX(-0.5px);
    }

.partnership-divider::before,
    .partnership-divider::after {
      content: '';
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
      width: 15px;
      height: 15px;
      border-radius: 50%;
      background: var(--parchment);
    }

.partnership-divider::before { top: -7.5px; }

.partnership-divider::after  { bottom: -7.5px; }

/* ── Full-width image section, same pattern as the
       homepage's .choose-images ── */

.partnership-images {
      position: relative;
      /* Dusk solid for the top half of the section, fading to
         fully transparent by 90% (the last 10% stays fully
         transparent). Sits over the parchment body bg, which
         shows through as the gradient becomes transparent and
         meets .upcoming (also parchment) flush at the section
         boundary. Explicit alpha-0 dusk (rgba) at the fade-out
         stop keeps the gradient in the dusk hue all the way
         down; the `transparent` keyword would interpolate
         through grey because it's shorthand for `rgba(0,0,0,0)`. */
      background: linear-gradient(
        to bottom,
        var(--dusk) 50%,
        rgba(89, 116, 139, 0) 90%
      );
      /* Clip the 3-image variant's edge bleed (110% width, -5%
         margin-left set on .image-gallery[data-image-count="3"]).
         Harmless for 1- and 2-image variants which don't overflow,
         and for the fallback PNG which fits flush at 100% width. */
      overflow-x: hidden;
      /* NO z-index on purpose. The .partnership-orb-divider above
         this section (inside .whos-it-for at z-index: 3 in the
         root stacking context) bleeds visually past whos-it-for's
         bottom into this section's vertical space. We want the orb
         to paint ABOVE this section's background gradient but
         BELOW the polaroid photos — a three-layer cake at that Y
         position. By leaving this section without a z-index it
         doesn't form a stacking context, so its background paints
         at the z-auto positioned stage in the root and the polaroid
         layer below can be lifted to z-index: 4 to sit above the
         orb. Final root paint order in the overlap region:
           1. partnership-images bg gradient   (z-auto, stage 6)
           2. orb image                        (z:3 via whos-it-for)
           3. polaroid photos                  (z:4, see rule below) */
    }

/* Polaroid layer — lifted above the .whos-it-for stacking context
   (z-index: 3 in root) so the orb image, which extends down into
   this section's vertical space from .whos-it-for above, paints
   beneath the photos rather than over them. Both the gallery
   container and the fallback composite img need the lift; targeting
   the direct children covers both render paths. */
.partnership-images > .image-gallery,
.partnership-images > img {
      position: relative;
      z-index: 4;
    }

.partnership-images img {
      display: block;
      width: 100%;
      height: auto;
    }

/* ═══════════════════════════════════════════════
       UPCOMING EVENTS  (parchment with filter + grid)
    ═══════════════════════════════════════════════ */

.upcoming {
      background: var(--parchment);
      padding: 160px 0 140px;
      position: relative;
      z-index: 3;
    }

.upcoming-heading {
      font-family: 'AlteHaasGrotesk', sans-serif;
      font-weight: 400;
      font-size: 140px;
      line-height: 0.95;
      letter-spacing: -0.01em;
      color: var(--ink);
      text-transform: uppercase;
      text-align: center;
      margin-bottom: 80px;
    }

/* Horizontal-scrollable pill filter row */

/* (Upcoming Events uses the shared .events-filter marquee) */


/* ═══════════════════════════════════════════════
   PROGRAMME PAGE — RESPONSIVE
   Mirrors the original programme.html responsive spec verbatim.
   The programme-specific sections (how-we-work, events, whos-it-for,
   audiences, partnership, upcoming) had no responsive coverage — only
   the shared .session-card / .sessions-grid rules from the homepage
   block were carrying over. Sessions rules are already correct so
   they're not repeated here.
   ═══════════════════════════════════════════════ */

@media (max-width: 1024px) {
  .how-we-work-heading { font-size: 100px; letter-spacing: -0.01em; }
  .how-we-work-copy { font-size: 26px; }
}
@media (max-width: 767px) {
  .how-we-work { padding: 100px 0 80px; }
  .how-we-work-heading { font-size: 60px; letter-spacing: -0.01em; margin-bottom: 30px; }
  .how-we-work-copy { font-size: 20px; margin-bottom: 50px; }
  .pill-row { margin-bottom: 70px; gap: 16px; }
}

@media (max-width: 1024px) {
  .events-section { padding: 80px 0 100px; }
  .events-grid { gap: 40px; }
  .events-filter-track { gap: 30px; padding-right: 30px; }
}
@media (max-width: 767px) {
  .events-section { padding: 70px 0 90px; }
  .events-grid { grid-template-columns: 1fr; gap: 30px; }
  .event-card-title { font-size: 28px; }
  .event-card-overlay { padding: 20px; }
  .event-card-body { padding: 20px; }
  .events-filter { margin-bottom: 40px; padding: 20px 0; }
  .events-filter-track { gap: 24px; padding-right: 24px; }
}

@media (max-width: 1024px) {
  .whos-it-for { padding: 80px 0 30px; }
  .whos-it-for-heading { font-size: 100px; margin-bottom: 60px; }

  .audiences { padding: 60px 0; }
  .audience { width: 280px; }

  /* Partnership: 2-col → 1-col stack on tablet, with the divider
     hidden (it ran vertically between the columns). */
  .partnership { grid-template-columns: 1fr; padding: 50px 0 80px; }
  .partnership-column { padding: 0; }
  .partnership-column + .partnership-column { margin-top: 60px; }
  .partnership-divider { display: none; }
  .partnership-column-heading { font-size: 36px; }
}
@media (max-width: 767px) {
  .whos-it-for-heading { font-size: 60px; letter-spacing: -0.01em; }

  .audiences { padding: 40px 0; }
  .audience { width: 260px; height: 170px; }
  .audience-title { font-size: 28px; }
  /* Reposition titles inside each audience card (alternating
     bottom/top) so they don't get cut at smaller widths. */
  .audience:nth-child(odd)  .audience-title { bottom: 12px; }
  .audience:nth-child(even) .audience-title { top:    12px; }

  .partnership-column-heading { font-size: 30px; }
}

@media (max-width: 1024px) {
  .upcoming { padding: 100px 0; }
  .upcoming-heading { font-size: 100px; margin-bottom: 60px; }
}
@media (max-width: 767px) {
  .upcoming { padding: 80px 0; }
  .upcoming-heading { font-size: 60px; letter-spacing: -0.01em; margin-bottom: 40px; }
}


/* ═══════════════════════════════════════════════
       FADE-IN REVEAL
    ═══════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════
     GENERIC CONTENT (page.php / index.php fallback)
   ═══════════════════════════════════════════════ */

.generic-content {
  background: var(--parchment);
  color: var(--ink);
  padding: 160px 0 120px;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 18px;
  line-height: 1.6;
}

.generic-content .story-heading {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(40px, 8vw, 128px);
  line-height: 0.95;
  margin-bottom: 32px;
  letter-spacing: -0.02em;
}

.generic-archive-header,
.generic-entry-header {
  margin-bottom: 48px;
}

.generic-entry-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(32px, 4vw, 56px);
  line-height: 1.05;
  margin-bottom: 16px;
}

.generic-entry-title a {
  color: inherit;
  text-decoration: none;
}

.generic-entry-title a:hover {
  text-decoration: underline;
}

.generic-entry-content p,
.generic-entry-content ul,
.generic-entry-content ol,
.generic-entry-content blockquote {
  margin-bottom: 1.4em;
}

.generic-entry-content h2,
.generic-entry-content h3,
.generic-entry-content h4 {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  margin-top: 1.6em;
  margin-bottom: 0.6em;
}

.generic-entry-content h2 { font-size: clamp(28px, 3vw, 42px); }
.generic-entry-content h3 { font-size: clamp(22px, 2.4vw, 32px); }
.generic-entry-content h4 { font-size: clamp(20px, 2vw, 24px); }

.generic-entry-content a {
  color: var(--olive);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.generic-entry-content a:hover {
  color: var(--bark);
}

.generic-entry-content img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
}

.generic-loop .generic-entry + .generic-entry {
  margin-top: 80px;
  padding-top: 80px;
  border-top: 1px solid rgba(29, 30, 27, 0.12);
}

.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.screen-reader-text:focus {
  position: fixed;
  top: 16px;
  left: 16px;
  width: auto;
  height: auto;
  clip: auto;
  padding: 12px 24px;
  background: var(--bark);
  color: var(--white);
  text-decoration: none;
  z-index: 10000;
  border-radius: 999px;
}

@media (max-width: 768px) {
  .generic-content {
    padding: 120px 0 80px;
  }
}

/* ═══════════════════════════════════════════════
     STORIES PAGE
   ═══════════════════════════════════════════════ */

/* --- HERO ----------------------------------------------------- */

.stories-hero {
  position: relative;
  min-height: 100vh;
  background: var(--ink);
  overflow: hidden;
  /* No `isolation: isolate` here — we want the hero's children to
     participate in the root stacking context so .stories-hero-content
     (z-index: 3) can paint OVER the .orb-divider that sits between
     this section and .golden-story below. The orb is z-index: 2 at
     root; content needs to be > 2 to win, which can't happen if the
     hero is its own stacking context.                              */
  padding-top: 56px; /* header height */
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Background image — homepage hero uses `center center / cover` for
   .hero-bg; here we anchor to TOP so the subject's face sits in the
   upper portion and isn't cropped on tall viewports. Same z-stack
   pattern as homepage: bg at z:0, fade at z:2 (above the orb at z:2
   uses DOM order), content at z:3.                                  */
.stories-hero-bg {
  position: absolute;
  inset: 0;
  background: url('assets/images/stories-hero.jpg') center top / cover no-repeat; background-image: image-set(url('assets/images/stories-hero.webp') type('image/webp'), url('assets/images/stories-hero.jpg') type('image/jpeg')); background-position: center top; background-size: cover; background-repeat: no-repeat;
  z-index: 0;
}

.stories-hero-fade {
  position: absolute;
  /* Olive→transparent gradient pinned to the bottom 35% of the hero,
     same pattern as homepage .hero-fade. The colour matches the
     .golden-story section beneath, so the hero photograph fades
     softly into the olive band rather than meeting it on a hard
     edge. The rgba olive (77,94,60) is the same triple var(--olive)
     resolves to. */
  bottom: 0;
  left: 0;
  width: 100%;
  height: 35%;
  background: linear-gradient(to top, var(--olive) 0%, rgba(77, 94, 60, 0) 100%);
  pointer-events: none;
  z-index: 1;
}

.stories-hero-content {
  position: relative;
  z-index: 3;            /* > .orb-divider's z:2 so the headline + copy
                            stay on top when the orb extends up into
                            the bottom of the hero on the boundary.   */
  text-align: center;
  color: var(--white);
  padding-top: 80px;
  padding-bottom: 80px;
}

.stories-hero-headline {
  /* Matched to .learn-hero-headline: same display headline treatment
     across Stories and Learn so the two pages read as siblings. The
     prior Perfectly Nineties capitalised treatment lived here for a
     phase; we've consolidated on the Alte Haas Grotesk uppercase
     headline for both. */
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: clamp(40px, 12vw, 154px);
  line-height: 0.92;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin-bottom: 48px;
}

.stories-hero-subtitle {
  /* Matched to .how-we-work-copy (v1.28.0) — previously this used
     AlteHaasGrotesk sans at a clamp() size, which read as a
     different typeface from the rest of the site's body-intro
     copy. Now mirrors how-we-work-copy's Perfectly Nineties serif
     treatment exactly, with one deliberate exception: margin stays
     `0 auto` (no 60px trailing margin) because the subtitle is the
     last element in .stories-hero-content, whose own
     padding-bottom: 80px already provides the bottom spacing — the
     60px on how-we-work-copy is specific to the gap before that
     section's sessions grid. Responsive font-size steps below
     match how-we-work-copy's 1024/767 breakpoints. */
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: 36px;
  line-height: 1.2;
  max-width: 920px;
  margin: 0 auto;
  color: var(--parchment);
}

.stories-hero-subtitle em { font-style: italic; }

@media (max-width: 1024px) {
  .stories-hero-subtitle { font-size: 26px; }
}
@media (max-width: 767px) {
  .stories-hero-subtitle { font-size: 20px; }
}

@media (max-width: 768px) {
  .stories-hero { min-height: 90vh; }
  .stories-hero-headline { margin-bottom: 32px; }
}


/* --- GOLDEN STORY (centerpiece) ------------------------------ */

.golden-story {
  background: var(--olive);
  padding: 120px 0 100px;
  position: relative;
  /* Explicit overflow:visible so the .golden-story-circles rail can
     extend past the section's top and bottom edges into the
     neighbouring sections (above into stories-hero, below into
     story-polaroids) — same pattern as .choose-circles straddling
     the choose section's top boundary on the homepage. */
  overflow: visible;
}

/* The container is the positioning context for .golden-story-circles
   so that the rail's `left` value is measured relative to a known box
   width (the container's padding edge) — this keeps the rail's
   horizontal anchor stable across viewport widths without absolute-
   pixel calc()s against viewport. */
.golden-story .container {
  position: relative;
}

/* Inset-border treatment — outer is the colour frame (parchment bg
   + 5px padding around inner + rounded corners, NO outer border line);
   inner carries the visible 1px border. Same pattern as .alumni-card
   /.alumni-card-inner on this page and .event-card on programme. */
.golden-story-card {
  background: var(--parchment);
  border-radius: 4px;
  padding: 5px;
  position: relative;
  z-index: 3;            /* > .orb-divider's z:2 so the orb sits behind
                            the card on the section-boundary overlap. */
}

.golden-story-card-inner {
  border: 1px solid var(--ink);
  border-radius: 2px;
  padding: 40px;
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 60px;
  align-items: center;
}

/* No-media variant — applied by PHP (inc/featured-card.php) when
   the editor hasn't selected an image for the card. Collapses the
   default 280px+1fr two-column grid to a single full-width column
   so the content spans the whole inner card width rather than
   rendering in the leftmost 280px column with empty space to its
   right. Global by design — if Stories or Partners ever has an
   image-less .golden-story-card in the future, the same fix
   applies without needing a parallel CSS rule. */
.golden-story-card--no-media .golden-story-card-inner {
  grid-template-columns: 1fr;
}

.golden-story-media {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Just a gently tilted image — no polaroid frame, no punch-holes,
   no shadow. The card's inset border is the framing device. */
.golden-story-photo {
  display: block;
  width: 100%;
  height: auto;
  transform: rotate(-3deg);
  border-radius: 2px;
}

/* Vertical line of parchment circles flowing down the centre of the
   photo column, extending above the section into the hero and below
   into .story-polaroids. Sibling of the card (not nested inside) so
   it paints in the root stacking context at z-index: 5 — above the
   orb-divider (z:2), the .stories-hero-content (z:3), the .golden-
   story-card (z:3), and the .story-polaroids img (z:3). The ~80px
   above-section / 60px below-section overlap matches the screenshot.

   Horizontal anchor: container's content-left (after padding) + card
   frame (5px) + card-inner padding-left (50px) + photo-column-half
   (140px) = 195px. `var(--pad-desktop)` accounts for the container's
   padding (which the absolute element measures `left:` past). */
.golden-story-circles {
  position: absolute;
  top: -50px;
  bottom: -50px;
  left: calc(var(--pad-desktop) + 195px);
  width: 16px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  z-index: 5;
}

.golden-story-circle {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--parchment);
  flex-shrink: 0;
}

.golden-story-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0;
}

.golden-story-heading {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-style: normal;
  text-transform: capitalize;
  font-size: clamp(28px, 3vw, 42px);
  line-height: 1.17;
  color: var(--ink);
  margin-bottom: 32px;
  letter-spacing: -0.005em;
}

/* Tablet & below — image stacks above content; circles rail no
   longer makes geometric sense (image is full-width, no left-side
   "centre" to anchor against), so hide it. */
@media (max-width: 1024px) {
  .golden-story-card-inner {
    grid-template-columns: 1fr;
    gap: 40px;
    padding: 40px 32px;
  }
  .golden-story-photo { transform: rotate(-2deg); max-width: 360px; margin: 0 auto; }
  .golden-story-circles { display: none; }
}

.golden-story-body {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--ink);
  text-align: justify;
}
.golden-story-body p {
  text-indent: 21px;
  margin-bottom: 0;
}
.golden-story-body p + p {
  margin-top: 0;
}

/* Featured-story card paragraphs: 1em between, none after the last.
   Equal specificity to the print-style rule above, placed later so it
   wins; the more-specific Programme override keeps its own spacing. */
.golden-story-card p {
  margin-bottom: 1em;
}
.golden-story-card p:last-of-type {
  margin-bottom: 0;
}

/* Stacked featured stories (Stories page repeater) sit 100px apart.
   Scoped to .golden-story so single-card golden sections (Partners)
   and the Programme featured-events cards are unaffected. */
.golden-story .golden-story-card + .golden-story-card {
  margin-top: 100px;
}

/* Spell-purple highlight, gradient + multiply blend (matches Figma) */
.golden-story-body mark {
  background: linear-gradient(180deg, rgba(221,215,255,0.8) 0%, rgba(221,215,255,1) 100%);
  mix-blend-mode: multiply;
  color: inherit;
  padding: 0 2px;
  border-radius: 1px;
}

@media (max-width: 1024px) {
  .golden-story-photo { width: 180px; }
}

@media (max-width: 768px) {
  .golden-story { padding: 80px 0 60px; }
  .golden-story-card-inner {
    padding: 32px 20px;
    gap: 32px;
  }
  .golden-story-photo {
    max-width: 280px;
  }
  .golden-story-heading { margin-bottom: 24px; }
}


/* --- STORY POLAROIDS — full-bleed composite image -----------
   Mirrors .partnership-images on the programme page: a coloured
   section with one full-width image laid across it. The polaroid
   tilt and dot-grid overlays are baked into stories-images.png,
   so no per-photo CSS is needed.

   Olive bg matches .golden-story above so the orb-divider's image
   (which extends DOWN past the hero→golden-story boundary into
   golden-story and on into this section on large screens) reads
   over a continuous olive band — the orb is one large image and
   shouldn't get clipped by a section bg colour change.

   No `z-index` on the section: that would create a stacking
   context that confines the orb-divider (z-index: 2 in the root
   context) to *outside* this section, so the orb couldn't extend
   into the section's visual space at all. The img inside the
   section gets `z-index: 3` instead — it sits above the orb,
   matching the rule "orb over backgrounds, under content". */

.story-polaroids {
  position: relative;
  background-color: var(--olive);
}

.story-polaroids img {
  position: relative;
  z-index: 3;
  display: block;
  width: 100%;
  height: auto;
}

/* Stories-page-only gradient override.

   The base `.story-polaroids` rule above keeps a solid olive bg
   because the class is also used on Partners (overridden to dusk
   via .partners-polaroids) and Sign-up (inherits olive verbatim
   to merge with the olive body band). On Stories specifically,
   we want the section to dissolve into the bark .quote section
   that follows it: olive solid for the top half, then a smooth
   olive → bark fade across the bottom half so the section ends
   in bark and reads as a continuous transition into the dark
   band below.

   Scoped via the `body.spellbound-stories` class set in
   functions.php (spellbound_body_classes). Partners and Sign-up
   are unaffected because their body classes don't match. */
body.spellbound-stories .story-polaroids {
  background: linear-gradient(
    to bottom,
    var(--olive) 0%,
    var(--olive) 50%,
    var(--bark) 100%
  );
}


/* --- QUOTE CAROUSEL ------------------------------------------ */

.story-quote {
  background: var(--bark);
  color: var(--parchment);
  padding: 120px var(--pad-desktop);
  position: relative;
  display: grid;
  grid-template-columns: 80px 1fr 80px;
  gap: 24px;
  align-items: center;
  min-height: 420px;
}

.story-quote-track {
  position: relative;
  text-align: center;
  max-width: 920px;
  margin: 0 auto;
  width: 100%;
  min-height: 280px;
}

.story-quote-slide {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 600ms ease, transform 600ms ease;
  pointer-events: none;
}

.story-quote-slide.is-active {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
  position: relative;
}

.story-quote-text {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(28px, 3.4vw, 48px);
  line-height: 1.18;
  margin: 0 0 48px;
  color: var(--parchment);
}

.story-quote-text em {
  font-style: italic;
}

.story-quote-meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.story-quote-name {
  font-weight: 700;
}

.story-quote-cohort {
  opacity: 0.7;
}

/* Quote arrows — same visual treatment as .timeline-arrow on the
   about page so all carousel/scroller arrows feel identical site-wide.
   The arrowhead direction is baked into each SVG (no rotate needed). */
.story-quote-arrow {
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--parchment);
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.25s ease;
}
.story-quote-arrow svg {
  display: block;
  width: 40px;
  height: 10px;
  overflow: visible;
}
.story-quote-arrow:hover { opacity: 0.7; }
.story-quote-arrow[hidden],
.story-quote-arrow:disabled {
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 768px) {
  .story-quote {
    padding: 80px 20px;
    grid-template-columns: 48px 1fr 48px;
    gap: 12px;
  }
  .story-quote-arrow svg { width: 32px; }
  .story-quote-text { margin-bottom: 32px; }
}


/* --- FILTER PILLS marquee ------------------------------------
   The marquee itself reuses the .events-filter pattern from the
   programme page (auto-scroll, hover-pause, two duplicated tracks
   for seamless loop, .pill / .pill--<colour> for buttons). All of
   that is defined under PROGRAMME — the only thing this section
   adds is the bark band the marquee sits in.

   Click → recolour-all-alumni-cards behaviour is wired up in
   main.js (initFilterClicks already targets every .events-filter
   on the page; for the stories page it finds the .alumni-card
   variants in the same section and swaps their colour class). */

.story-filters-section {
  background: var(--bark);
  /* No section padding — the .events-filter inside carries its own
     25px top + bottom padding (overridden to drop the shared
     60px bottom margin on Stories; see body.spellbound-stories
     .events-filter below). The strip is meant to read as a thin
     band between .quote and .alumni-cards, all sharing bark bg, so
     section-level padding here would just push it apart from the
     content above and below without a visual purpose. */
  padding: 0;
  position: relative;
  /* z-index creates a stacking context — required so the pills'
     mix-blend-mode: screen confines its blend to the bark bg of
     this section. Without it the blend leaks through to whatever
     sits beneath in the document (body bg, hero, etc.), making
     the pills look noticeably different from programme's marquees
     which are wrapped in .events-section / .upcoming (both have
     position:relative + z-index:3 for the same reason). */
  z-index: 3;
}


/* --- ALUMNI CARDS GRID --------------------------------------- */

.alumni-cards {
  background: var(--bark);
  /* Padding tuned to match the breathing rhythm on About (sections
     typically run ~80 top / ~120–160 bottom). Bottom is the larger
     side because that's where the .where-now-circles section
     divider straddles the boundary — the circles are absolutely
     positioned at .where-now's top with translateY(-50%) so the
     padding here gives them clearance before the alumni-card row
     above the boundary. */
  padding: 80px 0 140px;
}

.alumni-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
}

.alumni-card {
  /* Outer carries: card colour (variant-set bg), 5px padding (same as
     .event-card on programme), and rounded corners. The padding is
     what creates the "inset border" — a sliver of card colour shows
     between the outer rounded corner and the inner 1px frame, so the
     border looks "inside" the card. */
  position: relative;
  border-radius: 4px;
  padding: 5px;
  min-height: 380px;
}

/* Inner frame — carries the 1px border, the photo+content grid, and
   the overflow-hidden so the photo doesn't poke past the rounded
   inner corners. currentColor makes the border match the card's text
   colour for each colour variant (parchment on dark, ink on light). */
.alumni-card-inner {
  display: grid;
  grid-template-columns: 213fr 359fr;
  border: 1px solid currentColor;
  border-radius: 2px;
  overflow: hidden;
  height: 100%;
}

.alumni-card-photo {
  position: relative;
  overflow: hidden;
}
.alumni-card-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.alumni-card-content {
  padding: 32px 32px 28px;
  display: flex;
  flex-direction: column;
  font-family: 'AlteHaasGrotesk', sans-serif;
  /* Per-element margins (instead of a uniform `gap`) so we can have
     16px between quote and meta but ZERO between meta and body — the
     name/cohort meta and the body paragraph below it sit flush. */
}

.alumni-card-quote {
  margin: 0 0 16px;
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(22px, 2vw, 30px);
  line-height: 1.18;
  text-transform: capitalize;
}
.alumni-card-quote em {
  font-style: italic;
}

.alumni-card-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 13px;
  line-height: 1.4;
  margin-top: auto;
}
.alumni-card-name { font-weight: 700; }
/* Cohort matches the name above it — same weight, no opacity drop.
   Per the Figma design (node 1050-2796) name and cohort are visually
   identical lines stacked together. */
.alumni-card-cohort { font-weight: 700; }

.alumni-card-body {
  font-size: 13px;
  line-height: 1.55;
  /* Zero top margin → sits flush against the cohort line above. The
     parent `.alumni-card-content` removed its uniform `gap: 16px`
     specifically so this junction has no breathing room. */
  margin: 0;
  opacity: 0.92;
}

/* Colour variants — backgrounds + tuned text colour */
.alumni-card--spell { background: var(--spell); color: var(--ink); }
.alumni-card--dusk  { background: var(--dusk);  color: var(--parchment); }
.alumni-card--olive { background: var(--olive); color: var(--parchment); }
.alumni-card--dawn  { background: var(--dawn);  color: var(--ink); }

@media (max-width: 1024px) {
  .alumni-card-content { padding: 24px 24px 22px; }
}

@media (max-width: 768px) {
  .alumni-cards { padding: 56px 0 96px; }
  .alumni-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .alumni-card {
    grid-template-columns: 130px 1fr;
    min-height: 240px;
  }
  .alumni-card-content { padding: 18px 18px 16px; gap: 10px; }
}


/* --- SECTION DIVIDER (parchment circles between alumni-cards and where-now) --
   Same pattern as .feature-circles / .values-circles on about: 7 circles
   absolutely positioned at the section's top edge, translated up by 50%
   of their own height so they straddle the boundary line. Parchment
   colour per the design (about uses white; stories asks for parchment). */

.where-now-circles {
  position: absolute;
  top: 0;
  left: -10px;        /* extends 10px past edge so 1st/last sit half off-screen */
  right: -10px;
  transform: translateY(-50%);
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  z-index: 10;
}

.where-now-circle {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--parchment);
  flex-shrink: 0;
}


/* --- WHERE ARE THEY NOW? ------------------------------------- */

.where-now {
  background: var(--olive);
  color: var(--parchment);
  /* 120 top gives the .where-now-circles parchment divider
     (positioned at this section's top edge) clearance from the
     heading content. 120 bottom opens up the seam with .timeline
     below (which has only 30px top padding via its shared base
     rule — without a generous bottom here the two sections would
     read as cramped). */
  padding: 120px 0 120px;
  position: relative;
}

.where-now-heading {
  margin: 0;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12vw, 154px);
  line-height: 0.96;
  letter-spacing: -0.01em;
  display: flex;
  flex-direction: column;
}

.where-now-line--top {
  text-align: left;
}
.where-now-line--bottom {
  text-align: right;
  margin-top: -0.05em;
}

@media (max-width: 768px) {
  .where-now { padding: 60px 0 40px; }
}


/* --- STORIES TIMELINE — page-scoped tweaks to the shared .timeline ---
   The timeline carousel itself is defined under ABOUT (.timeline,
   .timeline-axis, .timeline-arrows, .timeline-track, .timeline-item,
   .timeline-top-dot, .timeline-stem, .timeline-bottom-dot) and reused
   on stories via the same class names + the same #timeline ID, so
   initCarousels() in main.js drives both. The two pages diverge only
   in stories-only additions:
     1. A large diffuse orb anchored to the section's bottom edge
        that extends UPWARD into the .where-now section above (mirrors
        .voice-orb in spirit, but inverted: voice clips its orb to
        the bottom; here we want the orb to bleed up into the section
        above, over background and under content).
   The orb has bottom: 0 (vs voice-orb's bottom: -200px) so it cannot
   overflow below the timeline into the footer — no clipping needed.
   .where-now content is lifted to z-index: 3 so the heading sits over
   the orb where the orb's top extends into where-now's visual area. */

.timeline-orb {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  z-index: 1;
  line-height: 0;
  pointer-events: none;
}

.timeline-orb img {
  width: 100%;
  display: block;
}

/* Lift .where-now content above the timeline-orb (z:1 in root context)
   that extends up into this section. Background paints below the orb;
   the heading paints above. */
body.spellbound-stories .where-now > .container {
  position: relative;
  z-index: 3;
}

/* The Stories quote carousel reuses the shared `.quote` pattern from
   About (markup, classes, JS — see initQuoteCarousel in main.js). The
   only page-specific concession is the background colour: About's
   default `.quote` sits on an olive band, but on Stories the section
   sits between an olive/bark-tinted `.story-polaroids` above and the
   bark `.story-filters-section` below, so the band needs to be bark
   here to read as part of the continuous dark band. */
body.spellbound-stories .quote {
  background-color: var(--bark);
}

/* The shared .events-filter rule has `margin: 0 auto 60px`, which
   makes sense on Programme + Sign Up where the filter sits ABOVE
   an events grid and needs gap before the cards. On Stories the
   strip sits in its own .story-filters-section (padding: 0) and
   the .alumni-cards section below carries its own top padding, so
   the inherited 60px bottom margin would just add unwanted space.
   Zero it here without touching the shared base rule. */
body.spellbound-stories .events-filter {
  margin: 0 auto 0;
}

/* ═══════════════════════════════════════════════
     PER-PAGE HEADER + SUBNAV THEMING

     The three original HTML files (index.html, about.html, programme.html)
     each set unique values for `body`, `<header>`, `.subnav-wrap`, and
     `.subnav` backgrounds. Merging them into one stylesheet kept the first
     occurrence per selector, so the shared base rules above currently bake
     in the home/about treatment (olive header, olive sub-nav strip).

     We re-apply the per-page variants here, scoped by body classes added
     in functions.php (spellbound_body_classes). The original mapping was:

       index.html      body #111      header olive   no sub-nav
       about.html      body olive     header olive   sub-nav-wrap olive,
                                                     sub-nav pill olive
       programme.html  body bark      header bark    sub-nav-wrap transparent
                                                     (so body bark shows
                                                     through and the orb
                                                     image bleeds up behind
                                                     the floating pill),
                                                     sub-nav pill bark

     About is already correct under the shared rules; we still write the
     class explicitly below so intent is visible to anyone reading the file.
   ═══════════════════════════════════════════════ */

/* Home (front page) — body uses dark grey from the shared rule already.
   No header/sub-nav overrides needed. */

/* About — explicit body bg so the green band reads continuously from the
   header through to the story hero. The shared header / .subnav-wrap /
   .subnav rules above are already olive, so no further overrides. */
body.spellbound-about {
  background: var(--olive);
}

/* Programme — parchment body so the lower half of the page (the
   transparent fade-out at the bottom of .partnership-images and
   the parchment .upcoming + footer area) reads as one continuous
   parchment field. The header band stays bark via the explicit
   #site-header / .subnav / .mobile-menu rules below (the inline
   overrides in functions.php repeat these with !important). The
   .subnav-wrap is left transparent on Programme to let the
   programme-orb-divider image bleed through behind the floating
   sub-nav pill — so the strip between the header and the
   how-we-work hero now reveals parchment rather than bark; the
   orb image and pill sit over that parchment ground. */
body.spellbound-programme {
  background: var(--parchment);
}
body.spellbound-programme #site-header {
  background-color: var(--bark);
}
body.spellbound-programme .subnav-wrap {
  background-color: transparent;
}
body.spellbound-programme .subnav {
  background-color: var(--bark);
}
/* Mobile menu inherits the header's per-page colour so opening the
   off-canvas menu from a bark header doesn't drop the user onto an
   olive backdrop. Scoped with the body class so it composes cleanly
   with the inline overrides in functions.php (which use !important
   for the same rules — both layers agree). */
body.spellbound-programme .mobile-menu,
body.spellbound-learn     .mobile-menu {
  background-color: var(--bark);
}

/* Programme sessions — mark highlight and session-btn hover use the
   SPELL colour (warm light) rather than the default DAWN (peach) seen
   on the homepage sessions. The session cards on Programme sit on a
   bark background; the spell highlight reads more cleanly against bark
   than dawn does. Scoped with the body class so it only overrides on
   the Programme template and doesn't affect the homepage's identical
   .session-card markup. */
body.spellbound-programme .session-card mark,
body.spellbound-programme .session-body mark {
  background: var(--spell);
}
body.spellbound-programme .session-btn:hover {
  background: var(--spell);
}

/* Stories — body bg deferred until the backgrounds pass; header stays
   olive (matches the design screenshot for the Stories page). No sub-nav
   on this template, so .subnav-wrap / .subnav are irrelevant here. */

/* ═══════════════════════════════════════════════
     LEARN PAGE
   ═══════════════════════════════════════════════ */

/* --- HERO ----------------------------------------------------- */

.learn-hero {
  position: relative;
  background: var(--bark);
  color: var(--white);
  overflow: hidden;
  padding: 120px var(--pad-desktop) 120px;
  /* No `isolation: isolate` — that would create a stacking context
     on the hero, which would bundle .learn-hero-content (z:3) into
     it. The orb-divider sibling (z:2 in root) would then paint over
     the whole hero composite, including the headline. Without
     isolation, the hero is just a positioned-with-overflow box;
     .learn-hero-content's z:3 applies in root and beats the orb's
     z:2 cleanly. */
  min-height: 80vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Dawn-coloured glow placeholder for the Figma orb. Real orb image lands
   in the backgrounds pass. */
/* ── LEARN HERO → SPEECH-TECHNIQUES ORB DIVIDER ──
   Same shape as about's .story-orb-divider: zero-height element
   placed BETWEEN two sections in the DOM. Image is absolutely
   positioned and centred ON the section join (translateY(-50%)
   plus translateX(-50%)) so half bleeds UP into .learn-hero and
   half bleeds DOWN into .speech-techniques — same layering rule
   as the homepage's .orb-divider between testimonial and partners.

   z-index: 2 sits above section backgrounds (default 0) but below
   section content at z-index: 3+ — both .learn-hero-content and
   .techniques-list are lifted to z:3 so the orb sits cleanly behind
   everything visible. */
.learn-orb-divider {
  position: relative;
  height: 0;
  overflow: visible;
  pointer-events: none;
  z-index: 2;
  line-height: 0;
}

.learn-orb-divider img {
  position: absolute;
  top: 0;
  left: 50%;
  /* Width scales with viewport WIDTH on wide screens (100vw), but on
     narrow / portrait viewports the image — which has aspect ratio
     1440:1948 (taller than wide) — would render too short to cover
     the hero. min-width:130vh forces the image up so its half-height
     reaches the hero's top edge. With ratio 1948/1440 ≈ 1.35, a width
     of 130vh produces a height of ~176vh, half of which is 88vh —
     just over the hero's `min-height: 80vh`. Horizontal overflow on
     portrait is clipped by .learn-hero's overflow:hidden. */
  width: 100vw;
  min-width: 130vh;
  transform: translate(-50%, -50%);
  display: block;
}

.learn-hero-content {
  position: relative;
  z-index: 3;
  text-align: center;
  max-width: 1200px;
}

.learn-hero-headline {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: clamp(40px, 12vw, 154px);
  line-height: 0.92;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin-bottom: 48px;
  color: var(--white);
}

.learn-hero-subtitle {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(24px, 3vw, 36px);
  line-height: 1.25;
  max-width: 920px;
  margin: 0 auto;
  color: var(--white);
}
.learn-hero-subtitle em {
  font-style: italic;
  font-weight: 400;
}

@media (max-width: 768px) {
  .learn-hero { padding: 80px 20px; min-height: 70vh; }
  .learn-hero-headline { margin-bottom: 32px; }
}

/* When the page renders the sub-nav (passed via get_header() args), pull the
   hero up by the wrap's height (50px) so the hero's bark + dawn-glow show
   THROUGH the transparent .subnav-wrap — matching the design where the glow
   bleeds up to the bottom edge of the site header.

   The .subnav-wrap (z-index: 50) is a sibling of <main> in body's stacking
   context, while .learn-hero sits inside <main> (auto z-index = 0), so the
   sub-nav pill stays on top of the overlap visually. .learn-hero already has
   `isolation: isolate; overflow: hidden`, so the glow's blur stays contained
   within the hero box even after the pull-up. */
body.spellbound-learn .learn-hero {
  margin-top: -50px;        /* matches .subnav-wrap height */
  padding-top: 170px;       /* 120px (default desktop pad-top) + 50px offset */
}

@media (max-width: 768px) {
  body.spellbound-learn .learn-hero {
    padding-top: 130px;     /* 80px (default mobile pad-top) + 50px offset */
  }
}


/* --- SPEECH TECHNIQUES (5 video rows, vertical stack per item) -----
   Per the updated Figma (node 1543:2): each technique stacks
   vertically — pill, title, body, video — with no separating
   dashed rail. Layout is the same on every viewport so we don't
   need a desktop / tablet / mobile axis change; the only responsive
   tweaks are sizing and section padding.

   z-index: 3 on the list lifts technique content above the
   .learn-orb-divider (z:2) bleeding down from the hero into this
   section's top portion. */

.speech-techniques {
  background: var(--bark);
  color: var(--white);
  padding: 60px 0 120px;
  position: relative;
}

.speech-techniques .container {
  position: relative;
}

.techniques-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 120px;
  position: relative;
  z-index: 3;
}

/* Each technique — single centred column. Width capped so the video
   doesn't stretch full container on wide viewports. Pill / title /
   body / video stack in DOM order with margins between them. */
.technique {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 800px;
  margin: 0 auto;
  width: 100%;
}

/* Pill sits at the top of each technique. Standard .pill .pill--<colour>
   styling provides the layout; we only need a margin below to space it
   from the title. */
.technique .pill {
  margin-bottom: 32px;
}

.technique-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(36px, 4vw, 48px);
  line-height: 1.15;
  letter-spacing: -0.005em;
  margin: 0 0 20px;
  color: var(--white);
  text-transform: capitalize;
}
.technique-title em { font-style: italic; }

.technique-body {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.7;
  color: var(--white);
  opacity: 0.92;
  margin: 0 0 40px;
  max-width: 600px;
}

/* Video block — full width of the technique column, capped by parent
   max-width. 5px parchment "frame" matches the inset-border treatment
   used elsewhere in the theme (alumni-card, golden-story-card, etc.). */
.technique-video {
  position: relative;
  width: 100%;
  border: 5px solid var(--parchment);
  border-radius: 2px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: #000;
}

/* The link is now a <button> when the technique has a video URL
   (so clicks open the watch-video modal) and a <div> when it's a
   static thumbnail (no video). The shared rule below covers both
   — button-default resets bring the <button> form back to a flat,
   borderless box matching the previous <a> behaviour. */
.technique-video-link {
  display: block;
  position: relative;
  width: 100%;
  height: 100%;
  color: var(--parchment);
  /* Button resets — only relevant when the element is <button>. */
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: inherit;
  cursor: pointer;
}

.technique-video-link img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 600ms ease, filter 400ms ease;
}

.technique-video-link:hover img {
  transform: scale(1.03);
  filter: brightness(1.05);
}

.technique-video-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.5));
  transition: transform 300ms ease;
}

.technique-video-link:hover .technique-video-play {
  transform: translate(-50%, -50%) scale(1.1);
}

@media (max-width: 1024px) {
  .techniques-list { gap: 96px; }
}

@media (max-width: 767px) {
  .speech-techniques { padding: 40px 0 80px; }
  .techniques-list { gap: 72px; }
  .technique-body { margin-bottom: 28px; }
  .technique .pill { margin-bottom: 24px; }
}


/* --- SECTION DIVIDER (7 parchment circles, .speech-techniques → .resources)
   Sibling element between the two sections, not nested inside either —
   same pattern as .watch-section-divider on the Watch template. The
   `height: 0` zero-height flex container with `align-items: center`
   places the dots ON the line between the two sections regardless of
   either section's padding / margin / positioning. This is more robust
   than the older nested-with-translateY(-50%) approach used on
   .where-now-circles / .feature-circles, because it doesn't depend on
   `top: 0` of an absolute child resolving exactly to the parent
   section's edge (which can drift in subtle cases involving stacking
   contexts and compositor layers, particularly when the page also has
   fixed mix-blend-mode overlays on body::before / body::after). */

.resources-divider {
  position: relative;
  height: 0;
  /* Extend 10px past viewport edges so first and last circles sit
     exactly half off-screen — same edge bleed pattern as
     .footer-circles / .watch-section-divider. */
  margin: 0 -10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  z-index: 10;
}

.resources-divider span {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--parchment);
  flex-shrink: 0;
  display: block;
}


/* --- RESOURCES section ------------------------------ */

.resources {
  background: var(--parchment);
  color: var(--ink);
  padding: 100px 0 120px;
  position: relative;
  /* z-index: 9 lifts .resources above .techniques-list (z-index: 3,
     inside the preceding .speech-techniques) in body's stacking
     context. Without it, the techniques-list's stacking context
     paints over .resources' parchment background where they meet,
     bleeding bark down into the resources section. The +9 value
     stays well below the body's fixed mix-blend-mode overlays
     (paper texture z:500 / film grain z:501) so those still
     layer correctly on top. */
  z-index: 9;
}

.resources-headline {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: clamp(40px, 11vw, 144px);
  line-height: 0.95;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-align: center;
  margin: 0 0 60px;
  color: var(--ink);
}

/* --- Two-row category marquee -------------------------------- */

.resources-marquee {
  margin: 40px 0 80px;
  /* overflow-x clips the horizontal track to the page width;
     overflow-y stays visible so each pill's .pill-bg halo (which
     extends 125% beyond the pill box, creating the screen-blended
     glow) can breathe above and below the marquee bounds without
     getting lopped off. */
  overflow-x: clip;
  overflow-y: visible;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.resources-marquee-row {
  width: 100%;
  /* No overflow:hidden — pills' .pill-bg halos extend 125% beyond
     the pill box (creating the screen-blended glow); clipping at
     the row edges would lop the halos off and read as a hard cut.
     Horizontal clipping is already handled by .resources-marquee
     (the parent has overflow:hidden), so no need to clip again here. */
}

/* Both rows align flush with the page's left edge. Visual offset
   between top and bottom now comes from the differing animation
   directions (top scrolls left, bottom scrolls right) — no manual
   translateX needed. */
.resources-marquee-row--top,
.resources-marquee-row--bottom { transform: none; }

.resources-marquee-track {
  display: flex;
  gap: 32px;
  width: max-content;
  animation: resourcesMarquee 36s linear infinite;
  /* will-change: transform — tells the browser to prepare the
     compositor layer for this element from the start, rather than
     promoting it the moment the transform animation kicks in. The
     promotion timing matters because mix-blend-mode children
     (here, the .pill-bg <img> on every pill, which uses
     mix-blend-mode:screen) blend against the parent's painted
     surface in the compositor layer. Without will-change the layer
     is created on-the-fly when the animation starts, and the screen
     blend briefly operates against a still-transparent composited
     surface before the renderer settles — producing the visible
     "pill backgrounds flash then disappear" behaviour. The
     equivalent .events-filter-scroller declaration uses the same
     pattern and stays stable from frame one. */
  will-change: transform;
}

.resources-marquee-track--left  { animation-direction: normal; }
.resources-marquee-track--right { animation-direction: reverse; }

@keyframes resourcesMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Pause on hover so users can read */
.resources-marquee:hover .resources-marquee-track {
  animation-play-state: paused;
}

/* Pill — uses the shared .pill .pill--<colour> styling defined under
   the homepage's PILL BUTTON block, with a .pill-bg image inside.
   No learn-specific overrides needed. */


/* --- Resource cards grid ------------------------------------- */
/* Per Figma node 1163:651, each resource card mirrors the .event-card
   pattern on programme: a 5px coloured "frame" wraps an inner 1px-
   bordered rectangle that's split 50/50 into an image area (top) and
   a text area (bottom). Differences from .event-card:
     • Meta (CATEGORY + date) overlays the TOP of the image (not the
       bottom-left like event-card's category+title).
     • No bottom CTA button — text fills the bottom half cleanly.
     • Title sits in the BODY (text-half), not overlaid on the image. */

.resources-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 30px;
  margin-top: 60px;
}

.resource-card {
  position: relative;
  border-radius: 4px;
  padding: 5px;
  display: flex;
  flex-direction: column;
}

.resource-card--dusk  { background: var(--dusk);  color: var(--parchment); }
.resource-card--spell { background: var(--spell); color: var(--ink); }
.resource-card--dawn  { background: var(--dawn);  color: var(--ink); }
.resource-card--olive { background: var(--olive); color: var(--parchment); }
.resource-card--bark  { background: var(--bark);  color: var(--parchment); }

/* Inner bordered rectangle — currentColor inherits from the card so
   parchment-bordered on dark cards (dusk/olive/bark), ink-bordered
   on light cards (spell/dawn). */
.resource-card-inner {
  border: 1px solid currentColor;
  border-radius: 2px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* The inner wrapper is an <a> (whole-card link to the resource's
   permalink). Browser user-agent `a:link` (specificity 0,1,1)
   beats `.resource-card-inner` (0,1,0) for `color`, so the title
   and description — both `color: inherit` — would otherwise pick
   up the default link blue instead of the variant text colour
   from the parent .resource-card. Selector below names the
   element (`a.resource-card-inner`) so its specificity (0,1,1)
   matches `a:link`, and we cover every link pseudo-state so
   visited / hover / active all stay in the variant colour too.
   text-decoration also explicitly cleared on every state to
   strip the default anchor underline that would otherwise carry
   down through the title + description. */
a.resource-card-inner,
a.resource-card-inner:link,
a.resource-card-inner:visited,
a.resource-card-inner:hover,
a.resource-card-inner:active,
a.resource-card-inner:focus {
  color: inherit;
  text-decoration: none;
}

/* Image area — 50% of card, with CATEGORY+date overlaid at top */
.resource-card-image {
  position: relative;
  aspect-ratio: 572 / 228;
  flex-shrink: 0;
  overflow: hidden;
  background: var(--ink);
  border-bottom: 1px solid currentColor;
}

.resource-card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* CATEGORY (left) + date (right) overlay at top of image */
.resource-card-meta {
  position: absolute;
  top: 20px;
  left: 25px;
  right: 25px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  z-index: 2;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--parchment); /* Always parchment — overlay sits on dark image */
}

.resource-card-category { /* inherits from .resource-card-meta */ }
.resource-card-date     { /* inherits from .resource-card-meta */ }

/* Body — bottom 50% with title + description */
.resource-card-body {
  padding: 25px 25px 28px;
  display: flex;
  flex-direction: column;
  flex: 1;
  aspect-ratio: 572 / 228;
}

.resource-card-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(28px, 2.5vw, 36px);
  line-height: 1.15;
  margin: 0 0 14px;
  letter-spacing: -0.005em;
  color: inherit;
}
.resource-card-title em { font-style: italic; }

.resource-card-description {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  text-align: justify;
  margin: 0;
  color: inherit;
}

@media (max-width: 768px) {
  .resources { padding: 60px 0 80px; }
  .resources-headline { font-size: clamp(40px, 12vw, 80px); margin-bottom: 32px; }
  .resources-grid {
    grid-template-columns: 1fr;
    gap: 24px;
  }
  .resource-card-body { padding: 20px; }
  .resource-card-meta { top: 16px; left: 18px; right: 18px; }
  .resources-marquee { margin: 24px 0 48px; }
}

/* ═══════════════════════════════════════════════
     WATCH PAGE
   ═══════════════════════════════════════════════ */

/* --- HERO ----------------------------------------------------- */

.watch-hero {
  position: relative;
  height: 78vh;
  min-height: 600px;
  max-height: 900px;
  overflow: hidden;
  isolation: isolate;
  background: var(--bark); /* fallback while image loads */
}

.watch-hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.watch-hero-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Subtle bottom gradient for legibility of overlaid title */
.watch-hero::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(
    180deg,
    rgba(0, 0, 0, 0) 40%,
    rgba(0, 0, 0, 0.45) 100%
  );
  pointer-events: none;
}

.watch-hero-content {
  position: absolute;
  bottom: 80px;
  left: 0;
  right: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 40px;
  align-items: end;
  color: var(--white);
}

.watch-hero-title {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: clamp(40px, 12vw, 154px);
  line-height: 0.92;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 0;
}

.watch-hero-subtitle {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(24px, 3vw, 36px);
  line-height: 1.18;
  margin: 0;
  max-width: 560px;
  justify-self: end;
}
.watch-hero-subtitle em {
  font-style: italic;
  font-weight: 400;
}
.watch-hero-subtitle em {
  font-family: 'PerfectlyNineties', serif;
  font-style: italic;
  font-weight: 400;
}

@media (max-width: 768px) {
  .watch-hero { height: 65vh; min-height: 480px; }
  .watch-hero-content {
    grid-template-columns: 1fr;
    gap: 24px;
    bottom: 48px;
  }
  .watch-hero-subtitle { justify-self: start; }
}


/* --- SECTION DIVIDER (zero-height, floating circles) ---------
   Sits at the boundary between any two sections. Each circle
   straddles the border (top half in the section above, bottom
   half in the section below). Works for any colour pair. */

/* --- SECTION DIVIDER (7 parchment circles, straddling section boundary)
   Same look as .resources-circles on the learn page and .feature-circles
   on the about page, but rendered as a sibling div BETWEEN sections
   (rather than nested inside the section that follows). The watch page
   has three of these — between hero/featured, featured/alumni, and
   alumni/highlights — and .watch-alumni keeps `overflow: hidden` for
   its orb, so a separate sibling div is cleaner than nested circles
   that would need a per-section `overflow-x: clip` exception. */

.watch-section-divider {
  position: relative;
  height: 0;
  /* Extend 10px past viewport edges so first and last circles are
     exactly half off-screen — same edge bleed pattern as
     .resources-circles, .feature-circles, .footer-circles. */
  margin: 0 -10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  pointer-events: none;
  z-index: 10;
}

.watch-section-divider span {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--parchment);
  flex-shrink: 0;
  display: block;
}


/* --- FEATURED section ---------------------------------------- */

.watch-featured {
  background: var(--parchment);
  color: var(--ink);
  padding: 100px 0 100px;
  position: relative;
  overflow: hidden;
}


/* --- Two-row category marquee --------------------------------
   Same pattern as .resources-marquee on learn: pill halos extend
   125% beyond the pill box (the .pill-bg screen-blended glow), so
   the marquee uses overflow-x: clip + overflow-y: visible to bound
   the horizontal scroll while letting halos breathe vertically.
   Both rows flow flush from the left edge — visual offset comes
   from the differing animation directions, not a translateX hack. */

.watch-categories {
  margin: 0 0 80px;
  overflow-x: clip;
  overflow-y: visible;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.watch-categories-row {
  width: 100%;
  /* No overflow:hidden — clipping happens at the .watch-categories
     parent level (overflow-x: clip) so halos can extend up/down
     past each row's bounds. */
}

.watch-categories-track {
  display: flex;
  gap: 32px;
  width: max-content;
  animation: watchCategoriesMarquee 36s linear infinite;
  /* will-change: transform — tells the browser to prepare the
     compositor layer for this element from the start, rather than
     promoting it the moment the transform animation kicks in. The
     promotion timing matters because mix-blend-mode children
     (here, the .pill-bg <img> on every pill, which uses
     mix-blend-mode:screen) blend against the parent's painted
     surface in the compositor layer. Without will-change the layer
     is created on-the-fly when the animation starts, and the screen
     blend briefly operates against a still-transparent composited
     surface before the renderer settles — producing the visible
     "pill backgrounds flash then disappear" behaviour. The same
     fix is applied to .resources-marquee-track and
     .events-filter-scroller (both shipped with will-change from
     the start); this matches them. */
  will-change: transform;
}
.watch-categories-track--left  { animation-direction: normal; }
.watch-categories-track--right { animation-direction: reverse; }

@keyframes watchCategoriesMarquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

.watch-categories:hover .watch-categories-track {
  animation-play-state: paused;
}

/* Pills use the shared .pill .pill--<colour> styling defined in
   the homepage's PILL BUTTON block, with a .pill-bg image inside.
   No watch-specific overrides needed. */


/* --- Video card grid -----------------------------------------
   Per Figma 1121:2646 each watch video card mirrors the inset-border
   pattern used by .event-card on programme and .resource-card on
   learn:
     • Outer .watch-video-card carries the colour bg as a 5px frame
       around an inner bordered rectangle.
     • .watch-video-card-inner holds 1px currentColor border (so the
       border colour inverts with the card colour: parchment on dark
       cards, ink on light) plus rounded corners + overflow: hidden.
     • Inside: thumb on top (16:9 with play-button overlay), footer
       below with title + speaker on the left, duration + dot on the
       right. All footer text is parchment 14px AlteHaasGrotesk Bold
       per the design.
     • No hover lift — cards stay still on hover; thumb image still
       has its slow zoom-in for the watch affordance. */

.watch-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 30px;
}

.watch-video-card {
  position: relative;
  border-radius: 4px;
  padding: 5px;
  display: flex;
  flex-direction: column;
}

.watch-video-card--bark  { background: var(--bark);  color: var(--parchment); }
.watch-video-card--olive { background: var(--olive); color: var(--parchment); }
.watch-video-card--spell { background: var(--spell); color: var(--ink); }
.watch-video-card--dawn  { background: var(--dawn);  color: var(--ink); }
.watch-video-card--dusk  { background: var(--dusk);  color: var(--parchment); }

.watch-video-card-link {
  display: block;
  color: inherit;
  text-decoration: none;
  flex: 1;
}

.watch-video-card-inner {
  border: 1px solid currentColor;
  border-radius: 2px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.watch-video-card-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #000;
  border-bottom: 1px solid currentColor;
}
.watch-video-card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 600ms ease;
}
.watch-video-card-link:hover .watch-video-card-thumb img {
  transform: scale(1.04);
}

.watch-video-card-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 48px;
  height: 48px;
  color: var(--parchment);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.5));
  transition: transform 300ms ease;
  opacity: 0.9;
}
.watch-video-card-link:hover .watch-video-card-play {
  transform: translate(-50%, -50%) scale(1.12);
  opacity: 1;
}

.watch-video-card-footer {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 22px;
  font-family: 'AlteHaasGrotesk', sans-serif;
  /* Background comes from the outer .watch-video-card (showing through
     since the inner has no bg of its own), so footer text sits on the
     same colour as the outer frame. */
}

.watch-video-card-meta {
  min-width: 0;
}

.watch-video-card-title,
.watch-video-card-speaker {
  font-size: 14px;
  font-weight: 700;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.6;
  color: inherit;
}

.watch-video-card-duration {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  flex-shrink: 0;
  color: inherit;
}
.watch-video-card-duration-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
  flex-shrink: 0;
}


/* --- ALUMNI section ------------------------------------------ */

.watch-alumni {
  position: relative;
  background-color: var(--bark);
  background-image: url('assets/images/watch-alumni.jpg'); background-image: image-set(url('assets/images/watch-alumni.webp') type('image/webp'), url('assets/images/watch-alumni.jpg') type('image/jpeg'));
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
  color: var(--parchment);
  padding: 120px 0 100px;
}

/* Orb formerly rendered via .watch-alumni-orb has moved to a real
   background-image on .watch-alumni itself (watch-alumni.jpg, cover
   centre). The standalone div + CSS-radial-gradient placeholder is
   no longer needed and has been removed from both markup and CSS. */


.watch-alumni-intro {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 80px;
  align-items: end;
  position: relative;
  z-index: 1;
  margin-bottom: 80px;
}

.watch-alumni-text {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  margin: 0;
  max-width: 560px;
  color: var(--parchment);
}
.watch-alumni-text strong {
  font-weight: 700;
  font-style: normal;
}
.watch-alumni-text em {
  font-family: 'PerfectlyNineties', serif;
  font-style: italic;
  font-weight: 400;
}

.watch-alumni-title {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  /* Capped at 100px (was 175px) in v1.29.0 — the heading is ACF-
     editable and the client's "ALUMNI HIGHLIGHTS" copy contains the
     unbreakable single word "HIGHLIGHTS", which at 175px is wider
     than the intro grid's right-hand column and overflowed the
     container. CSS has no auto-fit-text primitive, so the pragmatic
     fix is a cap sized to the longest current word; 100px keeps
     "HIGHLIGHTS" inside the column at the 1300px container width
     while still reading as a display-scale heading. Scoped to this
     class only — other big headings keep their original scale. */
  font-size: clamp(96px, 13vw, 100px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin: 0;
  text-transform: uppercase;
  text-align: right;
  color: var(--parchment);
}


/* Alumni horizontal scroller */

.watch-alumni-scroller {
  display: flex;
  gap: 60px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0 var(--pad-desktop) 80px;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
  position: relative;
  z-index: 1;
}
.watch-alumni-scroller::-webkit-scrollbar { display: none; }

.watch-alumni-card {
  flex: 0 0 294px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
  color: var(--parchment);
}

.watch-alumni-card-title {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 0;
  color: inherit;
}

/* Outer frame — parchment bg + 5px padding (the "frame" thickness) +
   2px rounded corners. Mirrors the inset-border pattern used across
   the rest of the theme (event-card, resource-card, watch-video-card,
   alumni-card on stories). */
.watch-alumni-card-image {
  display: block;
  width: 100%;
  background: var(--parchment);
  padding: 5px;
  border-radius: 2px;
}

/* Inner — 1px ink border (because the outer is parchment, ink reads
   for the border line), rounded 2px to match outer; aspect 284:513
   per the Figma for tall portrait video reels. */
.watch-alumni-card-image-inner {
  display: block;
  border: 1px solid var(--ink);
  border-radius: 2px;
  overflow: hidden;
  aspect-ratio: 284 / 513;
  background: #000;
}

.watch-alumni-card-image-inner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 600ms ease;
}
.watch-alumni-card-image:hover .watch-alumni-card-image-inner img {
  transform: scale(1.04);
}

.watch-alumni-card-body {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
  line-height: 1.6;
  margin: 0;
  color: inherit;
  max-width: 294px;
}


/* The "Highlights teaser" (heading + arrow) and the separate
   .watch-highlights grid section that followed were removed when
   the Watch template moved to a single Featured grid driven by
   the Video CPT (filtered in place via the category pills instead
   of split across two sections). Their CSS would otherwise live
   here; intentionally deleted along with the markup. */


/* --- RESPONSIVE ---------------------------------------------- */

@media (max-width: 1024px) {
  .watch-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  .watch-featured  { padding: 60px 0 80px; }
  .watch-categories { margin: 0 0 48px; }
  .watch-grid {
    grid-template-columns: 1fr;
    gap: 18px;
  }
  .watch-alumni { padding: 80px 0 60px; }
  .watch-alumni-intro {
    grid-template-columns: 1fr;
    gap: 32px;
    margin-bottom: 48px;
  }
  .watch-alumni-title { text-align: left; font-size: clamp(40px, 9.75vw, 75px); } /* 3/4 of the desktop clamp — keeps "HIGHLIGHTS" inside the viewport on mobile */
  .watch-alumni-scroller { padding-left: 20px; padding-right: 20px; padding-bottom: 48px; }
  .watch-alumni-card { flex: 0 0 240px; }
}


/* ════════════════════════════════════════════════════════════════
   PARTNERS PAGE
   Bark→dusk page bg with a parchment band for the PARTNER STORIES
   block and a parchment footer (the latter is unique to this page).
   Most building blocks reuse existing patterns: hero matches Stories,
   2-col matches Programme .partnership, centerpiece matches Stories
   .golden-story, polaroids and quote also Stories, partner-logos
   matches the homepage marquee, the 3-up partner-stories grid reuses
   .timeline-item visuals (with dusk-on-parchment colour overrides),
   and the feature card follows the .golden-story-card construction
   in landscape orientation.
   ════════════════════════════════════════════════════════════════ */


/* ── SUBNAV CENTER-ON-PARENT VARIANT ──────────────────────────────
   Used by Partners (registered with `'align' => 'center'` in
   spellbound_get_page_subnavs()). The JS positioning still sets
   .subnav.style.left to the parent link's centre x; this transform
   pulls the pill back by 50% of its own width so its centre lands on
   that anchor — instead of the default left-edge-on-anchor treatment
   that About/Programme/Learn use. Scoped to ≥1025px because the
   subnav wrap is `display: none` below that breakpoint. */

@media (min-width: 1025px) {
  .subnav[data-align="center"] {
    transform: translateX(-50%);
  }
}


/* ── HERO ─────────────────────────────────────────────────────────
   Construction mirrors .stories-hero: full-bleed photo with a fade
   to body-bg at the bottom so the cinematic image dissolves softly
   into the dusk band below. The headline is uppercase Alte Haas
   Grotesk (matches the .where-now-heading family), the subtitle is
   Perfectly Nineties at 42px to match the Figma. */

.partners-hero {
  position: relative;
  min-height: 100vh;
  background: var(--dusk);
  overflow: hidden;
  padding-top: 56px; /* header height — matches .stories-hero */
  display: flex;
  align-items: center;
  justify-content: center;
}

.partners-hero-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.partners-hero-bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}

/* Bottom-50% gradient — transparent at top fading to solid dusk at
   the bottom edge. Matches Figma's "Section Diffuse" overlay; same
   construction as .stories-hero-fade. The rgba triple matches what
   var(--dusk) resolves to (#59748B = 89,116,139). */
.partners-hero-fade {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 50%;
  background: linear-gradient(to top, var(--dusk) 0%, rgba(89, 116, 139, 0) 100%);
  pointer-events: none;
  z-index: 1;
}

.partners-hero-content {
  position: relative;
  z-index: 3;
  text-align: center;
  color: var(--parchment);
  padding-top: 80px;
  padding-bottom: 80px;
}

.partners-hero-headline {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12.5vw, 175px);
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin: 0 0 56px;
  color: var(--parchment);
}

.partners-hero-subtitle {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(22px, 2.9vw, 42px);
  line-height: 1.17;
  max-width: 920px;
  margin: 0 auto;
  color: var(--parchment);
}

.partners-hero-subtitle em {
  font-style: italic;
  font-family: 'PerfectlyNineties', serif;
}


/* ── COMMUNITY / CORPORATE 2-COL  (.partnership reuse) ────────────
   The .partnership block itself is shared with Programme, where it
   sits inside .whos-it-for. Here we wrap it in our own section so it
   gets independent vertical padding and lives directly on the dusk
   body band. */

.partners-partnership {
  background: var(--dusk);
  padding: 0 0 120px;
  position: relative;
}

.partners-partnership .partnership {
  /* Override the 80/120 padding the shared rule applies — the
     section wrapper above handles its own breathing room. */
  padding: 0;
}


/* ── PARTNERS-SPECIFIC POLAROIDS ──────────────────────────────────
   The .story-polaroids shared rule sets bg olive (matches Stories).
   On Partners we want dusk so the polaroid composite reads as part
   of the dusk band. The rest of the .story-polaroids img sizing and
   layout rules carry over unchanged. */

.partners-polaroids {
  background-color: var(--dusk);
}


/* ── WORK WITH US (heading + deferred form) ───────────────────────
   Anchor target for the "Work With Us" subnav link. Heading sits
   centred on dusk; the form region is currently a placeholder card
   (see template comment) sized to roughly the eventual form's
   footprint so swapping it later doesn't reflow the page. */

.work-with-us {
  background: var(--dusk);
  padding: 100px 0 60px;
  position: relative;
}

.work-with-us-heading {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12vw, 175px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin: 0 0 60px;
  color: var(--parchment);
  text-align: center;
}

/* ── CONTACT FORM ─────────────────────────────────────────────────
   1240×350 outer card. The visible parchment ground sits OUTSIDE a
   1230×340 ink-bordered grid, with a 5px frame of parchment between
   them. Hairlines between cells come from `border-top` on every row
   (except the first) and `border-left` on every cell (except the
   first cell of each row) — so adjacent cells share one line and we
   never get double-thick borders. Cell heights default to 59px; the
   message row overrides to 163px to host the textarea.

   Inputs and textareas are positioned to fill their cell with
   18px×30px internal padding so the value/placeholder lands at the
   same top-left anchor that the Figma labels are pinned to. Inputs
   are uppercase + bold (matching the placeholder). The textarea is
   regular-weight + sentence-case for the typed value, but its
   placeholder text reverts to bold uppercase via ::placeholder so
   it reads as a label until typing starts.

   Submit button hover: dusk fill, parchment text, smoothly
   transitioned. Default state: transparent fill, ink text.
   Checkbox: hidden native input + custom 28px circle that fills with
   dusk on :checked. */

.work-with-us-form {
  max-width: 1240px;
  margin: 0 auto;
}

.work-with-us-form-card {
  background: var(--parchment);
  border-radius: 2px;
  padding: 5px;
  /* The 5px parchment frame around the inner grid. The card itself
     has no visible border — the frame is just the gap between the
     parchment ground and the grid's ink border below. */
}

.work-with-us-form-grid {
  border: 1px solid var(--ink);
  border-radius: 2px;
  /* The grid uses flex rows because each row has its own column count
     (2 / 3 / 1 / 2). Borders live on the rows + cells, not on the
     grid itself, so individual cells can collapse on responsive
     breakpoints without leaving orphan border segments. */
}

.work-with-us-form-row {
  display: flex;
  border-top: 1px solid var(--ink);
  min-height: 59px;
}
.work-with-us-form-row:first-child {
  border-top: 0;
}

/* Row variant — message textarea row is taller per Figma (163px). */
.work-with-us-form-row--message {
  min-height: 163px;
}

.work-with-us-form-cell {
  position: relative;
  flex: 1 1 0;
  min-width: 0; /* allow flex children to shrink below their content
                   width so 3-col on narrow viewports doesn't overflow */
  border-left: 1px solid var(--ink);
}
.work-with-us-form-cell:first-child {
  border-left: 0;
}

/* Inputs + textareas absolutely fill their cell, with internal
   padding picking up the Figma label anchor (top:18, left:30). */
.work-with-us-form-input,
.work-with-us-form-textarea {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 18px 30px;
  background: transparent;
  border: 0;
  outline: 0;
  border-radius: 0;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
}

/* Textarea: typed value is regular weight + sentence case (per the
   filled-state Figma reference). The placeholder still renders bold
   uppercase via the ::placeholder block below so it reads as a label
   until the user starts typing. */
.work-with-us-form-textarea {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  resize: none;
  overflow-y: auto;
}

/* Placeholder styling — bold uppercase ink, opacity 1 so it reads
   exactly like the Figma's static label text. Each browser prefix is
   listed separately because grouping them in a single selector list
   makes the whole list invalid in browsers that don't recognise one
   of the prefixes (per spec). */
.work-with-us-form-input::placeholder,
.work-with-us-form-textarea::placeholder {
  color: var(--ink);
  opacity: 1;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.work-with-us-form-input::-webkit-input-placeholder,
.work-with-us-form-textarea::-webkit-input-placeholder {
  color: var(--ink);
  opacity: 1;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Focus ring — subtle. The cell border-left/top already gives a
   strong visual frame, so the focus indicator just slightly tints
   the field bg rather than adding an outline that would clash with
   the geometry. */
.work-with-us-form-input:focus,
.work-with-us-form-textarea:focus {
  background: rgba(89, 116, 139, 0.06); /* var(--dusk) at 6% */
}

/* Browser auto-fill (Chromium/WebKit) usually paints a hard yellow
   bg on inputs that breaks the parchment surface. Override with the
   parchment colour. The huge inset-shadow trick is the long-standing
   workaround for the fact that bg-color on autofill is ignored. */
.work-with-us-form-input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px var(--parchment) inset;
  -webkit-text-fill-color: var(--ink);
  caret-color: var(--ink);
}


/* ── Checkbox cell ──
   The native input is visually hidden (still focusable + form-
   submittable) and a sibling span renders the visible 28px circle.
   When :checked, the circle fills with dusk. The label text sits to
   the right of the circle. The whole row is wrapped in a <label> so
   clicking text or circle toggles the input. */

.work-with-us-form-cell--checkbox {
  display: flex;
  align-items: center;
  padding: 0 30px;
}

.work-with-us-form-checkbox {
  display: flex;
  align-items: center;
  gap: 22px;
  cursor: pointer;
  /* The whole row is the click target; the cursor change makes that
     affordance explicit. */
}

.work-with-us-form-checkbox-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  /* Keep it positioned absolutely off-flow rather than display:none
     so it's still focusable for keyboard users. Visible focus is
     transferred to the custom circle via :focus-visible below. */
}

.work-with-us-form-checkbox-circle {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--ink);
  background: transparent;
  flex-shrink: 0;
  display: block;
  transition: background 0.2s ease;
}

.work-with-us-form-checkbox-input:checked + .work-with-us-form-checkbox-circle {
  background: var(--dusk);
}

.work-with-us-form-checkbox-input:focus-visible + .work-with-us-form-checkbox-circle {
  outline: 2px solid var(--dusk);
  outline-offset: 2px;
}

.work-with-us-form-checkbox-text {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}


/* ── Submit cell ──
   Cell has no internal padding — the button fills its parent
   completely so the dusk hover bg paints right up to the cell's
   border edges. Default: transparent bg, ink text, cursor:pointer.
   Hover/focus-visible: dusk bg, parchment text. */

.work-with-us-form-cell--submit {
  padding: 0;
}

.work-with-us-form-submit {
  width: 100%;
  height: 100%;
  min-height: 59px;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  transition: background 0.25s ease, color 0.25s ease;
  -webkit-appearance: none;
  appearance: none;
}

.work-with-us-form-submit:hover,
.work-with-us-form-submit:focus-visible {
  background: var(--dusk);
  color: var(--parchment);
}


/* ── PARTNER STORIES HEADLINE  (parchment band start) ─────────────
   Large editorial heading that opens the parchment band. Anchor
   target for the "Partner Stories" subnav link. */

.partner-stories-headline {
  background: var(--parchment);
  padding: 100px 0 80px;
  position: relative;
}

.partner-stories-heading {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12vw, 175px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin: 0;
  color: var(--dusk);
  display: flex;
  flex-direction: column;
  text-align: center;
}

.partner-stories-heading-line {
  display: block;
}


/* ── PARTNER STORIES TIMELINE  (.timeline carousel reuse) ─────────
   The carousel structure is shared with Stories' where-are-they-now
   timeline; per-page colour flips (parchment bg, dusk axis + arrows
   + items) live in the body.spellbound-partners block lower in this
   file. The .partner-stories-timeline modifier on the section is
   reserved for any future Partners-only structural tweaks; right now
   all the page-specific styling is body-class scoped instead. */


/* ── FEATURE CARD  (landscape polaroid-style) ─────────────────────
   Same construction as .golden-story-card but landscape, sitting on
   the dusk band that bookends the parchment partner-stories area.
   Image is partners-video.jpg (a 1229×688 cinematic still). The
   surrounding frame is parchment with a 5px ground, an ink hairline
   border, and two ink perforation dots on the left edge — same
   polaroid motif as the golden-story card. */

.partners-feature {
  background: var(--dusk);
  padding: 80px 0 100px;
  position: relative;
}

.partners-feature-card {
  position: relative;
  background: var(--parchment);
  border-radius: 2px;
  padding: 5px;
  box-shadow: 0 0 9px rgba(0, 0, 0, 0.1);
  max-width: 1240px;
  margin: 0 auto;
}

.partners-feature-card-inner {
  position: relative;
  border: 1px solid var(--ink);
  border-radius: 2px;
  overflow: hidden;
  /* Sets a hard ratio so the image always fills a consistent
     landscape slot — 16:9-ish per Figma (1240×698 ≈ 1.78). */
  aspect-ratio: 1240 / 698;
}

/* Two perforation circles on the left edge — same motif as
   .golden-story-card's polaroid (in stories) but using ink-coloured
   dots so they read against the dark image inside. */
.partners-feature-card-inner::before,
.partners-feature-card-inner::after {
  content: "";
  position: absolute;
  left: 26px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--ink);
  z-index: 2;
}

.partners-feature-card-inner::before { top: 24px; }
.partners-feature-card-inner::after  { bottom: 24px; }

.partners-feature-card-inner img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}


/* ── PARTNERS QUOTE  (.story-quote, on dusk) ──────────────────────
   The shared .story-quote rule sets bg bark; on Partners the section
   sits inside the dusk band, so we need to flip the bg. Scoped via
   the .partners-quote modifier so we don't disturb Stories. */

.partners-quote {
  background: var(--dusk);
}


/* ── PAGE SCOPE: body.spellbound-partners ─────────────────────────
   Lives with the other body-class blocks (about / programme / learn)
   higher up in this file, but extending it here keeps the partners
   contributions co-located. The inline overrides in functions.php
   handle bg/colour swaps that need to fire before this stylesheet
   loads (header, subnav-wrap, footer); the rules below are layout-
   level tweaks that only matter once the page renders. */

body.spellbound-partners {
  background: var(--dusk);
}

/* Golden-story default bg is olive (matches Stories where it sits inside
   the olive band). On Partners the section sits within the dusk body
   continuum, so flip the bg so it reads as part of that band rather than
   an olive island. */
body.spellbound-partners .golden-story {
  background: var(--dusk);
}

/* Partnership headings on Partners are LEFT-aligned per Figma
   (Programme uses the default centred treatment). */
body.spellbound-partners .partnership-column-heading {
  text-align: left;
  margin-bottom: 32px;
}

/* Body copy is also left-aligned (not justified, as Programme
   defaults). Keeping it scoped so Programme's intentional justify
   stays untouched. */
body.spellbound-partners .partnership-column-copy {
  text-align: left;
}

/* The partner-stories timeline sits on parchment (the headline band
   above continues into the timeline). Default .timeline bg is olive
   (matches Stories' where-are-they-now); flip it here. The axis line
   and arrows default to parchment so they read against olive — flip
   both to dusk so they read against the lighter ground. */
body.spellbound-partners .timeline {
  background: var(--parchment);
}
body.spellbound-partners .timeline-axis {
  background: var(--dusk);
}
body.spellbound-partners .timeline-arrow {
  color: var(--dusk);
}

/* The 3-up partner-stories items also need to flip
   parchment-on-olive colours to dusk-on-parchment. Each child element
   (.timeline-date / .timeline-title / .timeline-body) declares
   `color: var(--parchment)` directly in its own rule, so inheritance
   from .timeline-item alone won't override them — we have to target
   each child explicitly. The dot/stem decorations get a matching
   background flip. */
body.spellbound-partners .timeline-item,
body.spellbound-partners .timeline-date,
body.spellbound-partners .timeline-title,
body.spellbound-partners .timeline-body {
  color: var(--dusk);
}
body.spellbound-partners .timeline-top-dot,
body.spellbound-partners .timeline-stem,
body.spellbound-partners .timeline-bottom-dot {
  background: var(--dusk);
}

/* Footer overrides — bg flip is also handled by the inline overrides in
   functions.php (priority 99 wp_head <style>); these are belt-and-braces
   in case those don't load (e.g. if the wp_head action is hijacked by
   another plugin earlier in the chain). The footer text is in two child
   patterns (.footer-left span + .footer-right a) so we target both via
   .footer-info as a single ancestor — that also catches any future
   social-menu markup that wp_nav_menu might emit if a custom 'social'
   menu is registered (it would render as <li><a>, still inside
   .footer-info, still picked up by this descendant selector). The
   !important matches the inline override's strength so the rules behave
   identically whether they fire from inline or from this stylesheet. */
body.spellbound-partners .footer {
  background-color: var(--parchment);
}
body.spellbound-partners .footer-info,
body.spellbound-partners .footer-info span,
body.spellbound-partners .footer-info a {
  color: var(--dusk) !important;
}


/* ── PARTNERS RESPONSIVE ──────────────────────────────────────────
   Mirrors the breakpoints used elsewhere (1024 = tablet, 768 = phone). */

@media (max-width: 1024px) {
  .partners-hero { min-height: 80vh; }
  .partners-hero-content { padding-top: 56px; padding-bottom: 56px; }

  .partners-partnership { padding: 0 0 80px; }
  .partners-partnership .partnership {
    grid-template-columns: 1fr;
    gap: 60px;
  }
  .partners-partnership .partnership-divider { display: none; }

  .work-with-us { padding: 80px 0 40px; }

  .partners-feature { padding: 60px 0 80px; }

  /* Form: keep the desktop-style multi-col grid on tablet too — just
     the inputs' internal padding shrinks slightly so the placeholder
     labels don't crowd the cell edges on a narrower viewport. The
     cell heights stay 59px. */
  .work-with-us-form-input,
  .work-with-us-form-textarea {
    padding: 16px 20px;
  }
  .work-with-us-form-cell--checkbox { padding: 0 20px; }
  .work-with-us-form-checkbox { gap: 16px; }
}

@media (max-width: 768px) {
  .partners-hero { min-height: 70vh; }
  .partners-hero-headline { margin-bottom: 32px; }

  .partner-stories-headline { padding: 60px 0 24px; }
  .work-with-us-heading { margin-bottom: 40px; }

  /* Form on phone — collapse every row to a single stacked column.
     Border-left becomes border-top; the row's own border-top is
     suppressed because each cell now provides its own. The message
     row keeps its taller min-height; everything else is 59px each.
     The submit cell still spans full width so the dusk hover paints
     across the full bottom of the card. */
  .work-with-us-form-row {
    flex-direction: column;
    border-top: 0; /* per-cell top border takes over instead */
  }
  .work-with-us-form-row:first-child .work-with-us-form-cell:first-child {
    /* Very first cell of the whole grid — no top border (the grid's
       outer ink border handles that edge). */
    border-top: 0;
  }
  .work-with-us-form-cell {
    flex: 0 0 auto;
    min-height: 59px;
    border-left: 0;
    border-top: 1px solid var(--ink);
  }
  .work-with-us-form-row--message .work-with-us-form-cell {
    min-height: 163px;
  }

  /* Compact internal padding so labels read at the top-left of each
     stacked cell on narrow viewports. */
  .work-with-us-form-input,
  .work-with-us-form-textarea {
    padding: 14px 18px;
  }
  .work-with-us-form-cell--checkbox {
    padding: 0 18px;
    min-height: 59px;
  }
  .work-with-us-form-cell--submit {
    /* Reset desktop padding so the button still fills the (now
       stacked) cell — the dusk hover bg paints right to the edges. */
    padding: 0;
  }
  .work-with-us-form-checkbox-text { font-size: 12px; letter-spacing: 0.04em; }
}


/* ════════════════════════════════════════════════════════════════
   SIGN UP PAGE
   Olive body throughout with a local bark band for the FAQs section
   near the bottom, bookended by section dividers. The hero is text-
   only on the olive ground (no hero photo); polaroids reuse the
   .story-polaroids construct from Stories / Partners; upcoming
   events reuses Programme's .upcoming + .events-grid + .event-card
   pattern with body-class overrides recolouring the heading. The
   filter pill row reuses Learn's .resources-marquee / --row / --track
   pattern (single row, painterly .pill / .pill-bg images). The FAQs
   grid is a new pattern.
   ════════════════════════════════════════════════════════════════ */


/* ── HERO  (text-only on olive) ────────────────────────────────────
   No photo bg, no fade gradient — just the editorial headline plus
   subtitle centred on the olive body. The headline matches the
   175px Alte-Haas-Grotesk uppercase scale shared with .where-now-
   heading and Partners' "Work With Us" / "Partner Stories" titles.
   The subtitle reuses the Perfectly Nineties 42px treatment from
   Partners' hero subtitle.

   Hero has NO own background (inherits body olive) so the
   .story-orb-divider rendered immediately above the hero in the
   template can extend its glow down into this section unobstructed.
   The headline and subtitle each carry z-index:3 + position:relative
   to layer above the orb (which sits at z-index:2), the same
   pattern About uses with .story-content. */

.signup-hero {
  position: relative;
  padding: 140px 0 100px;
  text-align: center;
}

.signup-hero-headline {
  position: relative;
  z-index: 3;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12.5vw, 175px);
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin: 0 0 56px;
  color: var(--parchment);
}

.signup-hero-subtitle {
  position: relative;
  z-index: 3;
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(22px, 2.9vw, 42px);
  line-height: 1.17;
  max-width: 920px;
  margin: 0 auto;
  color: var(--parchment);
}

.signup-hero-subtitle em {
  font-style: italic;
  font-family: 'PerfectlyNineties', serif;
}


/* ── POLAROIDS  (.story-polaroids modifier) ───────────────────────
   .story-polaroids defaults to olive bg, which is already the body
   bg on Sign Up — so the section visually merges with the olive
   band above and below. The modifier exists only for any future
   page-specific tweaks; right now it's a no-op selector reserved
   to keep markup explicit about which page owns the section. */

.signup-polaroids {
  /* No overrides needed — inherits the .story-polaroids defaults. */
}


/* ── FAQS  ─────────────────────────────────────────────────────────
   Bark band that holds the "FAQS" heading + a 3-column grid of
   questions/answers. Section dividers (rendered separately in the
   template) sit at the top + bottom edges to bridge the olive bg
   above and below.

   Each column is its own vertical stack of two FAQ articles
   separated by a hairline. Columns are separated by a vertical
   hairline (border-left on every column except the first). Because
   the row-1 question + answer in each column has different lengths,
   the in-column horizontal divider lands at a different y position
   in each column — this is what the Figma shows, and it falls out
   naturally from per-column border placement (no need to align the
   dividers across columns). */

.faqs {
  background: var(--bark);
  color: var(--parchment);
  padding: 100px 0 140px;
  position: relative;
  /* overflow:hidden clips the .faqs-orb (a soft glow rendered as a
     full-width image absolutely positioned at the section's vertical
     centre) to the section bounds. Without this, the orb would
     bleed into the section dividers above and below. */
  overflow: hidden;
}

/* Orb behind FAQ content — same signup-orb-1.png as the top of the
   page, repeated here as an atmospheric glow centred in the section.
   Rendered at z-index:1 over the bark bg but below the .container
   (z-index:2) that holds the heading + grid. */
.faqs-orb {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  line-height: 0;
  z-index: 1;
}

.faqs-orb img {
  display: block;
  width: 100%;
  height: auto;
}

/* FAQ content layered above the orb. */
.faqs > .container {
  position: relative;
  z-index: 2;
}

.faqs-heading {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12vw, 175px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin: 0 0 80px;
  color: var(--parchment);
  text-align: center;
}

.faqs-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* No gap on the grid — column dividers come from .faqs-col's
     border-left, and we want adjacent columns to share a single
     hairline rather than have a gap-coloured stripe between them. */
  gap: 0;
}

.faqs-col {
  position: relative;
  border-left: 1px solid var(--parchment);
  /* No horizontal padding here — padding lives on .faq instead so
     the horizontal divider between FAQ items spans the FULL column
     width (column-edge to column-edge), and the circular endcaps on
     that divider land exactly where the vertical column dividers
     are (visual cross-points). */
}

.faqs-col:first-child {
  border-left: 0;
}

/* Circle endcaps on the vertical column dividers — pinned to the
   top and bottom edges of each non-first column's left border. The
   translate(-50%, -50%) pulls the dot's centre onto the border line
   so the line visually terminates at the dot's centre rather than
   continuing through it. Same pattern .partnership-divider and
   .timeline-stem use elsewhere. */
.faqs-col:not(:first-child)::before,
.faqs-col:not(:first-child)::after {
  content: "";
  position: absolute;
  left: 0;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--parchment);
  transform: translate(-50%, -50%);
  z-index: 2;
}

.faqs-col:not(:first-child)::before { top: 0; }
.faqs-col:not(:first-child)::after  { top: 100%; }

.faq {
  padding: 0 50px;
  /* Horizontal breathing room for the question + answer text within
     the column. The column itself has no padding, so the .faq's outer
     box width = column width — meaning the border-top below extends
     edge to edge across the column (and the ::before/::after dots
     land on the column boundaries where vertical dividers meet). */
}

.faq:not(:first-child) {
  position: relative;
  border-top: 1px solid var(--parchment);
  padding-top: 60px;
  margin-top: 60px;
}

.faq:first-child {
  padding-top: 0;
}

.faq:last-child {
  padding-bottom: 0;
}

/* Circle endcaps on the in-column horizontal dividers — pinned to
   the left and right edges of the FAQ box (= column boundaries).
   Same translate trick: dot centred on the border line, so the line
   terminates cleanly at the dot. For columns 2 and 3 the left dot
   lands directly on top of that column's vertical divider top/bottom
   dot — both pseudos render the same parchment circle, so they
   visually merge into a single dot at the cross-point. For column
   1's left edge and column 3's right edge there's no vertical
   divider, so the dot is just the line's terminus. */
.faq:not(:first-child)::before,
.faq:not(:first-child)::after {
  content: "";
  position: absolute;
  top: 0;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: var(--parchment);
  transform: translate(-50%, -50%);
  z-index: 2;
}

.faq:not(:first-child)::before { left: 0; }
.faq:not(:first-child)::after  { left: 100%; }

.faq-question {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: 32px;
  line-height: 1.17;
  letter-spacing: 0;
  color: var(--parchment);
  margin: 0 0 32px;
}

.faq-answer {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  font-size: 14px;
  line-height: 1.6;
  color: var(--parchment);
  text-align: justify;
  text-indent: 1.5em;
  margin: 0;
}


/* ── PAGE SCOPE: body.spellbound-sign-up ──────────────────────────
   Most Sign Up styling lives in the section blocks above, with a
   handful of body-scoped overrides here for shared elements that
   need a per-page tweak. */

body.spellbound-sign-up {
  background: var(--olive);
}

/* "UPCOMING EVENTS" headline on Sign Up sits on olive (parchment
   on Programme), so flip the text colour to parchment and bump the
   size to match the larger 175px scale per the Figma. */
body.spellbound-sign-up .upcoming {
  background: transparent; /* let body olive show through */
}

body.spellbound-sign-up .upcoming-heading {
  color: var(--parchment);
  font-size: clamp(40px, 12vw, 175px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  margin-bottom: 60px;
}


/* ── SIGN UP RESPONSIVE ───────────────────────────────────────────
   1024 = tablet, 768 = phone — same breakpoints as elsewhere. */

@media (max-width: 1024px) {
  .signup-hero { padding: 100px 0 80px; }
  .signup-hero-headline { margin-bottom: 40px; }

  .faqs { padding: 80px 0 100px; }
  .faqs-heading { margin-bottom: 60px; }
  .faqs-grid {
    grid-template-columns: 1fr 1fr;
  }
  /* On the 2-col layout the third column wraps to a new row at the
     left edge of the grid. It needs a top border (separator from the
     row above) with the same circle-endcap treatment as the
     in-column horizontal dividers. The :nth-child(odd):not(:first-
     child) selector picks any odd-indexed column that isn't the
     very first one — i.e. the start of any wrapped row. */
  .faqs-col:nth-child(odd):not(:first-child) {
    border-left: 0;
    border-top: 1px solid var(--parchment);
    margin-top: 60px;
    padding-top: 60px;
    /* The wrap-row column itself becomes a horizontal-divider host
       on tablet — borrow the same endcap pseudos used on .faq above.
       But .faqs-col already uses ::before/::after for the vertical
       divider's top + bottom dots; on the wrapped layout those
       pseudos repurpose to the horizontal divider's endcaps. */
  }
  .faqs-col:nth-child(odd):not(:first-child)::before,
  .faqs-col:nth-child(odd):not(:first-child)::after {
    /* Reposition the inherited pseudos: instead of top:0/top:100% on
       the left border, put them at left:0/left:100% on the top
       border. */
    top: 0;
    left: 0;
  }
  .faqs-col:nth-child(odd):not(:first-child)::after {
    top: 0;
    left: 100%;
  }
}

@media (max-width: 768px) {
  .signup-hero { padding: 80px 0 60px; }
  .signup-hero-headline { margin-bottom: 32px; }

  .faqs { padding: 60px 0 80px; }
  .faqs-grid {
    grid-template-columns: 1fr;
  }
  /* Phone: every column stacks. Reset the wrap-row overrides from
     the 1024 block (since wrapping isn't a concept at 1-col), then
     give every non-first column a top border with the standard
     endcap pseudos. */
  .faqs-col:nth-child(odd):not(:first-child),
  .faqs-col:not(:first-child) {
    border-left: 0;
    border-top: 1px solid var(--parchment);
    margin-top: 60px;
    padding-top: 60px;
  }
  .faqs-col:not(:first-child)::before {
    top: 0;
    left: 0;
  }
  .faqs-col:not(:first-child)::after {
    top: 0;
    left: 100%;
  }
  .faq-question { font-size: 28px; margin-bottom: 24px; }
}


/* ════════════════════════════════════════════════════════════════
   CONTACT PAGE
   Olive body throughout (header included), bark footer (set via the
   inline overrides in functions.php). Two sections only: a hero
   pairing the big CONTACT headline with a small To/From envelope-
   style address block, and a form section reusing the Partners
   .work-with-us-form structure verbatim with Contact's own field
   set. The footer's own .footer-circles handle the visual seam at
   the olive↘bark transition; no separate section divider needed.
   ════════════════════════════════════════════════════════════════ */


/* ── HERO  (CONTACT headline + To/From envelope block) ────────────
   Two-column flex row on desktop: the headline takes whatever space
   it needs on the left, the envelope sits on the right vertically
   centred against the headline. On phone the row collapses to a
   stack — headline first, envelope below — so the envelope's small
   text doesn't get crushed against the headline on narrow widths. */

.contact-hero {
  position: relative;
  padding: 200px 0 60px;
}

.contact-hero-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 60px;
  /* The envelope block aligns to the bottom of the headline so its
     "From hello@spellbound.com" line sits flush with the headline's
     baseline — gives the composition a stronger horizontal anchor
     than centre-aligning would. The headline's bottom is the visible
     anchor (the descenders sit slightly lower) but flex-end is close
     enough to read as aligned without sub-pixel tuning. */
}

.contact-hero-headline {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  font-size: clamp(40px, 12.5vw, 175px);
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin: 0;
  color: var(--parchment);
}

/* Envelope block — uses a definition list because semantically that's
   what "To: ___, From: ___" is: pairs of terms and definitions. The
   grid layout puts each label (dt) flush left and each value (dd)
   flush against it on the right of the same row. Both lines share
   the same y row; the second pair stacks below. */

.contact-hero-envelope {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 24px;
  row-gap: 0;
  margin: 0;
  /* The envelope sits to the right of the headline. align-items
     handled by the parent flex .contact-hero-row, so the block as a
     whole is vertically centred against the headline by default. */
}

.contact-hero-envelope dt {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400; /* regular weight for the To / From labels */
  font-size: 14px;
  line-height: 22.457px;
  color: var(--parchment);
  margin: 0;
}

.contact-hero-envelope dd {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700; /* bold uppercase for the recipient + email */
  font-size: 14px;
  line-height: 22.457px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--parchment);
  margin: 0;
  white-space: nowrap;
}


/* ── FORM SECTION  (wrapper for the .work-with-us-form card) ──────
   Just provides bottom padding so the form has space before the
   footer. The form card itself handles its own max-width (1240px)
   and centring via the existing .work-with-us-form rules. */

.contact-form-section {
  position: relative;
  padding: 0 0 100px;
}


/* ── PAGE SCOPE: body.spellbound-contact ──────────────────────────
   Belt-and-braces backups for the inline overrides in functions.php
   in case those don't load (e.g. plugin hijacks wp_head priority
   99). The inline rules always win when present (!important); this
   block kicks in otherwise. */

body.spellbound-contact {
  background: var(--olive);
}

body.spellbound-contact .footer {
  background-color: var(--bark);
}


/* ── CONTACT RESPONSIVE ───────────────────────────────────────────
   1024 = tablet, 768 = phone — same breakpoints as elsewhere. */

@media (max-width: 1024px) {
  .contact-hero { padding: 140px 0 50px; }
  .contact-hero-row { gap: 40px; }
  .contact-form-section { padding-bottom: 80px; }
}

@media (max-width: 768px) {
  .contact-hero { padding: 100px 0 40px; }
  /* Stack: headline first, envelope below. align-items center keeps
     the envelope visually centred under the (now smaller) headline
     for a tidier composition on narrow viewports. */
  .contact-hero-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 28px;
  }
  .contact-form-section { padding-bottom: 60px; }
}


/* ════════════════════════════════════════════════════════════════
   APPLY NOW MODAL
   Sitewide dialog rendered once in the footer template-part. Triggered
   by any element with `data-modal-target="apply-now"` (currently the
   .event-card-btn buttons on Programme + Sign Up). The form uses a
   parallel rule set rather than reusing .work-with-us-form because
   the row composition is different — three text-input rows, four
   radio-group rows (each with a per-row checked-circle colour), and
   a full-width submit row — and trying to share a single rule set
   would mean threading exceptions through every existing selector.
   ════════════════════════════════════════════════════════════════ */


/* ── Modal container ──────────────────────────────────────────────
   Fixed full-viewport overlay. Hidden by default via opacity:0 +
   visibility:hidden so the dialog still occupies the DOM (assistive
   tech can find it after `aria-hidden=false` flips), and a 0.25s
   fade animates the entry. The .is-open class on the modal element
   is what flips the aria-hidden attribute via JS — CSS reads the
   class to drive the visual. */

.apply-now-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
  display: flex;
  /* flex-start (not center) so the card always sits at the top of
     the scroll container. Center-aligning a flex item that's taller
     than its container creates a known clipping bug — you can't
     scroll up to see the top — and even when it fits, top-aligned
     reads more naturally for a form. The close button's negative
     top offset means we need padding-top ≥ that offset so the
     button has room above the card; 60px clears the desktop -36px
     and mobile -32px positions comfortably. */
  align-items: flex-start;
  justify-content: center;
  padding: 60px 20px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.apply-now-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}

/* Body scroll lock when the modal is open. The class is toggled by
   JS at the same time as the modal's .is-open. */
body.has-modal-open {
  overflow: hidden;
}

/* Dimmer behind the dialog. Click to close (data-modal-close on the
   element). Slightly tinted with --ink (the brand's ink black) at
   60% opacity so it reads as part of the brand rather than a
   generic neutral grey.

   Position is FIXED (not absolute) so the overlay covers the
   viewport regardless of how far the user has scrolled inside the
   modal's overflow-y:auto container. With the overlay as a regular
   absolute child of .apply-now-modal it scrolled with the modal's
   contents and pulled away from the bottom edge once content
   overflowed — visible most clearly on mobile where the form is
   always taller than the viewport. Fixed positioning keeps the
   overlay pinned to the viewport throughout. */
.apply-now-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(29, 30, 27, 0.6); /* var(--ink) at 60% */
}

/* The modal card — sits over the overlay, centred. Reuses the
   parchment + ink-border construction the on-page contact forms
   use, scaled to the Figma's 1240px wide spec. The card has its
   own max-width so it doesn't stretch on ultra-wide screens; the
   surrounding modal padding (40px each side) gives breathing room
   on viewports just slightly wider than 1240. */
.apply-now-modal-card {
  position: relative;
  width: 100%;
  max-width: 1240px;
  /* Vertical centring without breaking mobile scroll. With the
     parent's `align-items: flex-start` as the fallback, auto top +
     bottom margins distribute the flex container's free vertical
     space — when the card fits in the viewport (desktop), that
     space is divided equally above and below, centring the card.
     When the card is taller than the viewport (mobile), free space
     is zero, auto margins compute to zero, and align-items:flex-
     start kicks in so the card sits at the top of the scroll
     container — keeping the close button and the form's first row
     accessible without forcing a scroll-up.

     Horizontal: justify-content:center on the parent already
     centres the card; the auto x margins below are redundant for
     that axis but keep the shorthand symmetric. */
  margin: auto;
  /* z-index above the now-fixed .apply-now-modal-overlay (which has
     no z-index and renders at z-auto in the modal's stacking
     context). Without this the overlay would paint on top of the
     card. */
  z-index: 1;
  /* The card itself has no bg — the form card inside provides the
     parchment surface. This keeps the close button (positioned
     above the card's top-right corner) outside the parchment frame
     so it reads as a modal-level control rather than form chrome. */
}

/* Close button — small ink "x" pinned to the top-right of the card,
   8px outside the parchment frame so it doesn't crowd the form's
   border. On click, the modal closes (data-modal-close). */
.apply-now-modal-close {
  position: absolute;
  top: -36px;
  right: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  color: var(--parchment);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.2s ease;
  -webkit-appearance: none;
  appearance: none;
}

.apply-now-modal-close:hover,
.apply-now-modal-close:focus-visible {
  opacity: 0.7;
}

.apply-now-modal-close svg {
  width: 16px;
  height: 16px;
  display: block;
}


/* ── Form card (parallel to .work-with-us-form-card) ──────────────
   5px parchment frame around an inner ink-bordered grid. Inner
   sizing matches the Figma: 1230×407 (7 rows × 59px - 1px overlap).
   The .apply-now-form-card holds the parchment ground; the
   .apply-now-form-grid holds the inner bordered structure. */

.apply-now-form-card {
  background: var(--parchment);
  border-radius: 2px;
  padding: 5px;
}

.apply-now-form-grid {
  border: 1px solid var(--ink);
  border-radius: 2px;
}


/* ── Rows + cells ─────────────────────────────────────────────────
   Same hairline pattern as the on-page contact forms: row
   border-top + cell border-left. Each row defaults to 59px tall;
   radio rows can grow if needed (e.g. on narrow viewports where
   options wrap). The submit row stays at 59px since the button
   needs a fixed footprint. */

.apply-now-form-row {
  display: flex;
  border-top: 1px solid var(--ink);
  min-height: 59px;
}

.apply-now-form-row:first-child {
  border-top: 0;
}

.apply-now-form-cell {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  border-left: 1px solid var(--ink);
}

.apply-now-form-cell:first-child {
  border-left: 0;
}


/* ── Text inputs ──────────────────────────────────────────────────
   Identical typography + placeholder treatment to the on-page
   contact forms. Inputs absolutely fill their cell with 18px×30px
   internal padding so the placeholder lands at the Figma label
   anchor, bold uppercase ink. */

.apply-now-form-input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 18px 30px;
  background: transparent;
  border: 0;
  outline: 0;
  border-radius: 0;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
}

.apply-now-form-input::placeholder {
  color: var(--ink);
  opacity: 1;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.apply-now-form-input:focus {
  background: rgba(89, 116, 139, 0.06); /* var(--dusk) at 6% */
}

.apply-now-form-input:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px var(--parchment) inset;
  -webkit-text-fill-color: var(--ink);
  caret-color: var(--ink);
}


/* ── Radio rows ───────────────────────────────────────────────────
   Single-cell row that holds a label on the left and the radio
   options on the right, all flowing horizontally. The cell has the
   18px×30px internal padding (matching input cells) plus flex so
   label + radios sit on the same baseline. */

.apply-now-form-row--radios .apply-now-form-cell--radios {
  display: flex;
  align-items: center;
  gap: 32px;
  padding: 0 30px;
}

.apply-now-form-row-label {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  flex-shrink: 0;
}

.apply-now-form-radios {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px 26px;
  /* gap: row-gap column-gap. Vertical row-gap kicks in when options
     wrap on narrow viewports; column-gap is the breathing room
     between options on the same row. */
}


/* ── Single radio (label + circle + text) ──────────────────────── */

.apply-now-form-radio {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  cursor: pointer;
  /* The whole label is the click target — clicking the text or the
     circle toggles the input. */
}

.apply-now-form-radio-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
  /* Off-flow but still focusable for keyboard users. Visible focus
     transfers to the custom circle via :focus-visible below. */
}

.apply-now-form-radio-circle {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid var(--ink);
  background: transparent;
  flex-shrink: 0;
  display: block;
  transition: background 0.2s ease;
}

.apply-now-form-radio-input:focus-visible + .apply-now-form-radio-circle {
  outline: 2px solid var(--dusk);
  outline-offset: 2px;
}

.apply-now-form-radio-text {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 400; /* regular weight per Figma — distinguishes options
                       from the bold uppercase label on the left */
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}


/* ── Per-row checked colour ───────────────────────────────────────
   The Figma assigns a different palette colour to each demographic
   question's checked-state circle. Each row's modifier class scopes
   the :checked + .circle bg to that row's colour. */

.apply-now-form-row--ethnicity .apply-now-form-radio-input:checked + .apply-now-form-radio-circle {
  background: var(--dawn);
}

.apply-now-form-row--status .apply-now-form-radio-input:checked + .apply-now-form-radio-circle {
  background: var(--bark);
}

.apply-now-form-row--applying .apply-now-form-radio-input:checked + .apply-now-form-radio-circle {
  background: var(--spell);
}

.apply-now-form-row--source .apply-now-form-radio-input:checked + .apply-now-form-radio-circle {
  background: var(--olive);
}


/* ── Submit row ───────────────────────────────────────────────────
   Full-width button. Cell has no internal padding — the button
   fills the cell completely so the dusk hover bg paints right up
   to the cell's border edges. Same hover pattern as the on-page
   contact forms. */

.apply-now-form-row--submit {
  /* Single-cell row spans full width naturally via flex: 1 1 0 on
     the cell, since this row only contains one cell. */
}

.apply-now-form-cell--submit {
  padding: 0;
  border-left: 0;
}

.apply-now-form-submit {
  width: 100%;
  height: 100%;
  min-height: 59px;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 22px;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-transform: uppercase;
  transition: background 0.25s ease, color 0.25s ease;
  -webkit-appearance: none;
  appearance: none;
}

.apply-now-form-submit:hover,
.apply-now-form-submit:focus-visible {
  background: var(--dusk);
  color: var(--parchment);
}

/* Submitting state — set by the form submit-guard JS module while a
   POST is in flight (button disabled + label swapped to "Sending…").
   pointer-events off so hover styling can't fire on the frozen
   button; shared across all four admin-post forms. */
.subscribe-form-submit:disabled,
.work-with-us-form-submit:disabled,
.apply-now-form-submit:disabled {
  opacity: 0.6;
  cursor: default;
  pointer-events: none;
}

/* Cloudflare Turnstile widget rows — rendered only when keys are
   configured (Theme General → Forms). The widget itself is a fixed
   ~300×65px iframe injected by Cloudflare's api.js into the
   .cf-turnstile container; these rows just centre it and give it
   breathing room within each form's grid. */
.work-with-us-form-row--turnstile,
.apply-now-form-row--turnstile,
.subscribe-form-row--turnstile {
  display: flex;
  justify-content: center;
  text-align: center;
  padding-top: 13px;
  padding-bottom: 10px;
  margin-top: 8px;
}

/* Centre the widget iframe within full-width cells too — flex
   centring handles content-sized cells, text-align handles the
   inline iframe inside a stretched cell. */
.work-with-us-form-cell--turnstile,
.apply-now-form-cell--turnstile {
  text-align: center;
}

/* The modal's widget renders explicitly on open (see
   initApplyNowModal). The row starts collapsed (.is-pending, set in
   the template) and the JS removes the class right before calling
   turnstile.render() — the container must be VISIBLE when Cloudflare
   paints into it, so the hidden state can't be inferred from
   :has(:empty) (that deadlocked in v1.40.1-3: hidden because empty,
   empty because hidden). If Cloudflare's script never loads, the
   class stays on and no dead space is reserved above the submit
   button. */
.apply-now-form-row--turnstile.is-pending {
  display: none;
}
.subscribe-form-row--turnstile {
  margin-bottom: 12px;
}

/* The subscribe card's base row carries a dividing border the
   Turnstile row shouldn't inherit. Doubled selector to outweigh
   the base rule's specificity. */
.subscribe-form-row.subscribe-form-row--turnstile {
  border: none;
}


/* ── APPLY NOW SUCCESS VIEW ──────────────────────────────────────
   Shown by JS in place of the form when the GET-after-redirect
   carries ?spellbound_form=success&form=apply_now. Hidden by
   default via the `hidden` attribute on the markup (which
   evaluates to display:none from the UA stylesheet); JS clears
   the attribute to show it.

   Visual treatment mirrors the .apply-now-form-card: parchment
   background, generous padding, centred content. The heading uses
   Perfectly Nineties to match the modal's editorial register; the
   body sits below in Alte Haas Grotesk at a comfortable reading
   size with 1.5 line-height. Max-width keeps long-line measure
   in the readable 65-80ch range even on wide viewports.
   ──────────────────────────────────────────────────────────── */

.apply-now-success {
  background: var(--parchment);
  color: var(--ink);
  border-radius: 4px;
  padding: 80px 60px;
  text-align: center;
}

.apply-now-success-heading {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: 48px;
  line-height: 1.1;
  margin: 0 0 24px;
  color: var(--ink);
}

.apply-now-success-body {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 18px;
  line-height: 1.55;
  margin: 0 auto;
  max-width: 640px;
  color: var(--ink);
}

/* Paragraph rhythm for the now-WYSIWYG-driven confirmation body.
   The body switched from a single <p> to a <div> wrapper so the
   editor's <p> tags nest validly; restore even spacing between
   them and drop the trailing margin so the card stays balanced. */
.apply-now-success-body p {
  margin: 0 0 1em;
}

.apply-now-success-body p:last-child {
  margin-bottom: 0;
}


/* ── APPLY NOW MODAL RESPONSIVE ──────────────────────────────────── */

@media (max-width: 1024px) {
  /* padding-top stays ≥50px so the close button (-36px above the
     card) lands at viewport y≥14px, comfortably visible. The
     desktop default is 60px. */
  .apply-now-modal { padding: 50px 16px; }
  .apply-now-form-input { padding: 16px 20px; }
  .apply-now-form-row--radios .apply-now-form-cell--radios {
    padding: 14px 20px;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  .apply-now-form-row--radios { min-height: auto; }
  .apply-now-form-radios { gap: 10px 20px; }
}

@media (max-width: 768px) {
  /* Mobile: 50px padding-top + close at -32px = viewport y=18. The
     close button used to be cut off because the previous 20px
     padding-top only left -12px for the button. */
  .apply-now-modal { padding: 50px 12px; }
  .apply-now-modal-close { top: -32px; }

  /* Stack input rows on phone — same approach as the on-page contact
     forms. Border-left becomes border-top; the row's own border-top
     is suppressed. */
  .apply-now-form-row {
    flex-direction: column;
    border-top: 0;
  }
  .apply-now-form-row:first-child .apply-now-form-cell:first-child {
    border-top: 0;
  }
  .apply-now-form-cell {
    flex: 0 0 auto;
    min-height: 59px;
    border-left: 0;
    border-top: 1px solid var(--ink);
  }
  .apply-now-form-input { padding: 14px 18px; }
  .apply-now-form-row--radios .apply-now-form-cell--radios {
    padding: 14px 18px;
  }
  .apply-now-form-row-label,
  .apply-now-form-radio-text { font-size: 12px; letter-spacing: 0.04em; }
  .apply-now-form-cell--submit { padding: 0; }

  /* Success view scales down to match the narrower modal card on
     mobile — smaller padding, smaller serif heading. */
  .apply-now-success { padding: 60px 24px; }
  .apply-now-success-heading { font-size: 36px; }
  .apply-now-success-body { font-size: 16px; }
}


/* ── EVENTS / RESOURCES MARQUEE — STATIC MODE ─────────────────────
   Applied by inc/event-cpt.php's `spellbound_event_filter_is_static`
   when fewer than 3 categories have upcoming events. Looping a
   1- or 2-pill marquee creates a long stretch of empty space
   between passes that reads as broken; static mode renders a
   single pass, centred, no animation.

   Both selectors override the base marquee's `width: max-content`
   on the scroller/track (so the inner element fills the container
   instead of measuring to its content) plus `justify-content:
   center` to centre the pills, and zero out the trailing padding
   that base mode uses to space adjacent passes (which doesn't
   apply when there's no second pass). Animation is disabled
   straightforwardly. */

.events-filter--static .events-filter-scroller {
  animation: none;
  width: 100%;
  justify-content: center;
}

.events-filter--static .events-filter-track {
  padding-right: 0;
}

.resources-marquee--static .resources-marquee-track {
  animation: none;
  width: 100%;
  justify-content: center;
}

/* Watch — same fallback. With fewer than 3 categories the marquee
   would have nothing meaningful to scroll into; render a single
   pass, centred, no animation. Both rows get the treatment so the
   --top and --bottom rows stay in sync. */
.watch-categories--static .watch-categories-track {
  animation: none;
  width: 100%;
  justify-content: center;
}

/* Homepage paths marquee — applied when there are 1-2 path items
   total. Below the 3-item threshold the looping animation would
   leave long stretches of empty track between passes that read as
   broken; static mode centres the items and disables the scroll,
   matching the events / resources / watch fallbacks. */
.paths--static .paths-track {
  animation: none;
  width: 100%;
  justify-content: center;
}


/* ── EVENT CATEGORY FILTER — INTERACTION STATES ──────────────────
   Toggled by the initEventCategoryFilters IIFE in main.js when a
   pill is clicked. .is-hidden hides cards filtered out by the
   active category; .is-selected highlights the active pill (with
   .pill:has() siblings dimmed to push the active one forward).

   The same .is-hidden / .is-selected classes are also used by the
   Watch filter (initWatchCategoryFilters) on .watch-video-card +
   .watch-categories .pill so the visual conventions stay in sync
   across all filter strips in the theme. */

.event-card.is-hidden,
.watch-video-card.is-hidden {
  display: none;
}

/* Selected pill — slight scale up, full opacity. The transition
   below applies to ALL pills so the de-emphasis on unselected
   ones eases in/out cleanly when the user changes the filter. */
.pill {
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.pill.is-selected {
  transform: scale(1.05);
}

/* When ANY pill in a filter strip is selected, dim the unselected
   siblings so the active filter pops. :has() lets us express
   "container with a selected child" in pure CSS, no JS class on
   the parent needed. Covers .events-filter (Programme),
   .resources-marquee (Sign Up), and .watch-categories (Watch). */
.events-filter:has(.pill.is-selected) .pill:not(.is-selected),
.resources-marquee:has(.pill.is-selected) .pill:not(.is-selected),
.watch-categories:has(.pill.is-selected) .pill:not(.is-selected) {
  opacity: 0.55;
}


/* ── WATCH GRID DEFAULT STATE — FEATURED-ONLY ─────────────────────
   The Watch template renders every non-Alumni video into .watch-
   grid, but in the default (unfiltered) state only those flagged
   `data-featured="1"` should be visible. Clicking a filter pill
   adds .is-filtering to .watch-featured, which suspends this rule
   so the JS can show ALL category-matching cards regardless of
   featured status. Clicking the active pill again removes the
   class and the featured-only default returns.

   This is a structural CSS rule (not a state class on the cards
   themselves), so non-featured cards don't even momentarily flash
   visible on page load before the JS attaches. */
.watch-featured:not(.is-filtering) .watch-video-card:not([data-featured="1"]) {
  display: none;
}


/* ── WATCH VIDEO MODAL ────────────────────────────────────────────
   Site-wide modal (template-parts/watch-video-modal.php). Mirrors
   the .apply-now-modal scaffold (fixed overlay, ink-tinted dimmer,
   top-aligned card with auto vertical centring, close button
   8px outside the card). Differences:

     - The card itself has no parchment frame — the player IS the
       card. A subtle 1px parchment border + 8px outer padding
       gives the player a faint cinematic mat to sit in, echoing
       the .watch-video-card thumbnail treatment without competing
       with the playback content.
     - 16:9 aspect-ratio container for the player slot. Modern
       aspect-ratio support is universal in evergreens; the
       padding-bottom fallback for very old browsers isn't worth
       the bytes.
     - Max-width 1180px so the player doesn't dwarf the viewport
       on ultra-wide screens. Slightly narrower than the apply-now
       modal because a 16:9 player at 1240px wide would be 698px
       tall — taller than many laptop viewports. 1180×664 is more
       considerate. */

.watch-video-modal {
  position: fixed;
  inset: 0;
  z-index: 9999;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 60px 20px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.watch-video-modal.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}

.watch-video-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(29, 30, 27, 0.72); /* slightly denser than apply-now;
                                          cinematic playback wants a
                                          stronger isolation from the
                                          page underneath. */
}

.watch-video-modal-card {
  position: relative;
  width: 100%;
  max-width: 1180px;
  margin: auto;
  z-index: 1;
}

.watch-video-modal-close {
  position: absolute;
  top: -36px;
  right: 0;
  width: 24px;
  height: 24px;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  color: var(--parchment);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 0.2s ease;
  -webkit-appearance: none;
  appearance: none;
  /* Lift above the player which sits at z-auto inside the card. */
  z-index: 2;
}

.watch-video-modal-close:hover,
.watch-video-modal-close:focus-visible {
  opacity: 0.7;
}

.watch-video-modal-close svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Player slot — 16:9 box. The injected <iframe> or <video> child
   absolutely fills the slot so its intrinsic dimensions never matter. */
.watch-video-modal-player {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
  border: 1px solid var(--parchment);
  /* Subtle outer padding so the parchment line reads as a mat
     rather than touching the player edge. */
  padding: 8px;
  box-sizing: border-box;
}

.watch-video-modal-player iframe,
.watch-video-modal-player video {
  position: absolute;
  top: 8px;
  left: 8px;
  width: calc(100% - 16px);
  height: calc(100% - 16px);
  display: block;
  border: 0;
  background: #000;
}

@media (max-width: 768px) {
  .watch-video-modal {
    padding: 50px 12px;
  }
  .watch-video-modal-close {
    top: -32px;
  }
  .watch-video-modal-player {
    padding: 4px;
  }
  .watch-video-modal-player iframe,
  .watch-video-modal-player video {
    top: 4px;
    left: 4px;
    width: calc(100% - 8px);
    height: calc(100% - 8px);
  }
}


/* ════════════════════════════════════════════════════════════════
   FORM SUBMISSION — HONEYPOT + SUCCESS/ERROR NOTICE
   ════════════════════════════════════════════════════════════════ */

/* Honeypot anti-bot field — visually hidden, kept in the
   accessibility tree's hidden state via aria-hidden + tabindex=-1
   on the input itself, kept out of layout via absolute + offscreen.
   Don't use display:none; some bots skip display:none fields
   specifically to evade honeypots. The offscreen + zero-size
   approach renders the field "real" enough to fool naive scrapers
   while staying invisible to humans. */
/* Auxiliary form field wrapper — offscreen, never visible. (Houses
   the honeypot; the class name is deliberately non-descriptive.) */
.form-aux {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
  pointer-events: none;
}


/* Success / error notice rendered by spellbound_render_form_notice
   on the wp_body_open hook after a form redirect. Fixed-positioned
   at the top of the viewport so it's visible regardless of where
   the page scrolls to (the inline forms get an anchor scroll on
   redirect; this notice sits above whatever the user lands on). */
.spellbound-form-notice {
  position: fixed;
  top: 80px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10000;
  max-width: calc(100vw - 32px);
  width: 600px;
  padding: 18px 56px 18px 24px; /* extra right padding for the close button */
  border-radius: 4px;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 1.4;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  box-shadow: 0 8px 24px rgba(29, 30, 27, 0.18);
  /* Initial slide-down + fade-in animation. The JS module that
     pairs with this CSS adds .is-dismissed for the success-case
     auto-fade after 7 seconds; .is-dismissed transitions opacity
     + transform back the way they came. */
  animation: spellbound-notice-in 0.35s ease-out both;
  transition: opacity 0.35s ease, transform 0.35s ease;
}

.spellbound-form-notice.is-dismissed {
  opacity: 0;
  transform: translateX(-50%) translateY(-12px);
  pointer-events: none;
}

/* Success — olive ground, parchment text. Reads "this is good news"
   without needing a green-tick affordance. */
.spellbound-form-notice--success {
  background: var(--olive);
  color: var(--parchment);
}

/* Error — bark ground, parchment text. Reads "something to address"
   without screaming red. */
.spellbound-form-notice--error {
  background: var(--bark);
  color: var(--parchment);
}

.spellbound-form-notice-text {
  display: block;
}

.spellbound-form-notice-close {
  position: absolute;
  top: 50%;
  right: 14px;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: 0;
  outline: 0;
  cursor: pointer;
  color: inherit;
  font-size: 24px;
  line-height: 1;
  font-weight: 400;
  -webkit-appearance: none;
  appearance: none;
  transition: opacity 0.2s ease;
}

.spellbound-form-notice-close:hover,
.spellbound-form-notice-close:focus-visible {
  opacity: 0.7;
}

@keyframes spellbound-notice-in {
  from { opacity: 0; transform: translateX(-50%) translateY(-12px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

@media (max-width: 768px) {
  .spellbound-form-notice {
    top: 70px;
    width: auto;
    left: 16px;
    right: 16px;
    transform: none;
    padding: 14px 48px 14px 18px;
    font-size: 13px;
    letter-spacing: 0.02em;
  }
  .spellbound-form-notice.is-dismissed {
    transform: translateY(-12px);
  }
  @keyframes spellbound-notice-in {
    from { opacity: 0; transform: translateY(-12px); }
    to   { opacity: 1; transform: translateY(0); }
  }
}


/* ════════════════════════════════════════════════════════════════
   SINGLE POST + ARCHIVE
   Used by single.php (individual post) and archive.php (post-type
   archive, categories, tags, dates, search fall-through).

   The hero + content section pair follows the same dark-then-light
   rhythm the Stories page uses: olive hero with parchment text,
   parchment body with ink text, joined by the watch-section-
   divider's seven circles. Reuses .container for the page-width
   constraint and .stories-grid for the archive grid so the
   archive picks up the home page's visual conventions for free.
   ════════════════════════════════════════════════════════════════ */

/* ── Hero — olive band, centred text ─────────────────────────────
   Shared by both single (with optional excerpt + date pill) and
   archive (page title + optional description). Padding-top 56px
   covers the fixed header height; padding-bottom 100px gives the
   olive band enough vertical weight that the divider below it
   doesn't read as a hairline. */

.post-hero {
  background: var(--olive);
  color: var(--parchment);
  padding: 136px 0 100px;
  position: relative;
}

.post-hero-content {
  max-width: 880px;
  margin: 0 auto;
  text-align: center;
}

.post-hero-date {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  display: inline-block;
  padding: 6px 12px;
  border: 1px solid var(--parchment);
  border-radius: 99px;
  margin: 0 0 24px;
}

.post-hero-title {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  font-size: clamp(40px, 6vw, 80px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  margin: 0;
}

.post-hero-excerpt {
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 17px;
  line-height: 1.6;
  margin: 28px auto 0;
  max-width: 640px;
  opacity: 0.92;
}


/* ── Featured image band ─────────────────────────────────────────
   Sits between the hero and the content; parchment background so
   the image gets a calm frame. The image itself is constrained
   to .container width (no full-bleed) — feels editorial without
   making large featured images dominate the layout. */

.post-featured-image {
  background: var(--parchment);
  padding: 60px 0 0;
}

.post-featured-image-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}


/* ── Post content prose ──────────────────────────────────────────
   Parchment body, ink text. Constrains the reading column to a
   comfortable measure (~640px) rather than the full container
   width — long lines hurt readability on prose, and the .container
   is set up for grid layouts not editorial reading. Padding-top
   shrinks when there's no featured image above (the --no-image
   modifier) so the content sits closer to the hero. */

.post-content {
  background: var(--parchment);
  color: var(--ink);
  padding: 60px 0 100px;
}

.post-content--no-image {
  padding-top: 100px;
}

.post-content-prose {
  max-width: 720px;
  margin: 0 auto;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 17px;
  line-height: 1.7;
  color: var(--ink);
}

.post-content-prose > * + * {
  margin-top: 1.2em;
}

.post-content-prose h2,
.post-content-prose h3,
.post-content-prose h4 {
  font-family: 'PerfectlyNineties', serif;
  font-weight: 400;
  line-height: 1.2;
  margin-top: 1.8em;
}

.post-content-prose h2 { font-size: 36px; letter-spacing: -0.01em; }
.post-content-prose h3 { font-size: 28px; }
.post-content-prose h4 { font-size: 22px; }

.post-content-prose a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.post-content-prose a:hover,
.post-content-prose a:focus-visible {
  opacity: 0.7;
}

.post-content-prose blockquote {
  font-family: 'PerfectlyNineties', serif;
  font-size: 26px;
  line-height: 1.35;
  font-style: italic;
  border-left: 3px solid var(--olive);
  padding-left: 24px;
  margin: 1.6em 0;
  color: var(--ink);
}

.post-content-prose img,
.post-content-prose figure {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
}

.post-content-prose figure figcaption {
  font-size: 13px;
  color: rgba(29, 30, 27, 0.65);
  margin-top: 8px;
  text-align: center;
}

.post-content-prose ul,
.post-content-prose ol {
  padding-left: 1.4em;
}

.post-content-prose li + li {
  margin-top: 0.4em;
}

.post-content-prose pre,
.post-content-prose code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em;
}

.post-content-prose pre {
  background: rgba(29, 30, 27, 0.05);
  padding: 16px 18px;
  border-radius: 4px;
  overflow-x: auto;
}


/* ── "Back to all stories" anchor ────────────────────────────────
   Centred under the prose, uses the existing .pill component so
   the visual language matches every other CTA in the theme. */

.post-back {
  max-width: 720px;
  margin: 60px auto 0;
  text-align: center;
}

.post-back .pill {
  display: inline-flex; /* override .pill's default block layout */
}

/* External-link CTA (used on single-resource.php when an editor
   has supplied an _resource_link). Sits between the prose and
   the back-link; same centering as .post-back but a tighter top
   margin so the two pills stack as a single CTA group with the
   back-link reading as "next step". */
.post-external-link {
  max-width: 720px;
  margin: 60px auto 0;
  text-align: center;
}
.post-external-link .pill {
  display: inline-flex;
}
.post-external-link + .post-back {
  margin-top: 16px;
}

/* In both centered button contexts (.post-back, .post-external-link)
   the .session-btn needs to size to its label rather than stretch
   the full width of the container — the base .session-btn rule
   uses `display: block` for the in-session-card layout where it
   spans the card body, but here we want a centered pill-width
   button. text-align:center on the parent takes care of the
   centering once the button is inline-sized. */
.post-back .session-btn,
.post-external-link .session-btn {
  display: inline-block;
  margin-top: 0; /* override .session-btn's default 24px top margin —
                    spacing here is set by the container's margin. */
}


/* ── Archive grid + pagination ───────────────────────────────────
   The grid reuses .stories-grid from the home page wholesale —
   same 2×2 columns, same 60px gap, same .story-card variants.
   The --archive modifier exists as a hook for any archive-only
   overrides without re-defining the base grid rules. */

.archive-grid {
  background: var(--parchment);
  color: var(--ink);
  padding: 80px 0 120px;
}

.stories-grid--archive {
  /* No overrides yet; the modifier is a future-proofing hook. */
}

.archive-empty {
  max-width: 640px;
  margin: 40px auto;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 18px;
  line-height: 1.6;
  color: rgba(29, 30, 27, 0.7);
  text-align: center;
}

.post-pagination,
.archive-grid .nav-links {
  margin: 80px auto 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  font-family: 'AlteHaasGrotesk', sans-serif;
  font-size: 14px;
}

.post-pagination .page-numbers,
.archive-grid .page-numbers {
  display: inline-block;
  padding: 10px 16px;
  border: 1px solid var(--ink);
  border-radius: 99px;
  color: var(--ink);
  text-decoration: none;
  transition: background 0.2s ease, color 0.2s ease;
  min-width: 44px;
  text-align: center;
}

.post-pagination .page-numbers:hover,
.archive-grid .page-numbers:hover,
.post-pagination .page-numbers:focus-visible,
.archive-grid .page-numbers:focus-visible {
  background: var(--ink);
  color: var(--parchment);
}

.post-pagination .page-numbers.current,
.archive-grid .page-numbers.current {
  background: var(--ink);
  color: var(--parchment);
}

.post-pagination .page-numbers.dots,
.archive-grid .page-numbers.dots {
  border: 0;
  padding: 10px 4px;
}

.screen-reader-text {
  /* Used by WP's pagination markup; matches the ubiquitous
     screen-reader-text utility expected by the platform. */
  border: 0;
  clip: rect(1px, 1px, 1px, 1px);
  -webkit-clip-path: inset(50%);
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute !important;
  width: 1px;
  word-wrap: normal !important;
}


/* ── Responsive ──────────────────────────────────────────────── */

@media (max-width: 768px) {
  .post-hero {
    padding: 100px 0 64px;
  }
  .post-hero-title {
    font-size: clamp(36px, 9vw, 56px);
  }
  .post-hero-excerpt {
    font-size: 15px;
  }
  .post-featured-image {
    padding: 40px 0 0;
  }
  .post-content,
  .post-content--no-image {
    padding: 48px 0 72px;
  }
  .post-content-prose {
    font-size: 16px;
    line-height: 1.65;
  }
  .post-content-prose h2 { font-size: 28px; }
  .post-content-prose h3 { font-size: 22px; }
  .post-content-prose h4 { font-size: 18px; }
  .post-content-prose blockquote {
    font-size: 20px;
    padding-left: 18px;
  }
  .post-back {
    margin-top: 40px;
  }
  .archive-grid {
    padding: 56px 0 80px;
  }
  .post-pagination,
  .archive-grid .nav-links {
    margin-top: 56px;
  }
}


/* ════════════════════════════════════════════════════════════════
   IMAGE GALLERY COMPONENT
   Shared sub-component used by:
     - .testimonial-left  (homepage testimonial, 1-2 images)
     - section.choose-images (homepage choose, 1-3 images)

   Each .image-card is a 5px parchment matte around an image,
   rotated by an inline --rotation CSS variable, with an optional
   dot grid overlay toggled by data-show-dots="1". Layout differs
   by count (data-image-count on the .image-gallery container):
     - 1: centred at a contained width
     - 2: overlapping pair with translate offsets, both rotated
     - 3: row of 3 with the gallery wider than its section so the
          outer two cards bleed off the edges (clipped by the
          section's overflow-x: hidden).
   ════════════════════════════════════════════════════════════════ */

.image-gallery {
  position: relative;
}

/* ── Single card — the parchment-matted, optionally-rotated frame ─
   The CSS variable approach for rotation is what lets PHP supply
   the per-image angle inline while the layout-specific translate
   offsets compose in the same `transform` statement (variants
   below). Without the variable, the layout would have to choose
   between editor control over angle OR composable positioning. */
.image-card {
  position: relative;
  background: var(--parchment);
  padding: 5px;
  border-radius: 2px;
  transform: rotate(var(--rotation, 0deg));
  transform-origin: center center;
}

/* The inner element scopes overflow: hidden to the image area
   only, so the dot overlay clips to the photo's bounds (not the
   parchment matte's outer edge), and provides a fixed aspect
   ratio so the gallery layout is predictable regardless of the
   uploaded photos' intrinsic dimensions. 4:5 portrait works for
   both Figma compositions; landscape uploads get cropped by
   object-fit: cover below. */
.image-card-inner {
  position: relative;
  overflow: hidden;
  border-radius: 1px;
  aspect-ratio: 4 / 5;
}

.image-card-inner img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Dot grid overlay — 7 evenly-spaced columns aligned to the
   image's left and right edges, with the same horizontal spacing
   maintained vertically (square grid).

   Mechanism — four radial-gradients per tile:
     - `background-size: calc(100% / 6)` produces tiles of 1/6 the
       container width. Six tiles span the full width; a seventh
       row of dots forms at x=100% from the right-corner gradients.
     - Each tile paints four small parchment dots — one at each
       of its four corners (0/0, 100%/0, 0/100%, 100%/100%). Since
       CSS background tiles don't bleed across their boundaries,
       a single corner gradient would only ever render a
       quarter-circle inside each tile. By placing a quarter at
       every corner, the four quarters meet at every shared tile
       intersection and combine into a full circle in the interior.
       Edges of the image show half-circles (only two adjacent
       tiles contribute); the four image corners show quarter-
       circles (only the single tile contributes). All three
       behaviours match the design.
     - The dot's size (3.75px radius = 7.5px diameter) is specified
       in absolute pixels, so it stays constant regardless of how
       large or small the tile is. Wider photos get the same dots
       just farther apart.
     - The square tile (height = width) keeps the vertical spacing
       consistent with the horizontal, so the grid reads as a
       regular square pattern not a stretched one. */

.image-card[data-show-dots="1"] .image-card-inner::after,
.photo-card[data-show-dots="1"] .photo-card-inner::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Two-stop gradient with a small feathered transition (0.5px)
     rather than a hard cut. Hard color stops at the same position
     (`var(--parchment) 3.75px, transparent 3.75px`) leave no
     anti-aliasing zone, so each browser rounds the circle edge
     differently — producing the slightly lumpy / ovoid look that
     showed up at higher zoom. The 3.25 → 3.75 feathered ring gives
     the renderer somewhere to smooth the curve, AND lets the four
     quarter-circles from adjacent tiles blend at the shared corner
     join even when sub-pixel tile widths shift the join by a
     fraction. Visually identical at the dot's full size, just
     cleanly round instead of jagged. */
  background-image:
    radial-gradient(circle at 0    0,    var(--parchment) 3.25px, transparent 3.75px),
    radial-gradient(circle at 100% 0,    var(--parchment) 3.25px, transparent 3.75px),
    radial-gradient(circle at 0    100%, var(--parchment) 3.25px, transparent 3.75px),
    radial-gradient(circle at 100% 100%, var(--parchment) 3.25px, transparent 3.75px);
  background-size: calc(100% / 6) calc(100% / 6);
  pointer-events: none;
}


/* ── 1-image layout — centred ─ */
.image-gallery[data-image-count="1"] {
  display: flex;
  justify-content: center;
}
.image-gallery[data-image-count="1"] .image-card {
  width: min(80%, 480px);
}


/* ── 2-image layout — overlapping pair ─
   Both cards stack in a single grid cell (1×1 grid). Translate
   offsets pull each toward opposite corners; the rotation comes
   from the inline --rotation variable, with a sensible fallback
   in the rotate() call so an empty variable still gives a tilt.

   Padding-top/bottom on the gallery prevents the rotated card
   corners from clipping into surrounding layout, since rotating
   a rectangle expands its bounding box vertically. */
.image-gallery[data-image-count="2"] {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  place-items: center;
  padding: 60px 0;
}
.image-gallery[data-image-count="2"] .image-card {
  grid-column: 1;
  grid-row: 1;
  width: 62%;
}
.image-gallery[data-image-count="2"] .image-card:nth-child(1) {
  transform: translate(-22%, -7%) rotate(var(--rotation, -3deg));
  z-index: 1;
}
.image-gallery[data-image-count="2"] .image-card:nth-child(2) {
  transform: translate(22%, 7%) rotate(var(--rotation, 5deg));
  z-index: 2;
}


/* ── 3-image layout — row with edge bleed ─
   The gallery is wider than its section (110% with -5% margin-
   left) so the leftmost and rightmost cards' outer edges sit
   beyond the section's content box. The section's overflow-x:
   hidden (declared on .choose-images further up) clips them at
   the section edges, creating the cropped-at-edges effect.

   Each card keeps its own rotation; the middle card stacks
   above the outer two so the visual centre of the row has a
   slight depth. */
.image-gallery[data-image-count="3"] {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  width: 110%;
  margin-left: -5%;
  padding: 40px 0;
}
.image-gallery[data-image-count="3"] .image-card:nth-child(1) { z-index: 1; }
.image-gallery[data-image-count="3"] .image-card:nth-child(2) { z-index: 2; }
.image-gallery[data-image-count="3"] .image-card:nth-child(3) { z-index: 1; }


/* The choose-images section needs overflow clipping so the
   3-image variant's bleed crops correctly at the section edges.
   Harmless for 1- and 2-image variants (which don't overflow). */
.choose-images {
  overflow-x: hidden;
}


/* ── Responsive ─
   The image gallery's positioning (overlap pair, edge-bleed row)
   uses percentage-based widths + translations, so it scales
   naturally with the container width. On mobile we don't reflow
   to a stacked layout — keep the desktop arrangement and let the
   percentages produce smaller cards. Tweaks below are just the
   minimum needed to fit narrower viewports gracefully:
     - 1-image cap on width: prevents single photos from looking
       lonely-but-huge on phones.
     - 2-image padding reduction: less vertical breathing room is
       fine on phones; the visual still reads as a pair.
     - 3-image gap reduction: 24px between cards is too generous
       on a narrow viewport; 8px keeps the row tight without
       overlapping.

   The 2-image card width stays at desktop's 62%. (The previous
   mobile-only bump to 68% caused the rotated card corners to push
   past the container edge on narrow viewports, producing a
   visible crop at the testimonial section's bounds.) */
@media (max-width: 768px) {
  .image-gallery[data-image-count="1"] .image-card {
    width: min(85%, 320px);
  }

  .image-gallery[data-image-count="2"] {
    padding: 40px 0;
  }

  .image-gallery[data-image-count="3"] {
    gap: 8px;
    padding: 24px 0;
  }
}


/* ─────────────────────────────────────────────────────────────
   Post-launch responsive fixes (v1.35.0)
   ───────────────────────────────────────────────────────────── */

/* Stories page only: lift + widen the orb divider on smaller screens. */
@media (max-width: 1000px) {
  body.spellbound-stories .orb-divider img {
    width: 150%;
    transform: translateY(-90%);
    left: -25%;
  }
}

/* About page only: collapse the team grid to a single centred column. */
@media (max-width: 1255px) {
  body.spellbound-about .team-grid {
    grid-template-columns: 1fr;
  }
  body.spellbound-about .team-card {
    max-width: 700px;
    margin: 0 auto;
  }
}

/* About page only: fixed team-photo height on mobile. */
@media (max-width: 767px) {
  body.spellbound-about .team-card-photo {
    height: 340px;
  }
}

/* Team cards: an optional mobile portrait swaps in at 767px and under.
   When no mobile photo is set the main photo keeps the plain
   .team-card-img class and is never hidden, so it shows at all sizes. */
.team-card-photo .team-card-img--mobile {
  display: none;
}
@media (max-width: 767px) {
  .team-card-photo .team-card-img--desktop {
    display: none;
  }
  .team-card-photo .team-card-img--mobile {
    display: block;
  }
}
