/* =========================================================
   AC-v3 Section: Eternal Tales intro + tile grid
   (#our-stories + #eternal-tales-tales)

   20.05.26: chamber strip + later same-day restore.
     - First pass: the empty #our-stories dwell-region was retired
       and the chamber wrapper stripped. id="our-stories" folded
       onto the ET intro card section.
     - Second pass (later same day): the chamber backdrop was
       restored as a STATIC layer scoped to just the ET sections
       (#our-stories + #eternal-tales-tales). The animation pieces
       (night/flooded crossfade, glass occlusion fade, orthostat
       logo-pin dwell, et-chamber.js) stay gone. The ET lockup +
       tagline now live as a normal-flow .et-intro-lockup at the
       top of #our-stories rather than a scroll-driven sticky pin.

   Pre-restore backup: archive/fix-v3-200526-pre-chamber-restore.*.
   ========================================================= */

/* ==========================================================
   Static-chamber overrides: the .et-chamber--static modifier
   adjusts the chamber wrapper for the post-strip world where
   et-chamber.js is no longer driving anything.

   z-index: the base .et-chamber rule sets z-index: 1 (added
   19.05.26 to keep the chamber-over-TEoTE visual stacking
   during the JS-driven dissolve). With no dissolve, the chamber
   stage-wrap's night image cover-fits taller than its parent and
   the overflow paints OVER TEoTE for the first ~50vh of the
   parallax (TEoTE bg gets gated behind --teote-late and stays
   invisible until --scroll-progress > 0.55). Reverting to
   z-index: auto puts chamber-then-TEoTE in DOM-order paint, so
   TEoTE wins on overlap. The (i) click issue this z:1 was
   originally protecting against no longer applies (the i lives
   in TEoTE, not the chamber).

   transform / opacity: forced to inert so the legacy --chamber-out
   driver in section-et-chamber.css has nothing to lerp against.
   ========================================================== */
.et-chamber.et-chamber--static {
  /* Doubled class selector to win specificity against the base
     .et-chamber { z-index: 1 } rule in section-et-chamber.css
     (which loads after this file in <head>). */
  z-index: auto;

  /* 21.05.26 r19: hardcoded dark background on the chamber wrapper
     so fast scrolling between the ET section and TEOTE doesn't briefly
     show the white body bg between the chamber image's bottom edge
     and TEOTE's top edge. The chamber image (sticky-pinned at viewport
     top, 100vh tall) doesn't always cover the wrapper's full visible
     area at the moment it unsticks; this dark plate is the fallback. */
  background: #16181A;

  /* r48 (20.05.26): padding-bottom back to 0. The r43 100vh "curtain
     region" created a one-viewport HOLD between the glass fully
     covering ET and TEoTE settling, so TEoTE read as arriving
     SEPARATELY from the glass. With 0, the TEoTE track starts right
     at the content end (trackTop = osBottom), so TEoTE rises into
     place DURING the glass rise (hidden behind the still-pinned
     chamber) and is settled exactly when the glass fully covers --
     glass + TEoTE now come in as one aligned unit. The glass covers
     the whole transition, so there is no white-flash risk from
     dropping the old dark-plate padding. */
  padding-bottom: 0;

  /* 21.05.26 r20: --et-brightness written by section-observers.js
     based on VotF tile's top in viewport (1 when VotF.top >= vh,
     lerps to 0 as VotF.top → 0). The brightness filter dims the
     entire chamber wrapper (chamber image + lockup + section content)
     to black as the user scrolls past the last tile. Fade completes
     when VotF reaches viewport top. */
  filter: brightness(var(--et-brightness, 1));

  /* r43 (20.05.26): --et-clear is flipped 1 -> 0 by section-observers.js
     once glass #2 has fully risen to cover the viewport (scrollY >=
     riseEnd). The chamber's cover-fit image is ~2x viewport-tall and
     does NOT scroll clear of the viewport on its own as the user
     enters TEoTE, so without this it would still be painting on top
     of TEoTE (chamber z:7 > track z:5) when glass #2 eases out. Held
     at 0 through the dark hold + the fade so glass #2 reveals TEoTE,
     not the chamber. The toggle happens behind the fully-opaque glass,
     so it is never seen; it reverses on scroll-up so the chamber
     returns when the curtain descends. */
  opacity: var(--et-clear, 1);

  /* 20.05.26 (urgent restore, pass 2): chamber framing locked to
     the MID endpoint - this is the OS-dwell framing the original
     .logo-pin alignment was calibrated against. The base
     .et-chamber rule defaults --chamber-bg-y to the BOT endpoint
     (= 0px), but the BOT framing is the dawn-flood resting
     position (Phase D end), NOT the orthostat dwell. The
     painted-L of the logo at viewport y=208 aligns with the
     carved-L of the orthostat ONLY when the chamber image is
     transformed to MID (bg-y: -140px, scale: 1.0). All per-BP
     blocks in section-et-chamber.css declare the same MID values
     (-140 / 1.00 / 50% / 34%), so the var() reads inherit the
     correct per-BP value automatically. */
  --chamber-bg-y: var(--chamber-bg-y-mid, -140px);
  --current-chamber-scale: var(--chamber-zoom-scale-mid, 1);
}
.et-chamber--static .et-chamber__layer--night {
  opacity: 1;
}
.et-chamber--static .et-chamber__stage-wrap {
  /* 21.05.26 r3: --teote-handoff-y is written by the ET->TEOTE
     handoff handler in section-observers.js. Default 0 (no slide)
     until TEOTE's top edge starts entering the viewport. */
  /* r51: the chamber backdrop eases UP slightly into place as glass #1
     clears (--et-reveal-y starts a short distance below, settles to 0),
     fading in through the glass blur. While it is offset there is a
     small top gap, but that shows the charcoal .et-chamber bg behind
     the still-opaque glass, so it is not visible. */
  transform: translateY(calc(var(--teote-handoff-y, 0px) + var(--et-reveal-y, 0px)));
  opacity: 1;
}

/* ==========================================================
   Chamber -> TEoTE hard separation (20.05.26).

   Previously, TEoTE used margin-top: calc(-1 * (nav-height -
   95px + 25vh)) (-205px at vh=800) so its stage-wrap rose into
   the bottom of the chamber wrap during the JS-driven dissolve.
   Without the dissolve, that negative margin meant TEoTE's
   sticky-pinned content rose into view WHILE the tile section
   was still mid-scroll - cutting off tile body text with
   Toby+Molly + the title logo painted on top.

   Earlier attempt (a fade gradient overlay) made it worse: the
   gradient covered visible tile text in the overlap zone.

   The correct fix is to zero the negative margin and rebase the
   track height so TEoTE starts CLEANLY below the chamber wrap.
   Tiles get to scroll fully off-screen before TEoTE rises. The
   sticky chamber image follows the chamber wrap off-screen and
   TEoTE pins from y=0 onward as expected.

   Note: section-teote.js still applies its 205px preRoll to the
   --scroll-progress calc (matched the original CHAMBER_DISSOLVE
   constants). With the negative margin gone, the preRoll now
   means scroll-progress stays at 0 for the first ~205px of
   scroll inside TEoTE (just the title logo + Toby+Molly visible
   while the scenery + Malus stay hidden behind --teote-late).
   Acceptable as a brief dwell on the entry frame; the bg image
   reveals once the user scrolls past the 205px buffer.
   ========================================================== */
.teote-parallax-track {
  /* Override the legacy negative-margin + matching height bump.
     Specificity: single class selector matches the base rule in
     section-teote.css, and this file loads BEFORE that one in
     <head>. Bumping to a compound selector to ensure this wins
     load-order. */
}
.teote-parallax-track.teote-parallax-track {
  margin-top: 0;
  height: 300vh;
  min-height: 300vh;
  /* scroll-margin-top (r2, 21.05.26): NEGATIVE so the "Ends of the Earth"
     nav anchor lands one nav-height PAST the track top, not at it. Two
     things gate TEoTE's visibility there: the reveal flips --teote-reveal
     to 1 only once track.top <= 0 (i.e. scrollY >= trackTop), and glass #2
     fades clear only by trackTop + 0.75*nav-height. Landing at the track
     top (the previous var(--nav-height), which put track.top at +100)
     satisfied NEITHER - TEoTE stayed at opacity 0 behind a still-opaque
     curtain, so the nav link read as "not loading the content". The nav
     handler subtracts this margin, so a negative value lands the jump at
     trackTop + nav-height: reveal fired, curtain gone, scene visible. */
  /* r60 (21.05.26): -1 -> -1.6 nav-heights. The TEoTE chrome (logo,
     tagline, credit, chevron) fades in via --teote-ui-in over
     [trackTop+0.75*nav, trackTop+1.5*nav] (AFTER the glass #2 title
     clears). The old -1 landed the nav jump at trackTop+nav, only ~1/3
     through that ramp, so the logo/tagline came in dim. Landing at
     trackTop+1.6*nav is just past the ramp's end, so the chrome is FULL
     on arrival (and the "ought to know" title is well gone). */
  scroll-margin-top: calc(var(--nav-height) * -1.6);
}

/* ==========================================================
   ET intro lockup (20.05.26 urgent restore)

   Sticky-pinned overlay matching the original .logo-pin
   geometry from section-et-chamber.css exactly. Lives outside
   #our-stories (sibling of .et-chamber__stage-wrap) so it pins
   relative to the outer .et-chamber wrapper for the full ET
   region. The chamber image is at its default rest-frame (see
   .et-chamber.et-chamber--static above); these --logo-x /
   --logo-y / --logo-w values land the painted-L of the lockup
   over the carved-L of the orthostat as previously calibrated.

   z-index: 0 so the intro card section (z:1 under .et-chamber)
   paints OVER the lockup as the card scrolls up - the same
   stacking the original .logo-pin used. The lockup is purely
   decorative (aria-hidden in markup); the intro card prose
   carries the actual "Eternal Tales" semantics.

   Per-BP values match the original logo-pin overrides at the
   bottom of section-et-chamber.css.
   ========================================================== */

/* 27.05.26: --lockup-enter gates the ET lockup's reveal at desktop
   (>=992): 0 pre-reveal so the logo + tagline stay hidden during the
   glass #1 fade, 1 once #our-stories.et-revealed is set (the glass has
   fully cleared). Registered via @property so it can transition smoothly
   without forcing a transition on `opacity` itself - which would lag the
   per-frame --lockup-exit fade-out when glass #2 rises later. The
   consuming override + reveal rule live inside the @media (>=992)
   block. */
@property --lockup-enter {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* r4-r6 sticky-CTA / lockup-class machinery removed in r7: the
   "Read more" CTA is back under VotF in normal flow, and the lockup
   ease-in is per-frame JS-driven again, so neither --cta-enter nor
   --lockup-opacity needs @property registration here. */

.et-intro-lockup {
  position: sticky;
  top: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  pointer-events: none;
  /* r39: track UP with the chamber stage during the chamber-to-TEoTE
     handoff via --teote-handoff-y (written by section-observers.js
     to both the stage-wrap and the lockup).
     r40 (20.05.26): also add --lockup-counter -- the inverse of the
     OS transform-pin applied during the glass fade. The lockup lives
     inside #our-stories, so it inherits OS's translateY pin; the
     counter cancels that inheritance so the logo stays truly static
     during the fade (no downward drift / "reverse"). After the fade
     the counter clamps at -navHeight and the handoff var takes over
     for the chamber tracking. */
  /* r51: the lockup (logo + tagline) eases UP slightly into place as
     glass #1 clears -- --et-reveal-y starts a short distance below and
     settles to 0 (folded into the translateY). Was a right-to-left
     slide in r50. */
  transform: translateY(calc(var(--lockup-counter, 0px) + var(--teote-handoff-y, 0px) + var(--et-reveal-y, 0px)));
  /* 21.05.26 r14: --lockup-opacity is written by section-observers.js
     at <=991 only - lerps 1 -> 0 as the intro et-card scrolls up past
     the tagline. r53 (21.05.26): --lockup-exit is written at >=992 by
     the glass #2 IIFE - eases the lockup OUT as the TEoTE curtain comes
     into view at the bottom + rises. The two are breakpoint-exclusive
     (each defaults to 1 where the other is active), so the product is
     the single correct fade at any width. */
  opacity: calc(var(--lockup-opacity, 1) * var(--lockup-exit, 1));
  /* z:-1 paints BELOW the in-flow card content within #our-stories
     stacking context, so the card glass-bg scrolls cleanly over the
     lockup as the user reads the prose. Still ABOVE the chamber
     stage (chamber stage is in .et-chamber stacking context, section
     is z:1 above stage). */
  z-index: -1;

  /* Wide-zone defaults (production default + 991 + 576 inherit
     unless overridden in the per-BP media blocks below). Lifted
     verbatim from .logo-pin in section-et-chamber.css. */
  --logo-x: 10px;
  --logo-y: 208px;
  --logo-w: 177px;
}
.et-intro-lockup > * { pointer-events: none; }

.et-intro-lockup__logo {
  display: block;
  width: var(--logo-w, 177px);
  height: auto;
  max-width: none;
  margin-top: var(--logo-y, 208px);
  margin-left: var(--logo-x, 0px);
  /* 21.05.26 r16: unified three-stack drop-shadow shared with TEOTE
     logo + the hero .logo-pin__logo per Caoimhín. Tight contact,
     mid lift, soft ambient. */
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.65))
          drop-shadow(0 3px 8px rgba(0, 0, 0, 0.5))
          drop-shadow(0 6px 24px rgba(0, 0, 0, 0.36));
  user-select: none;
  -webkit-user-drag: none;
}

.et-intro-lockup__tagline {
  margin: 28px 0 0;
  font-family: var(--font-sans);
  font-size: 16px;
  font-weight: 700;
  /* r57 (21.05.26): left-justified per Caoimhin (reverting the r56
     centre). Both tagline lines start at the same left edge, and the
     "Read more" CTA below is left-justified + left-aligned to match. */
  text-align: left;
  color: var(--primary-text);
  letter-spacing: 0.005em;
  text-shadow: 0 1px 6px rgba(0, 0, 0, 0.85),
               0 1px 2px rgba(0, 0, 0, 0.7);
}

/* #our-stories section: drop the inherited padding-top + scroll-
   margin-top so the lockup's natural-flow position sits at the
   section's top edge. When the user anchor-jumps or scrolls so
   that section.top = viewport y=0, sticky activates immediately
   and the logo lands at viewport y=208 (= the calibrated --logo-y
   from the original .logo-pin). The lockup itself occupies the
   top of the section in flow; the intro card sits below the lockup
   and scrolls UP over it as the user reads.

   ID-prefixed for specificity: section-et-card.css has an
   @media (max-height: 800px) { .section--et-intro { padding-top:
   calc(nav-height + 8px) } } rule that loads after this file and
   would otherwise re-introduce 108px of padding-top at narrow
   heights. Using #our-stories.section--et-intro wins specificity
   without needing !important. */
#our-stories.section--et-intro {
  padding-top: 0;
  /* scroll-margin-top (r2, 21.05.26): NEGATIVE so the "Our Stories" /
     "Eternal Tales" nav anchors land ~1.25 nav-heights PAST the section
     top, not at it. The section top now coincides with glass #1 at FULL
     cover (the rise curtain reaches full opacity exactly at aboutEnd =
     this section's top), so a scroll-margin-top of 0 landed the jump on
     the opaque dark glass - the ET content was invisible behind it.
     Glass #1 fades clear over the next nav-height; landing past clears
     it with a small buffer and reveals the lockup + intro card.
     (The nav handler subtracts this margin, so negative lands further
     down the page.)
     27.05.26 r80: bumped from -1.25 to -1.6 nav-heights to tighten the
     scroll-snap landing position. Lands deeper into the section, where
     the lockup is fully settled and the card opacity transition has
     started/completed -- less play between snap-end and "card is in
     its proper, visible position". Matches the TEoTE convention. */
  scroll-margin-top: calc(var(--nav-height) * -1.6);
}

/* ==========================================================
   Wide-BP two-column composition (>=992px)
   At desktop widths, the chamber image + logo shift LEFT
   together so the carved-L of the orthostat sits at viewport
   x=25vw, and the intro card moves to the RIGHT half of the
   section. Below 992px, this @media block doesn't apply and
   the current centered single-column layout holds (chamber
   full-width, logo centered above the card).

   How alignment is preserved:
     1. The chamber image gets an extra translateX(-25vw) on top
        of its existing translateY(--chamber-bg-y) + scale - the
        carved-L of the orthostat moves from viewport x=50vw to
        x=25vw.
     2. The lockup is placed in grid column 1 (left half = 50vw
        wide). align-items: center on the lockup centres the
        logo within that column, so the logo lands at column
        centre = viewport x=25vw. Carved-L AND logo at x=25vw =>
        alignment preserved with no manual logo-x dial.
   ========================================================== */
@media (min-width: 992px) {
  /* Drop the grid layout - the lockup is positioned via sticky +
     fixed width (50vw), and the card has explicit margins. This
     lets us shrink the lockup's flow box to 0 (height: 0 +
     overflow: visible) so the sticky pin range = the FULL section
     height. Without this, sticky releases mid-section as soon as
     the section's remaining scroll = lockup's natural height. */

  /* Lockup: 50vw wide so the logo (flex-centered + 10px logo-x)
     lands at viewport x=25vw + a 10px nudge - aligned with the
     chamber's carved-L which is at viewport x=25vw after the
     translateX(-25vw) below.
     height: 0 + overflow: visible: the lockup's layout box is 0
     tall, so sticky pin range = section height. Logo + tagline
     overflow visibly via their natural margins (logo margin-top
     208, tagline margin-top 28 below the logo). */
  #our-stories .et-intro-lockup {
    /* 21.05.26 r17: lockup is sticky again at >=992 per Caoimhín:
       it should stay pinned with the chamber image and only lift
       once the background image scrolls up. The non-sticky override
       from r14/15 is reverted. */
    width: 50vw;
    height: 0;
    overflow: visible;
  }
  /* Section inner (the card holder) spans grid col 2 (right 50%).
     Constrained to respect the SAME right-edge boundary as the
     About section's .about__body, which is the page's reading-
     measure container (max-width: clamp(280px, 92vw, 900px),
     centered inside .section__inner). The card's right edge sits
     at the same viewport x as the last character of any line in
     the About prose - that's the boundary Caoimhín marked in red.

     Math at 1280 viewport:
       - about_body max-width clamps to 900
       - about_body is centered: left=190, right=1090
       - section_inner right needs to be at x=1090
       - margin-right = (1280 - 900) / 2 = 190 -> section_inner
         right at 1280 - 190 = 1090 ✓
       - max-width = 900 / 2 = 450 -> section_inner left at
         1090 - 450 = 640 (= grid col 2 left = viewport center) ✓
     The formula scales fluidly at wider viewports.

     padding-top: 208px aligns the card's top edge with the lockup
     logo's top edge (= the lockup's --logo-y from the sticky pin
     at viewport y=0). */
  #our-stories .section__inner {
    --about-body-w: clamp(280px, 92vw, 900px);
    margin: 0;
    /* 21.05.26 r50: the intro (dark-glass) card now matches the tile
       CARD box exactly, so the ET card + the tiles below read as one
       set per Caoimhin ("same size as the others"). The tile card box
       is the tile band (left 25vw+134, right (100vw-aboutw)/2) inset
       by the tile stack's 74 / 18 padding -> left = 25vw+208, right
       pulled in 18px. Match that here.
       (Was 25vw+134 .. (100vw-aboutw)/2, which left the glass card
       74px wider on the left + 18px wider on the right than the
       tiles, and aligned the card's right to the About text boundary
       instead.) */
    margin-left: calc(25vw + 208px);
    margin-right: calc((100vw - var(--about-body-w)) / 2 + 18px);
    max-width: none;
    /* 27.05.26 (v2): card lands at the lockup logo's top edge per user
       direction. The OS transform-pin that previously held the card
       static during the glass fade has been removed (see
       section-observers.js) because its start / release boundaries
       created two velocity discontinuities the user felt as a "loose /
       more than one rest point" sensation. Without the pin the card
       moves with normal scroll, and the pin used to contribute roughly
       +navHeight to the card's visual position at reveal-time. To keep
       the arrival position the same (= lockup logo top), padding-top is
       bumped by ~navHeight relative to the no-pin natural-scroll case:
       208 (logo top) + ~100 (navHeight) = 308 cap. clamp scales the
       padding down on short desktops. The NUMBER is higher than the
       previous v1 value but the VISUAL POSITION at arrival is the same;
       the change is the motion (now a single continuous scroll). */
    /* 27.05.26 r94: padding-top bumped to 1196 (= original 278 + 918)
       so the card's natural-flow position is at absoluteTop ≈ 2970.
       With vh=800, this means the card's top edge naturally enters
       viewport at scrollY ≈ 2170 -- the same scroll point AOoS
       currently first appears. Since AOoS sits AFTER the card in DOM,
       it shifts down by the same amount, maintaining the original 918px
       DOM distance between intro card and AOoS. Net effect: chamber
       (sticky) + heading transition + lockup ease-in all happen before
       the user has scrolled far enough to see the card; the card then
       enters viewport from below naturally with continued scroll. */
    padding-top: clamp(1196px, calc(26vh + 918px), 1300px);
    padding-left: 0;
    padding-right: 0;
    /* 27.05.26 r86: min-height removed. It was sized for the sticky
       card's pin range, but the sticky positioning is now gone (card
       reverted to natural flow because sticky was failing to re-pin
       reliably on scroll-up, leaving the user unable to see the card
       when scrolling back). Without the min-height, section_inner
       sizes to its content as normal. */
  }
  /* Chamber image: shift LEFT by 25vw so the carved-L of the
     orthostat (was at viewport x=50vw via focal-x: 50%) moves to
     x=25vw, aligning with the lockup's painted-L in grid col 1.
     translateX comes BEFORE translateY + scale in the transform
     list because CSS applies transforms right-to-left and the
     scale needs to happen before the offset.

     The mask-image fades the image's right edge to transparent so
     the hard right edge (which becomes visible on viewports wider
     than the post-shift image effective width) dissolves smoothly
     into the dark stage-wrap bg. Without the mask, viewports
     >~1340 see a vertical seam where the image ends. */
  .et-chamber--static .et-chamber__layer {
    transform: translateX(-25vw)
               translateY(var(--chamber-bg-y))
               scale(var(--current-chamber-scale));
    -webkit-mask-image: linear-gradient(to right, black 65%, transparent 100%);
            mask-image: linear-gradient(to right, black 65%, transparent 100%);
  }
}

@media (min-width: 992px) {
  /* Tile grid moves up - reduces the empty vertical gap below
     the (now shorter) intro section. The .section--et-tales class
     itself no longer exists (20.05.26: the tiles moved into
     #our-stories' right column as #eternal-tales-tales stack), but
     leaving this rule in case the class ever returns or any other
     element picks it up. */
  .section--et-tales {
    padding-top: 0;
  }

  /* Tiles single-column stack aligned with the prose card (the
     magenta text-boundary lines) at >=992px. margin-left +
     margin-right match section_inner's, and padding-left/right now
     match the prose body's 74 / 18 insets (21.05.26 r49) so the tile
     content edges land on the SAME left + right as the prose text --
     the two boundary lines Caoimhin marked. (r10 had inset them
     narrower than the prose; r49 lines every card up flush per his
     "align all the cards to the left + right line" note.) */
  #our-stories > #eternal-tales-tales {
    box-sizing: border-box !important;
    display: block !important;
    width: auto !important;
    max-width: none !important;
    margin-left: calc(25vw + 134px) !important;
    margin-right: calc((100vw - clamp(280px, 92vw, 900px)) / 2) !important;
    margin-top: clamp(48px, 6vw, 88px);
    padding-left: 74px;
    padding-right: 18px;
    /* r52 (21.05.26): + var(--s-9) widens the gap between the last
       tile (VotF) and the TEoTE transition ("the rest of the site")
       by the same distance as the gap between the About section's
       last line and the glass layer beneath it -- which is About's
       padding-bottom, var(--s-9) = 96px. Per Caoimhin. The TEoTE
       trigger tracks #our-stories' end, so a taller tile stack pushes
       the transition that much further down. */
    padding-bottom: calc(clamp(96px, 14vh, 180px) + var(--s-9));
  }
  #our-stories #eternal-tales-tales .et-tales-grid {
    grid-template-columns: minmax(0, 1fr);
    justify-content: stretch;
    gap: clamp(28px, 3vw, 44px);
    max-width: none;
    margin: 0;
  }
  /* Reset narrow-BP last-child centering (no longer needed at 3-up) */
  #our-stories #eternal-tales-tales .et-tale-tile:last-child {
    grid-column: auto;
    max-width: none;
    justify-self: stretch;
  }

  /* CTA attached to VotF tile bottom (27.05.26 r76). The CTA now lives
     INSIDE #eternal-tales-tales as a sibling of the <ul>, so it
     inherits the tile container's horizontal frame and sits just below
     the last tile (VotF) without breaking the tile grid structure.
     padding-left matches the tile cards' internal padding so the CTA
     text left edge aligns with the tile title / body text. Small top
     margin (12-20) keeps it visually attached to VotF rather than
     reading as a separate element. */
  #eternal-tales-tales > .et-tales-cta {
    margin: clamp(12px, 1.5vw, 20px) 0 0;
    padding-left: clamp(20px, 3vw, 32px);
    padding-right: clamp(20px, 3vw, 32px);
  }

  /* 21.05.26 r50: now the card box matches the tile card box (above),
     the prose insets match the TILE TEXT inset too -- the same
     clamp(20px, 3vw, 32px) the tile title / body use -- so the ET
     prose left + right edges line up with the tile text below
     ("the text width aligned appropriately to match the others").
     At 1280 that's 32px each side -> prose at 560-1040, same as the
     tile body. (Was 74 / 18, sized for the OLD wider card box.) */
  #our-stories .et-card--intro .et-card__body {
    padding-left: clamp(20px, 3vw, 32px);
    padding-right: clamp(20px, 3vw, 32px);
  }

  /* Format icons sit inside .et-card__release. Match the body inset
     so the first icon's left edge aligns with the prose left edge. */
  #our-stories .et-card--intro .et-card__release {
    padding-left: clamp(20px, 3vw, 32px);
    padding-right: clamp(20px, 3vw, 32px);
  }

  /* Prose typography - modest bump over base. Caoimhín dialled
     the previous clamp(15px, 0.3vw + 13px, 17px) down by 1px
     ("too big by 1px"), so the formula shifts to 15.84px at 1280.
     Line-height stays at 1.65 for vertical breathing room without
     forcing overflow. */
  #our-stories .et-card--intro .et-card__prose {
    font-size: clamp(14px, 0.3vw + 12px, 16px);
    line-height: 1.65;
  }
  #our-stories .et-card--intro .et-card__underpinning {
    font-size: clamp(11px, 0.2vw + 10px, 13px);
  }

  /* Intro section is a 100vh+ "stage" so the lockup + card can stay
     sticky-pinned for the full scroll-through and fade out together
     as the tile section approaches from below. min-height pushes
     the section to at least one viewport, padding-bottom: 0 puts
     the tile section's top right at the section's natural bottom
     edge (the gap to the icons is driven by the .et-card--intro's
     own release padding-bottom). */
  #our-stories.section--et-intro {
    min-height: 100vh;
    padding-bottom: 0;
  }
  /* Card is in normal flow (not sticky). It scrolls naturally with
     the section so all of its content is reachable - the previous
     sticky+height:0 setup pinned the card but clipped content past
     the viewport bottom. The intro fade (--intro-fade-progress)
     handles the visual ramp-out as the user scrolls.
     Padding stays at the .et-card default (5px 0 0). */

  /* ----- Lockup reveal (27.05.26 r95) -----
     Lockup ease-in is now SCROLL-POSITION driven (was: time-based CSS
     transition gated on .et-revealed class). The lockup-opacity IIFE
     in section-observers.js writes --lockup-opacity each frame based
     on where the ET intro card sits relative to the tagline's bottom:
     opacity 0 when card.top is at viewport bottom (card just entering
     from below); opacity 1 when card.top reaches tagline.bottom. So
     the lockup eases in as the card approaches, and reverses on
     scroll-up. --lockup-enter (formerly gated via .et-revealed) is
     dropped from the opacity calc since the JS now handles the reveal
     entirely. --lockup-exit (glass #2 fade-out) remains as a multiplier
     so the lockup still eases out as the TEoTE curtain rises. */
  #our-stories .et-intro-lockup {
    /* r6: per-frame JS-driven opacity (section-observers.js writes
       --lockup-opacity each scroll frame). No CSS transition -- the
       reveal IS the scroll. */
    opacity: calc(var(--lockup-opacity, 0) * var(--lockup-exit, 1));
  }

  /* r2-r6 sticky-CTA block REMOVED in r7: the "Read more at eternal
     tales.ie" CTA went back to its static-flow position under VotF
     (see #eternal-tales-tales > .et-tales-cta below, and the same
     selector inside the >=992 block above for desktop padding). The
     under-VotF CTA shows at all widths again. */

  /* ----- Card ease-in (r41, 20.05.26) -----
     The card eases in (opacity) a fraction AFTER the logo + tagline
     are revealed by the clearing glass. The card's POSITION is held
     static by the OS transform-pin during the fade; this only
     animates its opacity. .et-revealed is toggled on #our-stories by
     section-observers.js once the glass has fully cleared
     (scrollY >= aboutEnd + navHeight).
     r51 (21.05.26): the card + its text now also come in "from
     further down" -- they start translated 56px DOWN and ease UP to
     0 as they fade in (the OS transform-pin still holds the card's
     base position during the fade; this transform composes on top).
     Delay bumped 450ms -> 600ms so it arrives slightly later, per
     Caoimhin. (r50 was opacity-only at 450ms; r41 was 250ms.)
     Tunable. */
  /* 27.05.26 r94: card opacity logic REMOVED entirely per user. No
     JS-driven reveal, no opacity transition, no opacity:0 default. The
     card is always visible in the CSS sense; what controls when the
     user SEES it is purely its DOM position, which has been pushed
     down via section_inner padding-top so the card naturally enters
     the viewport from below at the user's specified scroll point
     (= old AOoS scroll-in). Lockup ease-in still uses .et-revealed
     (unchanged). */

  /* ----- Legacy staged fade-in (only active if .fade-up present) -----
     Logo + tagline fade in first (opacity only, no translate -
     overriding .fade-up's default translateY(72)).
     Card text follows once the lockup has fully arrived. */
  .et-intro-lockup__logo.fade-up,
  .et-intro-lockup__tagline.fade-up {
    /* No vertical translate - logo/tagline are calibrated against
       the chamber image so they must NOT slide; only opacity fades. */
    transform: none;
    transition: opacity 700ms ease-out;
  }
  .et-intro-lockup__logo.fade-up.is-visible,
  .et-intro-lockup__tagline.fade-up.is-visible {
    transform: none;
  }
  /* Tagline lags the logo by 200ms so the lockup reads as a
     sequence rather than a single block fade. */
  .et-intro-lockup__tagline.fade-up {
    transition-delay: 200ms;
  }
  /* Card text starts AFTER the lockup tagline finishes: logo 700ms
     + tagline 200ms delay + 700ms duration = ~900ms total. Card
     transition-delay 900ms puts the text's ease-in right after the
     tagline lands. */
  #our-stories .et-card--intro.fade-up {
    transition-delay: 900ms;
  }
}

/* 27.05.26 r80: native scroll-snap on #our-stories removed; the snap
   is now JS-driven from section-observers.js (et-snap IIFE) so the
   easing curve and duration are tunable. scroll-margin-top on
   #our-stories below still feeds the snap target -- the JS reads
   navHeight and computes target = section.offsetTop + 1.6 * navHeight,
   matching the same offset that the CSS scroll-margin-top reversed. */

/* 21.05.26 r2: at <992px the layout collapses to single-column
   with the lockup at the top and the et-card below. The prose
   was kissing the "Restored." tagline (per Caoimhín's 991px
   screenshot); this padding pushes the .section__inner content
   (et-card prose) down to leave breathing room from the tagline
   bottom. The >=992px override above keeps its own padding-top
   for the right-column two-column layout. */
@media (max-width: 991.98px) {
  /* 27.05.26 r76: CTA inside #eternal-tales-tales (sibling of <ul>) at
     all widths. On mobile the tile grid is centred at clamp(280, 92vw, 900),
     so the CTA naturally inherits that frame -- no extra margin work
     needed beyond the small top gap and matching tile-internal padding. */
  #eternal-tales-tales > .et-tales-cta {
    margin: clamp(16px, 2vw, 24px) 0 0;
    padding-left: clamp(20px, 3vw, 32px);
    padding-right: clamp(20px, 3vw, 32px);
  }

  /* r2-r6 .et-tales-cta--sticky hide removed in r7: the sticky
     CTA element is gone from the HTML, so nothing to hide here. */
  #our-stories .section__inner {
    padding-top: clamp(64px, 9vw, 120px);
  }
}

/* Per-BP overrides - mirror the original .logo-pin narrow-zone
   blocks at the foot of section-et-chamber.css. */
@media (max-width: 576px) {
  .et-intro-lockup__tagline {
    font-size: 15px;
    margin-top: 20px;
  }
}
@media (max-width: 400px) {
  .et-intro-lockup {
    --logo-y: 170px;
    --logo-w: 164px;
    --logo-x: 10px;
  }
}

/* ==========================================================
   Eternal Tales tile grid (19.05.26)
   Replaces the three full tale sections (AOoS, BtM, VotF) that
   migrated to eternaltales.ie. Image / title / subtitle /
   short body per tile, one shared CTA out to eternaltales.ie
   below. The dark page background (--bg via [data-theme="dark"]
   in tokens.css) is the section bg now that the chamber is gone.
   ========================================================== */
.section--et-tales {
  /* Sit at section min-height: auto so it compresses to its
     content rather than forcing 100vh of empty above + below. */
  min-height: auto;
  padding-top: clamp(40px, 8vh, 96px);
  padding-bottom: clamp(40px, 8vh, 96px);
}
.section--et-tales .section__inner {
  max-width: clamp(280px, 92vw, 1200px);
}

.et-tales-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 3vw, 36px);
}

/* ==========================================================
   ET tile cards (20.05.26 rebuild)
   Patterned on the original .et-card from section-et-card.css:
   white card body with paper-grain noise, soft shadow, rounded
   corners, image flush at the top edge clipped by overflow:hidden,
   header + prose with internal padding. Smaller-scale than the
   original tale cards (these are tile summaries linking out to
   eternaltales.ie) but the same visual language.

   Local token overrides flip the inherited dark-theme tokens (the
   section is data-theme="dark") to light values so all descendant
   text reads as dark ink on the white card body, matching the
   original .et-card behaviour.
   ========================================================== */
/* 27.05.26 r91: 132ms delay on tile .fade-up transitions so the tiles
   come in 33% later than the bare .fade-up timing (= 33% of the 400ms
   .fade-up duration in shell.css). Part of the "delay all ET cards by
   33%" user direction. The IntersectionObserver in section-observers.js
   still flips .is-visible at the same scroll moment; only the CSS-side
   transition start is delayed. */
.et-tale-tile.fade-up {
  transition-delay: 132ms;
}

.et-tale-tile {
  display: flex;
  flex-direction: column;
  position: relative;
  --tile-text-primary: #0F1419;
  --tile-text-soft: #5E7780;
  background: #FFFFFF;
  color: var(--tile-text-primary);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.45);
  isolation: isolate;
}
.et-tale-tile::before {
  /* Paper grain over the whole card, same SVG/opacity values used
     on the original .et-card. */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='3.5' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.55  0 0 0 0 0.45  0 0 0 0 0.30  0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 400px 400px;
  opacity: 0.08;
}
.et-tale-tile > * {
  position: relative;
  z-index: 1;
}

.et-tale-tile__img {
  display: block;
  position: relative;          /* containing block for lightbox-trigger */
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: rgba(22, 24, 26, 0.4);
  margin: 0;
  /* 21.05.26 r8: zoom-in cursor removed per Caoimhín - the lightbox
     (i) trigger sits at the bottom-right corner, the image itself
     should read as a normal hero plate without a magnifier hint. */
}
.et-tale-tile__img picture,
.et-tale-tile__img img {
  width: 100%;
  height: 100%;
  display: block;
}
.et-tale-tile__img img {
  object-fit: cover;
}

/* 27.05.26 r73: Before the Myth tile carousel. Three artwork slides
   stacked absolutely inside the figure; the .is-active one paints on
   top (z:1) and is opacity-1. Crossfade timing matches js/v3/tile-
   carousel.js (interval 5s + 800ms ease). Outgoing + incoming opacity
   ease curves are mirrored so their sum stays at 1 mid-fade -- no dark
   midpoint dip through the figure's rgba(22,24,26,0.4) bg.
   tile-carousel.js also re-orders the active <picture> to be the
   figure's first child so lightbox.js's figure.querySelector('img')
   picks up the currently-visible image; the figure's data-lightbox-
   caption is mirrored from the active slide's data-caption. The
   .lightbox-trigger button injected by lightbox.js stays at the end of
   the figure as appendChild() puts it. */
.et-tale-tile__img--carousel .et-tile-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  z-index: 0;
  /* 27.05.26 r75: fade duration 800ms -> 600ms (25% faster) per user. */
  transition: opacity 600ms ease;
  pointer-events: none;
}
.et-tale-tile__img--carousel .et-tile-slide.is-active {
  opacity: 1;
  z-index: 1;
}
@media (prefers-reduced-motion: reduce) {
  /* Disable the crossfade for reduce-motion users; tile-carousel.js
     also skips auto-advance when the same media query matches, so the
     first slide simply stays visible. */
  .et-tale-tile__img--carousel .et-tile-slide {
    transition: none;
  }
}
/* 21.05.26 r8: hover scale retired per Caoimhín. The lightbox (i)
   trigger at the corner is the only interaction on the image; the
   image plate itself stays still on hover. */

.et-tale-tile__title {
  margin: 0;
  padding: clamp(18px, 2.5vw, 28px) clamp(20px, 3vw, 32px) clamp(8px, 1vw, 14px);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(22px, 1.4vw + 14px, 30px);
  line-height: 1.1;
  letter-spacing: -0.005em;
  color: var(--tile-text-primary);
}

/* Subtitle in display roman (not italic serif) - mirrors the
   .et-card__subtitle treatment from section-et-card.css so the
   subtitle reads as a sub-headline rather than a stylised pull. */
.et-tale-tile__tagline {
  margin: 0;
  padding: 0 clamp(20px, 3vw, 32px) clamp(14px, 1.6vw, 20px);
  font-family: var(--font-display);
  font-weight: 500;
  font-style: normal;
  font-size: clamp(14px, 0.5vw + 12px, 17px);
  line-height: 1.35;
  color: var(--tile-text-soft);
}

.et-tale-tile__body {
  margin: 0;
  padding: 0 clamp(20px, 3vw, 32px);
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  /* flex-grow pushes the CTA below to the tile's bottom so all
     three tiles end with their "Read more" links aligned. */
  flex-grow: 1;
}
.et-tale-tile__body p {
  margin: 0;
  font-family: var(--font-serif);
  font-size: clamp(14px, 0.25vw + 13px, 16px);
  line-height: 1.55;
  color: var(--tile-text-primary);
}

/* Per-tile CTAs retired - replaced by the lightbox info trigger
   that lightbox.js injects into each .et-tale-tile__img figure.
   Clicking the (i) opens a fullscreen view of the cover with the
   artist credit + extended caption. */
.et-tale-tile__body {
  padding-bottom: clamp(22px, 2.5vw, 32px);  /* restore the bottom padding the CTA used to provide */
}

.et-tales-cta {
  margin: clamp(28px, 4vw, 48px) 0 0;
  /* 27.05.26 r75: CTA now lives at the section foot (after the tile
     grid), so it's only ever in view once the reader has finished the
     three tiles -- the natural DOM position handles the "only show
     once they're properly into the ET region" behaviour without an
     IntersectionObserver toggle. opacity / pointer-events gating
     dropped accordingly. */
  text-align: left;
  font-family: var(--font-sans);
  font-size: clamp(14px, 0.3vw + 13px, 16px);
  letter-spacing: 0.02em;
}
.et-tales-cta a {
  color: var(--primary-text);
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 63, 209, 0.4);
  padding-bottom: 2px;
  transition: color 160ms ease, border-color 160ms ease;
}
.et-tales-cta a:hover,
.et-tales-cta a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* 21.05.26 r11: single-column stack at ALL widths per Caoimhín.
   The earlier 2-up / 1-up step-down at &lt;992 broke the stacked
   rhythm established by the &gt;=992 layout. Now every viewport
   reads as a vertical stack of tiles, matching the desktop
   composition. */
@media (max-width: 991px) {
  /* r60 (21.05.26): the intro glass card (1/4) holder must use the SAME
     width as the tile grid below 992 - clamp(280px, 92vw, 900px) - not
     the section's default max-content (920px) minus its 24px gutters,
     which left the intro card up to ~28px NARROWER than the 2-4/4 tiles
     at the wide end of this range (e.g. 872 vs 900 at 991). Drop the
     section's horizontal gutter here and adopt the shared card width so
     all four cards are exactly the same width across every breakpoint.
     (>=992 already matches via the column maths in the min-width rule.) */
  #our-stories .section__inner {
    max-width: clamp(280px, 92vw, 900px);
    padding-left: 0;
    padding-right: 0;
  }
  /* 21.05.26 r13: bump margin-top so the first product tile (2/4)
     doesn't crowd the intro glass card (1/4) above it. */
  #our-stories > #eternal-tales-tales {
    margin-top: clamp(48px, 8vw, 96px);
  }
  .et-tales-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(28px, 5vw, 44px);
    /* 21.05.26 r13: match the intro et-card's max-width so the 2/4,
       3/4, 4/4 product tiles sit at exactly the same width as the
       1/4 intro card above. */
    max-width: clamp(280px, 92vw, 900px);
    margin-left: auto;
    margin-right: auto;
  }
  .et-tale-tile:last-child {
    grid-column: auto;
    max-width: none;
    justify-self: stretch;
  }
}
