/* ─── page-level: pages/homepage.html (sha=373132d) ─── */
/* ────────────────────────────────────────────────────────────────────
   Page-level styles for #tenki-page-homepage.

   This block ONLY contains rules that need to be at the PAGE level —
   i.e. the page wrapper, the page-level build chip, and inter-section
   spacing.  Section-specific styles live in each section's own file
   and get concatenated into dist/homepage.css after this block.

   The cascade therefore looks like:
     1. these page-level rules
     2. hero's CSS  (from hero/hero.html)
     3. meaning's CSS
     4. pillars's CSS
     5. timeline's CSS
     6. social's CSS
   Sections style their own scope with prefixed class names
   (.tenki-hero-*, .tenki-meaning-*, .dashboard-*, .tenki-social-*, etc.)
   so concatenation doesn't leak between them. */

#tenki-page-homepage {
  position: relative;
  /* We control the entire vertical rhythm now — no SQS section padding
     to fight.  The single Code Block that holds this composed shell
     should still respect the SQS page's outer margins, but we own
     everything inside. */

  /* Reveal-on-CSS-loaded.  The inline <style> at the top of the shell
     starts the wrapper at opacity:0 so the wall-of-text flash never
     happens before the CDN bundle lands.  Because this CDN bundle is
     loaded AFTER that inline style, the cascade picks this rule by
     source order + the same selector wins — flipping opacity to 1 and
     fading the page in over the inline transition (0.35 s). */
  opacity: 1;
}
/* Don't animate the section reveals individually until the page
   wrapper itself has finished fading in — otherwise the first
   section "races" the wrapper and looks jittery on slow connections.
   The reveal IntersectionObserver still fires, but the transition
   inherits the wrapper's eased ramp. */

/* Page-level build chip — same look as the per-section chips so it's
   visually consistent. */
#tenki-page-homepage .tenki-page-build-chip {
  position: absolute; top: 8px; right: 12px;
  font: 600 10px/1 monospace; padding: 3px 8px; border-radius: 4px;
  background: #1A1A1A; color: #fff; opacity: .85;
  display: none; pointer-events: none; z-index: 10000;
}
#tenki-page-homepage[data-debug="1"] .tenki-page-build-chip { display: block; }

/* Inter-section vertical rhythm.  We're free to pick whatever spacing
   reads best now that SQS isn't padding between us. */
#tenki-page-homepage > * + * { margin-top: 0; }
/* Specific overrides if a particular section needs more / less air: */
/* Meaning section keeps its OWN margin-top — its CSS pulls it UP via
   negative margin (~ -240 px) so the glass card overlays the bottom-
   right of the hero photo.  Don't override that here. */
/* #tenki-page-homepage .tenki-meaning-container { ... } */
#tenki-page-homepage .tenki-pillars-container         { margin-top: 32px; }
#tenki-page-homepage .dashboard-container             { margin-top: 32px; }
#tenki-page-homepage .tenki-funnel-container          { margin-top: 32px; }
#tenki-page-homepage .tenki-social-parallax-section   { margin-top: 24px; }
#tenki-page-homepage .tenki-newsletter-section        { margin-top: 0; }
/* Footer is NOT composed into this shell — it lives in the global
   SQS footer (one paste, every page).  See footer/footer.html for
   the source + dist/footer.shell.html for the paste payload. */

/* ── Section reveal animation ─────────────────────────────────────
   Every major composed section starts invisible + translated down
   8 px; when it scrolls into view, IntersectionObserver flips a
   `.is-revealed` class which slides + fades it into place.  This
   masks the staggered shell-then-CDN-then-API content cascade so
   the page reads as a deliberate sequence rather than a clunky
   pop-in flurry.

   .respect-motion exempts users with prefers-reduced-motion. */
#tenki-page-homepage .tenki-pillars-container,
#tenki-page-homepage .dashboard-container,
#tenki-page-homepage .tenki-funnel-container,
#tenki-page-homepage .tenki-social-parallax-section,
#tenki-page-homepage .tenki-newsletter-section,
#tenki-page-homepage .tenki-meaning-container {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.7s cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 0.7s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: opacity, transform;
}
#tenki-page-homepage .tenki-pillars-container.is-revealed,
#tenki-page-homepage .dashboard-container.is-revealed,
#tenki-page-homepage .tenki-funnel-container.is-revealed,
#tenki-page-homepage .tenki-social-parallax-section.is-revealed,
#tenki-page-homepage .tenki-newsletter-section.is-revealed,
#tenki-page-homepage .tenki-meaning-container.is-revealed {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  #tenki-page-homepage .tenki-pillars-container,
  #tenki-page-homepage .dashboard-container,
  #tenki-page-homepage .tenki-funnel-container,
  #tenki-page-homepage .tenki-social-parallax-section,
  #tenki-page-homepage .tenki-newsletter-section,
  #tenki-page-homepage .tenki-meaning-container {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Hide the per-section build chips when composed — only the page-level
   chip should be visible to avoid stacking ~5 chips in the same spot.
   Per-section chips can still be inspected by removing `display: none`
   from devtools. */
#tenki-page-homepage .tenki-hero-build-chip,
#tenki-page-homepage .tenki-meaning-build-chip,
#tenki-page-homepage .tenki-pillars-build-chip,
#tenki-page-homepage .tenki-build-chip,
#tenki-page-homepage .tenki-funnel-build-chip,
#tenki-page-homepage .tenki-social-build-chip,
#tenki-page-homepage .tenki-newsletter-build-chip {
  display: none !important;
}

/* ─── hero: hero/hero.html (sha=39b9171) ─── */
/* Tenki Hero Section
     Full-bleed (escapes Squarespace's container max-width via 100vw +
     margin-left:-50vw trick) photo with an overlay card on the right.
     CSS scoped via the .tenki-hero class prefix; no globals affected. */
  .tenki-hero {
    --hero-text:  #ffffff;
    --hero-shadow: 0 4px 18px rgba(0, 0, 0, 0.35);
    --hero-cta-bg:    #ffffff;
    --hero-cta-text:  #1A1A1A;
    --hero-cta-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);

    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    position: relative;
    width: 100vw;
    /* Full-bleed horizontal + flush against the nav bar above.
       Squarespace section wrappers add their own vertical padding
       (typically 17 px top / 17 px bottom).  We compensate by
       PULLING UP with a negative top margin equal to that padding
       so the hero sits visually right against the nav.  Some SQS
       templates use a larger top pad; if there's still a gap on
       your specific page, either bump this -68px to -90px / -110px
       OR enable the .sqs-block-code:has() override below. */
    margin: -68px -50vw 0 -50vw;
    left: 50%;
    right: 50%;
    /* Aspect ratio matched to the source SQS hero: roughly 16:7 on
       desktop, taller on mobile so the photo doesn't compress.  The
       photo is set as a background-image inside `.tenki-hero-image` so
       background-position can be tuned without changing the markup. */
    aspect-ratio: 16 / 7;
    min-height: 320px;
    max-height: 78vh;
    overflow: hidden;
    isolation: isolate;
    z-index: 1;
    box-sizing: border-box;
  }
  .tenki-hero *,
  .tenki-hero *::before,
  .tenki-hero *::after { box-sizing: border-box; }

  /* The photo layer — fills the entire hero, behind the overlay card */
  .tenki-hero-image {
    position: absolute;
    inset: 0;
    z-index: 1;
    background-position: center 32%;   /* bias slightly UP so faces stay in frame on wide viewports */
    background-size: cover;
    background-repeat: no-repeat;
    /* Fallback colour while the photo is loading — deep Tenki blue so
       an unloaded hero reads as a deliberate dark panel rather than
       blank whitespace under floating headline text.  Replaced by the
       photo as soon as it arrives. */
    background-color: #00243f;
  }

  /* Hide the headline + meaning card until the hero photo has loaded
     (JS adds .is-hero-loaded on the hero root when the Image() preloader
     fires onload).  Until then the deep-blue fallback shows alone — no
     orphan text on a blank panel.  3-second failsafe in the JS guarantees
     the overlay reveals even if the photo can't load (offline / CDN
     hiccup) so we never trap the visitor on a faceless hero. */
  .tenki-hero .tenki-hero-overlay {
    opacity: 0;
    transition: opacity 0.45s ease-out;
  }
  .tenki-hero.is-hero-loaded .tenki-hero-overlay { opacity: 1; }
  @media (prefers-reduced-motion: reduce) {
    .tenki-hero .tenki-hero-overlay { transition: none; }
  }
  /* Soft right-side vignette that boosts headline contrast without
     darkening the whole image.  Adjusts per .data-align variant below. */
  .tenki-hero[data-align="right"] .tenki-hero-image::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to left,
                rgba(0, 0, 0, 0.45) 0%,
                rgba(0, 0, 0, 0.30) 25%,
                rgba(0, 0, 0, 0.08) 55%,
                rgba(0, 0, 0, 0)    80%);
    pointer-events: none;
  }
  .tenki-hero[data-align="center"] .tenki-hero-image::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom,
                rgba(0, 0, 0, 0)    0%,
                rgba(0, 0, 0, 0.18) 50%,
                rgba(0, 0, 0, 0.55) 100%);
    pointer-events: none;
  }
  .tenki-hero[data-align="left"] .tenki-hero-image::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to right,
                rgba(0, 0, 0, 0.45) 0%,
                rgba(0, 0, 0, 0.30) 25%,
                rgba(0, 0, 0, 0.08) 55%,
                rgba(0, 0, 0, 0)    80%);
    pointer-events: none;
  }

  /* Overlay container — positions the card per data-align */
  .tenki-hero-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    padding: 5%;
    pointer-events: none;
  }
  .tenki-hero[data-align="right"]  .tenki-hero-overlay { justify-content: flex-end; }
  .tenki-hero[data-align="center"] .tenki-hero-overlay { justify-content: center; align-items: flex-end; padding-bottom: 9%; }
  .tenki-hero[data-align="left"]   .tenki-hero-overlay { justify-content: flex-start; }

  /* Stack wrapper — meaning card sits ABOVE the headline+CTA on the
     right side.  align-items: flex-end keeps both children right-
     aligned (matches the data-align="right" pattern). */
  .tenki-hero-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 24px;
    pointer-events: auto;
    max-width: 560px;
  }

  /* The "Tenki for born" glass card overlay.  Translucent white +
     backdrop-filter blur reads as layered over the photo without
     obscuring it.  Higher opacity (0.92 vs prev 0.84) so the body
     text is comfortably readable against the busy photo behind.
     Hover lift + soft shadow growth matches the bounce animation
     the rest of the site's cards use (pillars, deep-dive, etc.),
     inviting desktop users to engage with it. */
  .tenki-hero-meaning {
    width: 100%;
    max-width: 500px;
    padding: 26px 32px;
    background-color: rgba(255, 255, 255, 0.96);   /* nearly opaque — body text fully legible */
    backdrop-filter: blur(18px) saturate(140%);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    border-radius: 14px;
    border-left: 5px solid #005A9C;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.32),
                0 0 0 1px rgba(255, 255, 255, 0.6) inset;
    text-align: left;
    color: #111;
    transition: transform 0.28s ease, box-shadow 0.28s ease;
    cursor: default;
    animation: tenkiHeroMeaningIn 0.7s ease-out 0.15s both;
  }
  .tenki-hero-meaning:hover {
    transform: translateY(-4px);
    box-shadow: 0 22px 48px rgba(0, 0, 0, 0.36),
                0 0 0 1px rgba(255, 255, 255, 0.65) inset;
  }
  @keyframes tenkiHeroMeaningIn {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .tenki-hero-meaning-title {
    margin: 0 0 6px 0;
    color: #005A9C;
    font-size: 1.6rem;          /* was 1.4 — bigger, more confident */
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1.2px;
  }
  .tenki-hero-meaning-subtitle {
    margin: 0 0 14px 0;
    color: #111;
    font-size: 1.1rem;          /* was 1.0 — more readable italic */
    font-weight: 500;
    font-style: italic;
  }
  .tenki-hero-meaning-divider {
    width: 48px;
    height: 3px;
    background-color: #005A9C;
    border-radius: 2px;
    margin-bottom: 14px;
  }
  .tenki-hero-meaning-text {
    margin: 0;
    color: #1a1a1a;             /* darker for higher contrast */
    font-size: 1.02rem;         /* was 0.92 — comfortable reading */
    line-height: 1.6;
    font-weight: 500;
  }
  .tenki-hero-meaning-text strong { color: #000; font-weight: 800; }

  /* The card — headline + CTA stacked.  Transparent background to keep
     the photo behind it visible; text + shadow do the lift.
     Gentle fade-in so it appears in sync with the meaning card above
     (reduces the staggered "stuff popping in" load feel). */
  .tenki-hero-card {
    max-width: 560px;
    color: var(--hero-text);
    text-align: right;
    pointer-events: auto;
    animation: tenkiHeroCardIn 0.8s ease-out 0.35s both;
  }
  @keyframes tenkiHeroCardIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .tenki-hero[data-align="center"] .tenki-hero-card { text-align: center; }
  .tenki-hero[data-align="left"]   .tenki-hero-card { text-align: left;  }

  .tenki-hero-headline {
    margin: 0 0 28px 0;
    font-size: clamp(1.6rem, 3.6vw, 3rem);
    font-weight: 400;
    line-height: 1.18;
    letter-spacing: 0.2px;
    color: var(--hero-text);
    text-shadow: var(--hero-shadow);
    /* Allow the headline to wrap naturally to 2-3 lines on most viewports */
    text-wrap: balance;
  }
  .tenki-hero-headline strong,
  .tenki-hero-headline em {
    font-weight: 700;
    font-style: normal;
  }

  .tenki-hero-cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 14px 32px;
    background: var(--hero-cta-bg);
    color: var(--hero-cta-text);
    text-decoration: none;
    border-radius: 4px;
    font-weight: 600;
    font-size: clamp(0.92rem, 1.4vw, 1.05rem);
    letter-spacing: 0.4px;
    box-shadow: var(--hero-cta-shadow);
    transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    -webkit-tap-highlight-color: transparent;
  }
  .tenki-hero-cta:hover,
  .tenki-hero-cta:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 10px 26px rgba(0, 0, 0, 0.26);
    background: #f8fafc;
    outline: none;
  }
  .tenki-hero-cta:active {
    transform: translateY(0);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.20);
  }

  /* Build chip — only visible with ?debug=1 (toggled via .show-build) */
  .tenki-hero-build-chip {
    position: absolute;
    top: 10px;
    right: 12px;
    z-index: 5;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: #fff;
    background: rgba(0, 90, 156, 0.85);
    padding: 3px 8px;
    border-radius: 10px;
    letter-spacing: 0.3px;
    display: none;
  }
  .tenki-hero.show-build .tenki-hero-build-chip { display: inline-block; }

  /* Responsive — narrower viewports get a taller hero + overlay shifts
     to bottom for thumb-friendly button placement.  Below 640 px the
     headline gets a darker bottom gradient so it stays legible against
     the body of the photo. */
  @media (max-width: 960px) {
    .tenki-hero {
      aspect-ratio: 4 / 3;
      max-height: 70vh;
    }
    .tenki-hero[data-align="right"] .tenki-hero-overlay,
    .tenki-hero[data-align="left"]  .tenki-hero-overlay {
      align-items: flex-end;
      padding-bottom: 8%;
    }
    .tenki-hero-card { text-align: right; }
  }
  @media (max-width: 640px) {
    .tenki-hero {
      aspect-ratio: 3 / 4;     /* tall portrait on phone */
      max-height: 80vh;
    }
    /* Phone-portrait hero is too tall + narrow to host both the
       meaning card AND the headline overlay — hide the meaning card
       (it still renders below via the standalone meaning section in
       the page composer, which mobile shows in-flow). */
    .tenki-hero-meaning { display: none; }
    .tenki-hero-stack { gap: 0; }
    .tenki-hero-image::after,
    .tenki-hero[data-align="right"] .tenki-hero-image::after,
    .tenki-hero[data-align="left"]  .tenki-hero-image::after {
      background: linear-gradient(to bottom,
                  rgba(0, 0, 0, 0)    35%,
                  rgba(0, 0, 0, 0.35) 65%,
                  rgba(0, 0, 0, 0.70) 100%) !important;
    }
    .tenki-hero-overlay {
      padding: 6%;
      align-items: flex-end;
      justify-content: center !important;
    }
    .tenki-hero-card {
      text-align: center !important;
      max-width: 100%;
    }
    .tenki-hero-cta { padding: 12px 28px; }
  }

/* ─── meaning: meaning/meaning.html (sha=6d31b43) ─── */
.tenki-meaning-container {
    --tenki-blue:  #005A9C;
    --tenki-black: #1A1A1A;
    --tenki-white: #FFFFFF;
    --text-light:  #333333;

    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    /* This standalone .tenki-meaning-container is now MOBILE-ONLY for
       the homepage — desktop carries the meaning card inline INSIDE
       the hero (see hero/hero.html, .tenki-hero-meaning).  This
       version still ships in the page composer so phones (where the
       hero is portrait + can't host an overlay card) see the
       meaning content as a normal in-flow card below the hero. */
    max-width: 800px;
    margin: 16px auto 24px;
    background-color: var(--tenki-white);
    border-radius: 16px;
    padding: 34px 28px;
    box-shadow: 0 10px 40px rgba(0, 90, 156, 0.08);
    border-left: 8px solid var(--tenki-blue);
    position: relative;
    overflow: hidden;
    box-sizing: border-box;
  }
  /* Desktop: hide the standalone in-flow meaning card — its content
     now lives INSIDE the hero (see .tenki-hero-meaning in hero.html).
     Showing both would be duplicate content. */
  @media (min-width: 769px) {
    .tenki-meaning-container { display: none; }
  }
  .tenki-meaning-container *,
  .tenki-meaning-container *::before,
  .tenki-meaning-container *::after { box-sizing: border-box; }

  /* Decorative oversized quote mark behind the content */
  .tenki-meaning-container::before {
    content: '"';
    position: absolute;
    top: -20px;
    left: 30px;
    font-size: 150px;
    color: rgba(0, 90, 156, 0.05);
    font-family: Georgia, serif;
    line-height: 1;
    z-index: 0;
  }

  .tenki-meaning-content {
    position: relative;
    z-index: 1;
  }

  .tenki-meaning-title {
    color: var(--tenki-blue);
    font-size: 1.55rem;
    font-weight: 800;
    margin: 0 0 4px 0;
    text-transform: uppercase;
    letter-spacing: 1px;
  }
  .tenki-meaning-subtitle {
    color: var(--tenki-black);
    font-size: 1rem;
    font-weight: 400;
    font-style: italic;
    margin: 0 0 14px 0;
  }
  .tenki-meaning-divider {
    width: 48px;
    height: 3px;
    background-color: var(--tenki-blue);
    border-radius: 2px;
    margin-bottom: 14px;
  }
  .tenki-meaning-text {
    color: var(--text-light);
    font-size: 0.92rem;
    line-height: 1.55;
    margin: 0;
  }
  .tenki-meaning-text strong {
    color: var(--tenki-black);
    font-weight: 700;
  }
  .tenki-meaning-text em {
    font-style: italic;
  }

  /* Build chip — visible only with ?debug=1 */
  .tenki-meaning-build-chip {
    position: absolute;
    top: 10px;
    right: 12px;
    z-index: 5;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: #fff;
    background: rgba(0, 90, 156, 0.85);
    padding: 3px 8px;
    border-radius: 10px;
    letter-spacing: 0.3px;
    display: none;
  }
  .tenki-meaning-container.show-build .tenki-meaning-build-chip { display: inline-block; }

  @media (max-width: 768px) {
    /* Mobile: revert to a normal in-flow card BELOW the hero — the
       floating-glass overlay pattern doesn't make sense on a 390 px
       phone screen.  No backdrop-filter (perf hit on mobile + the
       card no longer floats over a photo).  Restore generous
       in-flow margin. */
    .tenki-meaning-container {
      max-width: 100%;
      padding: 28px 22px;
      margin: 16px 16px 24px;
      background-color: var(--tenki-white);
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
      box-shadow: 0 8px 24px rgba(0, 90, 156, 0.10);
    }
    .tenki-meaning-title    { font-size: 1.55rem; }
    .tenki-meaning-subtitle { font-size: 1rem; }
    .tenki-meaning-text     { font-size: 0.95rem; line-height: 1.6; }
    .tenki-meaning-container::before {
      left: 15px;
      font-size: 120px;
    }
  }

/* ─── pillars: pillars/pillars.html (sha=b29f35b) ─── */
:root {
    --tenki-blue:  #005A9C;
    --tenki-green: #008C45;
    --tenki-black: #1A1A1A;
    --tenki-white: #FFFFFF;
    /* White-theme accents — matches deepdive-page.html's theme-white palette
       so the SOS pillar card on the homepage visually rhymes with the
       deep-dive page it links to.  Dark glyph + dashed cream-stroke accents
       (white-on-white would be invisible). */
    --tenki-white-dark:   #2A2A2A;
    --tenki-white-cream:  #FAF6EE;
    --tenki-white-stroke: #C0C5CC;
    --bg-light:    #F8F9FA;
    --text-dark:   #222222;
    --text-light:  #555555;
  }

  body { font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
         background-color: var(--tenki-white); margin: 0; padding: 20px; box-sizing: border-box; }
  *, *::before, *::after { box-sizing: inherit; }

  .infographic-container { max-width: 1200px; margin: 0 auto;
                           background-color: var(--bg-light); border-radius: 16px;
                           padding: 40px; box-shadow: 0 10px 30px rgba(0,0,0,.05); }

  .infographic-header { text-align: center; margin-bottom: 50px; }
  .infographic-header h2 { color: var(--tenki-black); font-size: 2.5rem;
                           margin: 0 0 15px; text-transform: uppercase;
                           letter-spacing: 1px; line-height: 1.2; }
  .infographic-header p { color: var(--text-light); font-size: 1.2rem;
                          max-width: 800px; margin: 0 auto; line-height: 1.6; }

  /* Loader / error */
  #loading-spinner { text-align: center; padding: 50px;
                     color: var(--tenki-blue); font-weight: bold; font-size: 1.2rem; }
  #loading-spinner.error { color: #b91c1c; }

  .pillars-wrapper { display: none;
                     grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
                     gap: 30px; align-items: start; }

  .pillar-card { background-color: var(--tenki-white); border-radius: 12px;
                 padding: 30px; border-top: 6px solid;
                 display: flex; flex-direction: column;
                 position: relative; cursor: pointer;  /* whole card is clickable */
                 transition: transform .4s cubic-bezier(0.175, 0.885, 0.32, 1.275),
                             box-shadow .4s ease, outline-color .2s ease; }
  .pillar-card:hover { transform: translateY(-4px);
                       box-shadow: 0 12px 30px rgba(0,0,0,.08); }
  .pillar-card:focus-visible { outline: 3px solid var(--tenki-blue); outline-offset: 4px; }
  /* Subtle chevron in the top-right indicates the card expands */
  .pillar-chevron { position: absolute; top: 18px; right: 18px;
                    width: 28px; height: 28px; border-radius: 50%;
                    display: flex; align-items: center; justify-content: center;
                    font-size: 14px; color: #888; background: rgba(0,0,0,.04);
                    transition: transform .35s ease, background .2s ease, color .2s ease;
                    pointer-events: none; /* clicks pass through to the card */ }
  .pillar-card:hover .pillar-chevron { background: rgba(0,0,0,.08); color: #444; }
  .pillar-card.is-expanded .pillar-chevron { transform: rotate(180deg); color: #444; }
  /* Subtle "click to expand" hint that fades out on hover */
  .pillar-click-hint { font-size: 10px; color: #999; text-align: center;
                       margin: 10px 0 -6px; letter-spacing: .5px;
                       text-transform: uppercase; font-weight: 600;
                       transition: opacity .2s ease; }
  .pillar-card.is-expanded .pillar-click-hint { opacity: 0; }
  .pillar-card:hover .pillar-click-hint { color: #555; }

  /* ── Smooth drawer using grid-template-rows: 0fr→1fr (no display:none hacks) ── */
  .pillar-expanded-drawer { display: grid; grid-template-rows: 0fr;
                            transition: grid-template-rows .4s ease, opacity .35s ease;
                            opacity: 0; margin-top: 0; }
  .pillar-expanded-drawer > .drawer-inner { overflow: hidden; min-height: 0; }
  .pillar-card.is-expanded .pillar-expanded-drawer {
    grid-template-rows: 1fr; opacity: 1;
    margin-top: 15px; border-top: 1px dashed #EAEAEA; padding-top: 20px;
  }

  /* Pillar themes */
  .card-blue  { border-top-color: var(--tenki-blue);
                background: linear-gradient(180deg, rgba(0,90,156,.04) 0%, rgba(255,255,255,1) 220px); }
  .card-blue .icon-wrapper       { background-color: var(--tenki-blue); }
  .card-blue .section-title      { color: var(--tenki-blue); }
  .card-blue .program-list li::before { color: var(--tenki-blue); }
  .card-blue .learn-more-btn     { background-color: var(--tenki-blue); color: var(--tenki-white); }
  .card-blue .learn-more-btn:hover { background-color: #004578; }
  .card-blue .explore-toggle-btn { background-color: #F0F4F8; color: var(--tenki-blue); }
  .card-blue .explore-toggle-btn:hover { background-color: #E1EBF5; }

  .card-green { border-top-color: var(--tenki-green);
                background: linear-gradient(180deg, rgba(0,140,69,.04) 0%, rgba(255,255,255,1) 220px); }
  .card-green .icon-wrapper       { background-color: var(--tenki-green); }
  .card-green .section-title      { color: var(--tenki-green); }
  .card-green .program-list li::before { color: var(--tenki-green); }
  .card-green .learn-more-btn     { background-color: var(--tenki-green); color: var(--tenki-white); }
  .card-green .learn-more-btn:hover { background-color: #006B34; }
  .card-green .explore-toggle-btn { background-color: #E8F5E9; color: var(--tenki-green); }
  .card-green .explore-toggle-btn:hover { background-color: #C8E6C9; }

  /* White theme — mirrors deepdive-page.html's theme-white palette so
     the SOS pillar card visually rhymes with its deep-dive landing page.
     Dashed cream-stroke top border, dark glyph on dark icon-wrapper
     (white-on-white would be invisible), dark button bg with white text. */
  .card-white { border-top: 6px dashed var(--tenki-white-stroke);
                background-color: #FDFCF8; }
  .card-white .icon-wrapper       { background-color: var(--tenki-white-dark); }
  .card-white .section-title      { color: var(--tenki-white-dark); }
  .card-white .program-list li::before { color: var(--tenki-white-dark); }
  .card-white .learn-more-btn     { background-color: var(--tenki-white-dark); color: var(--tenki-white); }
  .card-white .learn-more-btn:hover { background-color: #111; }
  .card-white .explore-toggle-btn { background-color: var(--tenki-white-cream); color: var(--tenki-white-dark); }
  .card-white .explore-toggle-btn:hover { background-color: #EFEAD8; }

  .card-black { border-top: 6px dashed var(--tenki-black);
                background: linear-gradient(180deg, rgba(26,26,26,.03) 0%, rgba(255,255,255,1) 220px); }
  .card-black .icon-wrapper       { background-color: var(--tenki-black); }
  .card-black .section-title      { color: var(--tenki-black); }
  .card-black .program-list li::before { color: var(--tenki-black); }
  .card-black .learn-more-btn     { background-color: var(--tenki-black); color: var(--tenki-white); }
  .card-black .learn-more-btn:hover { background-color: #333; }
  .card-black .explore-toggle-btn { background-color: #EEEEEE; color: var(--tenki-black); }
  .card-black .explore-toggle-btn:hover { background-color: #DDDDDD; }

  .card-header-flex { display: flex; justify-content: space-between;
                      align-items: center; min-height: 80px;
                      margin-bottom: 25px; gap: 15px; }
  .icon-wrapper { width: 80px; height: 80px; border-radius: 50%;
                  display: flex; align-items: center; justify-content: center;
                  font-size: 38px; color: var(--tenki-white); flex-shrink: 0;
                  box-shadow: 0 4px 10px rgba(0,0,0,.1);
                  transition: transform .4s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
  .pillar-card:hover .icon-wrapper { transform: scale(1.08); }

  .status-badge { padding: 6px 12px; border-radius: 20px;
                  font-size: .72rem; font-weight: 700; text-transform: uppercase;
                  letter-spacing: .5px; text-align: center; display: inline-block;
                  max-width: 65%; line-height: 1.3; }
  .badge-active { background-color: #e8f5e9; color: var(--tenki-green); border: 1px solid #c8e6c9; }
  .badge-future { background-color: #eeeeee; color: var(--tenki-black); border: 1px solid #ccc; }

  .pillar-title { font-size: 1.4rem; color: var(--tenki-black);
                  margin: 0 0 15px; font-weight: 700; line-height: 1.3; }
  .pillar-subtitle { margin: -10px 0 15px; font-style: italic;
                     font-size: .95rem; color: var(--text-light); }

  .pillar-section { margin-bottom: 20px; width: 100%; }
  .section-title { font-weight: 700; text-transform: uppercase;
                   font-size: .9rem; letter-spacing: .5px; margin-bottom: 8px; }
  .pillar-text { font-size: .95rem; color: var(--text-light);
                 line-height: 1.6; margin: 0 0 10px; }

  .media-wrapper { width: 100%; height: 180px; margin: 10px 0 15px;
                   border-radius: 8px; overflow: hidden;
                   box-shadow: 0 2px 8px rgba(0,0,0,.05); background-color: #eaeaea; }
  .pillar-image, .pillar-video { width: 100%; height: 100%; object-fit: cover; }

  .program-list { list-style: none; padding: 0; margin: 0 0 15px; width: 100%; }
  .program-list li { font-size: .95rem; color: var(--text-light); line-height: 1.5;
                     margin-bottom: 12px; padding-left: 20px; position: relative; }
  .program-list li::before { content: '•'; position: absolute; left: 0;
                             font-size: 1.2rem; font-weight: bold; }
  .split-list { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; margin-bottom: 15px; }

  .learn-more-btn { align-self: center; display: flex;
                    justify-content: center; align-items: center; gap: 8px;
                    margin: 25px auto 15px; padding: 14px 28px;
                    border-radius: 8px; text-decoration: none;
                    font-weight: 700; text-transform: uppercase;
                    font-size: .85rem; letter-spacing: .5px;
                    width: auto; min-width: 200px; max-width: 100%;
                    transition: background-color .2s ease, transform .2s ease; }
  .learn-more-btn::after { content: '→'; font-size: 1.1rem;
                           transition: transform .2s ease; margin-left: 4px; }
  .learn-more-btn:hover { transform: translateY(-2px); }
  .learn-more-btn:hover::after { transform: translateX(4px); }

  .explore-toggle-btn { align-self: center; width: auto;
                        min-width: 200px; max-width: 100%;
                        display: flex; justify-content: center; align-items: center;
                        margin-top: auto; padding: 14px 24px;
                        border-radius: 8px; border: none;
                        font-weight: 700; text-transform: uppercase;
                        font-size: .85rem; letter-spacing: .5px;
                        cursor: pointer;
                        transition: background-color .2s ease, transform .2s ease; }
  .explore-toggle-btn:hover { transform: translateY(-1px); }

  /* Funding boxes */
  .funding-container { display: none; gap: 20px; margin-top: 40px; }
  .funding-box { flex: 1; padding: 25px; border-radius: 12px;
                 display: flex; align-items: center; gap: 20px;
                 box-shadow: 0 4px 10px rgba(0,0,0,.03);
                 transition: transform .3s ease, box-shadow .3s ease;
                 text-decoration: none; }
  .funding-box:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0,0,0,.06); }
  .funding-icon { font-size: 2.5rem; flex-shrink: 0; transition: transform .3s ease; }
  .funding-box:hover .funding-icon { transform: scale(1.1); }
  .funding-text h4 { margin: 0 0 5px; font-size: 1.1rem;
                     text-transform: uppercase; letter-spacing: .5px; }
  .funding-text p { margin: 0; font-size: .95rem; line-height: 1.5; }
  .funding-current { background-color: #eef5fb; border: 2px solid #b3d4f0; }
  .funding-current h4 { color: var(--tenki-blue); }
  .funding-current p { color: #2C4A68; }
  .funding-future  { background-color: #f5f5f5; border: 2px dashed #ccc; }
  .funding-future h4 { color: var(--tenki-black); }
  .funding-future p { color: var(--text-light); }

  /* ── Card extras ────────────────────────────────────────────────────────── */
  /* Subtle "recent activity" tag — same gentle styling for any pillar that has it. */
  .recent-activity-tag { display: inline-flex; align-items: center; gap: 4px;
                         font-size: .65rem; font-weight: 700; text-transform: uppercase;
                         letter-spacing: .5px; color: #166534;
                         background: #f0fdf4; border: 1px solid #bbf7d0;
                         padding: 2px 8px; border-radius: 10px;
                         margin: 4px 0 0; align-self: flex-start; }
  .recent-activity-dot { width: 6px; height: 6px; border-radius: 50%;
                         background: var(--tenki-green); animation: gentlePulse 2s ease-in-out infinite; }
  @keyframes gentlePulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

  .reading-time { font-size: .68rem; color: #888; margin: -4px 0 12px;
                  font-style: italic; padding-top: 12px;
                  border-top: 1px dashed rgba(0,0,0,.05); }

  /* ── Optional element-level links (Icon, Title, Section Title, Media …) ──
     When an editor sets an "X Link URL" field on the sheet, the matching
     element wraps in an <a>.  These styles keep the visual exactly the same
     as the non-linked version — no default underline, native colors — but
     reveal a small ↗ glyph on hover so users know it leads somewhere. */
  .pillar-elem-link { display: inline-block; color: inherit; text-decoration: none;
                      position: relative; transition: opacity .15s; }
  .pillar-elem-link:hover { opacity: .85; }
  .pillar-elem-link.link-heading::after,
  .pillar-elem-link.link-title::after { content: ' ↗'; font-size: .7em;
                                        opacity: 0; transition: opacity .15s;
                                        color: var(--tenki-blue); }
  .pillar-elem-link.link-heading:hover::after,
  .pillar-elem-link.link-title:hover::after { opacity: 1; }
  .pillar-elem-link.link-media { display: block; position: relative; cursor: pointer; }
  .pillar-elem-link.link-media::after {
    content: '↗'; position: absolute; top: 12px; right: 12px;
    width: 26px; height: 26px; border-radius: 50%;
    background: rgba(0,0,0,.55); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; opacity: 0; transition: opacity .2s;
  }
  .pillar-elem-link.link-media:hover::after { opacity: 1; }
  .pillar-elem-link.link-icon { display: inline-block; border-radius: 50%; }
  .pillar-elem-link.link-icon:hover .icon-wrapper { transform: scale(1.12); }

  /* Share button in the top-right of each card (next to the chevron) */
  .share-btn { position: absolute; top: 18px; right: 54px;
               width: 28px; height: 28px; border-radius: 50%;
               background: rgba(0,0,0,.04); color: #666;
               border: none; cursor: pointer; padding: 0;
               display: flex; align-items: center; justify-content: center;
               transition: background .2s, color .2s, opacity .2s;
               opacity: .35; /* always faintly visible; brightens on hover */ }
  .pillar-card:hover .share-btn,
  .share-btn:focus-visible { opacity: 1; }
  .share-btn:hover { background: rgba(0,0,0,.08); color: var(--tenki-blue); }
  .share-btn.copied { background: var(--tenki-green); color: #fff; }
  .share-btn.copied svg { display: none; }
  .share-btn.copied::after { content: '✓'; font-size: 14px; font-weight: bold; }

  /* First-visit chevron pulse — auto-disables after first interaction */
  @keyframes chevronAttention {
    0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0,90,156,0); }
    50%      { transform: scale(1.18); box-shadow: 0 0 0 6px rgba(0,90,156,.15); }
  }
  .pillar-card.first-visit-pulse .pillar-chevron { animation: chevronAttention 1.5s ease-in-out 2; }

  /* ── Image skeleton placeholder ─────────────────────────────────────────── */
  .pillar-image, .pillar-video { transition: opacity .4s ease; }
  .pillar-image:not(.loaded), .pillar-video:not(.loaded) { opacity: 0; }
  .media-wrapper { position: relative; }
  .media-wrapper.loading::before {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, #eaeaea 0%, #f3f4f6 50%, #eaeaea 100%);
    background-size: 200% 100%; animation: skeleton 1.4s ease-in-out infinite;
  }
  @keyframes skeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

  /* ── Funding progress bar ───────────────────────────────────────────────── */
  .funding-progress { width: 100%; margin-top: 10px; }
  .funding-progress-bar { width: 100%; height: 8px; background: rgba(0,0,0,.06);
                          border-radius: 4px; overflow: hidden; }
  .funding-progress-fill { height: 100%; border-radius: 4px;
                           transition: width .6s ease; }
  .funding-current .funding-progress-fill { background: var(--tenki-blue); }
  .funding-future  .funding-progress-fill { background: #999; }
  .funding-progress-labels { display: flex; justify-content: space-between;
                             font-size: .7rem; margin-top: 4px;
                             font-weight: 600; }
  .funding-current .funding-progress-labels { color: #2C4A68; }
  .funding-future  .funding-progress-labels { color: var(--text-light); }

  /* ── Inline donation/embed iframe ───────────────────────────────────────── */
  .funding-embed { width: 100%; margin-top: 14px; border: none;
                   border-radius: 8px; background: #fff;
                   min-height: 240px; }

  /* ── Newsletter banner (after engagement threshold) ─────────────────────── */
  #newsletter-banner { display: none; max-width: 1200px; margin: 30px auto 0;
                       background: #fff; border-radius: 12px; padding: 28px;
                       box-shadow: 0 4px 20px rgba(0,0,0,.08);
                       border-top: 4px solid var(--tenki-blue);
                       position: relative; }
  #newsletter-banner.show { display: block; animation: bannerSlide .5s ease-out; }
  @keyframes bannerSlide { from { opacity: 0; transform: translateY(20px); }
                            to   { opacity: 1; transform: translateY(0); } }
  #newsletter-banner h3 { margin: 0 0 8px; font-size: 1.2rem; color: var(--tenki-black);
                          text-transform: uppercase; letter-spacing: .5px; }
  #newsletter-banner p { margin: 0 0 14px; color: var(--text-light);
                         font-size: .95rem; line-height: 1.5; }
  #newsletter-close { position: absolute; top: 10px; right: 14px;
                      background: none; border: none; cursor: pointer;
                      font-size: 18px; color: #999; line-height: 1; padding: 4px 8px; }
  #newsletter-close:hover { color: var(--tenki-black); }

  /* Honor user system setting — turn off the bouncy/parallax transitions */
  @media (prefers-reduced-motion: reduce) {
    .pillar-card, .pillar-card:hover, .icon-wrapper, .funding-box,
    .pillar-chevron, .pillar-expanded-drawer, .funding-icon {
      transition: none !important; transform: none !important; animation: none !important;
    }
    html { scroll-behavior: auto !important; }
  }
  html { scroll-behavior: smooth; } /* used by deep-link + scroll-into-view */

  @media (max-width: 900px) { .funding-container { flex-direction: column; } }
  @media (max-width: 768px) {
    body { padding: 10px; }
    .infographic-container { padding: 25px 15px; }
    .infographic-header { margin-bottom: 35px; }
    .infographic-header h2 { font-size: 1.8rem; }
    .infographic-header p  { font-size: 1rem; }
    .pillar-card  { padding: 20px; }
    .pillar-title { font-size: 1.25rem; }
    .card-header-flex { height: auto; flex-direction: row;
                        justify-content: space-between; margin-bottom: 15px; }
    .status-badge { max-width: 100%; }
    .icon-wrapper { width: 65px; height: 65px; font-size: 30px; }
    .funding-box  { flex-direction: column; text-align: center; padding: 20px; gap: 10px; }
    .split-list   { grid-template-columns: 1fr; }
  }

/* ─── timeline: timeline/timeline.html (sha=373132d) ─── */
:root {
    --tenki-blue: #005A9C;
    --tenki-blue-light: #eef5fb;
    --tenki-green: #008C45;
    --tenki-green-light: #e8f5e9;
    --tenki-black: #1A1A1A;
    --tenki-white: #FFFFFF;
    /* Tenki SOS (white pillar) — uses charcoal as the actual stroke color
       since pure white would be invisible on the white timeline background */
    --tenki-white-stroke: #2A2A2A;
    --tenki-white-light:  #FAF6EE;
    --tenki-white-border: #C0C5CC;
    --bg-light: #F8F9FA;
    --text-dark: #222222;
    --text-light: #555555;
    --timeline-line: #E2E8F0;

    --phase-accomplished-bg: #e8f5e9;
    --phase-accomplished-text: #2e7d32;
    --phase-planning-bg: #e3f2fd;
    --phase-planning-text: #1565c0;
    --phase-execution-bg: #fff8e1;
    --phase-execution-text: #f57f17;
    --phase-future-bg: #f5f5f5;
    --phase-future-text: #616161;
  }

  body {
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    background-color: var(--tenki-white);
    margin: 0;
    padding: 20px;
    box-sizing: border-box;
  }

  *, *::before, *::after { box-sizing: inherit; }

  .dashboard-container {
    max-width: 1200px;
    margin: 30px auto;
    background-color: var(--bg-light);
    border-radius: 16px;
    padding: 40px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.03);
    display: flex;
    flex-direction: column;
    position: relative;
    z-index: 10;
    /* Create our own stacking context so siblings of the embedding
       Squarespace section cannot paint into our content area.  The
       mobile @media block below also sets `isolation: isolate` for
       its own reasons (immersive mode + Maya-quote overlap fix
       documented at lines ~877-895).  Lifting it to the base rule
       makes the same defense apply on desktop, where it was missing
       and the social section's masked-bg was painting over the
       bottom of the timeline. */
    isolation: isolate;
    /* Option C — desktop scroll snap.  When the user scrolls down the
       homepage and the dashboard comes into view, the page snaps so the
       whole section fills the viewport.  `proximity` is gentle — won't
       trap the user; they can scroll past freely.  Pair with the
       compressed-fit sizing below so the snapped view actually shows
       the whole timeline + map without overflow. */
    scroll-snap-align: start;
    scroll-margin-top: 0;
  }
  /* Desktop sizing — split into two media queries.  We suggest
     viewport-fit on tall screens but DON'T enforce it on shorter
     ones, where the previous `max-height: calc(100vh - 60px)` was
     crushing the timeline events down to ~120 px (user-reported
     map-overlapping-timeline on common 800-900 px laptop viewports). */
  @media (min-width: 769px) {
    .dashboard-container {
      /* Wider envelope on desktop so the timeline + map both have
         breathing room.  Was 1200 — user-reported the section felt
         cramped on common laptop screens.  Now scales up to 1600 px
         (or 96vw on ultra-wide monitors, capping the line length so
         it doesn't read as a strip across the screen). */
      max-width: min(1600px, 96vw);
      min-height: calc(100vh - 60px);
      margin: 16px auto;       /* was 30 — pulls section up */
      padding: 14px 32px 18px; /* was 24/32/24 — top trimmed to feed
                                  ~30 px down into map/cards/sidebar */
    }

    /* ── Desktop timeline cards: always full height, NO hover lift ──
       2026-05-28 redesign: every card shows its full content inline
       at all times.  Map is sized smaller (clamp 200/28vh/320) so
       cards have room below it without any "come forward on hover"
       gymnastics — text never reflows, never scrunches, never
       jitters.  Click-to-lock the .active / .focused state still
       paints a subtle outline so the user sees which card the
       sidebar is currently describing. */
    .timeline-block {
      position: relative;
      z-index: 5;
      /* Block sizes to its card (which is now auto-height with the
         chip-only layout).  Without align-self:flex-start the block
         stretches to the tallest sibling in the row — left a 100-px
         band of empty card below the content. */
      align-self: flex-start;
    }
    .timeline-card  {
      /* No max-height clamp — card grows to fit its content.  Box-
         shadow + transform transitions kept only so the .active
         outline below animates cleanly on click. */
      transition: box-shadow .25s ease;
      position: relative;        /* anchor the absolute-positioned share-btn (see below) */
    }

    /* ── Compact location chip (was a wide button at the bottom of the
       card) ─────────────────────────────────────────────────────────
       In detailed view the sidebar already shows the region prominently
       under the title, so a second region row at the bottom of the card
       wastes vertical space.  Re-style .click-indicator as a small
       icon-only chip pinned to the top-LEFT of the card.  Tap target
       stays 28 px so it's still hittable.  The full region name still
       lives in the chip's title="" attribute for hover discovery. */
    .timeline-card .click-indicator {
      position: absolute;
      top: 8px; left: 8px;
      width: 28px; height: 28px;
      min-width: 0;
      padding: 0 !important;
      margin: 0 !important;
      font-size: 0.95rem !important;
      background: rgba(0,0,0,0.05) !important;
      color: var(--text-light) !important;
      border-radius: 50%;
      border: 1px solid rgba(0,0,0,0.10);
      display: inline-flex;
      align-items: center;
      justify-content: center;
      /* Hide the region text — only the 📍 icon shows.  Title attribute
         (added by trackIconHTML render path) is the hover tooltip with
         the full region name. */
      overflow: hidden;
      text-indent: -9999px;
      white-space: nowrap;
    }
    .timeline-card .click-indicator::before {
      content: '📍';
      position: absolute;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      text-indent: 0;
      font-size: 0.95rem;
      line-height: 1;
    }
    .timeline-card .click-indicator:hover {
      background: rgba(0, 90, 156, 0.12) !important;
      border-color: rgba(0, 90, 156, 0.30);
    }
    /* Make room for the corner chip — title block gets a left padding
       so the icon doesn't overlap the title text. */
    .timeline-card > .time-marker,
    .timeline-card > .duration-span,
    .timeline-card h3 {
      padding-left: 36px;
    }

    /* ── Visual line-clamps (backstop for runtime truncate) ──────────
       Defense-in-depth pair with JS truncate() — even if a field
       slips past the runtime cap, the visual clamp keeps card heights
       predictable.  Mirror these line counts to TENKI_CHAR_LIMITS in
       the JS (the source-of-truth char counts). */
    .timeline-card h3 {
      display: -webkit-box;
      -webkit-line-clamp: 2;            /* ≈ 48 chars title */
      -webkit-box-orient: vertical;
      overflow: hidden;
      word-break: break-word;
    }
    /* The in-flow card no longer renders a body <p> (description was
       merged into the sidebar text).  Any rogue <p> that does sneak
       in still gets clamped to a single line as a safety net. */
    .timeline-card p {
      display: -webkit-box;
      -webkit-line-clamp: 1;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
    .timeline-card .click-indicator {
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    /* Sidebar full-detail paragraph (merged desc + sidebarText).
       Switched from -webkit-line-clamp (which sometimes collapses
       the box to zero height under certain flex / margin combos —
       user was reporting "I don't see the body text in the sidebar")
       to a plain max-height + overflow.  The runtime truncate cap
       (TENKI_CHAR_LIMITS.sidebar = 460) is the actual content cap;
       this is just a visual safety net. */
    .map-details-sidebar .details-content-view p,
    .map-details-sidebar #dynamic-sidebar-view  p {
      margin: 0 0 8px 0;
      line-height: 1.5;
      font-size: 0.92rem;
      color: var(--text-light);
      overflow: hidden;
    }
    /* The sidebar can host two paragraphs after the desc + sidebarText
       merge.  Tighter margin between them so the body still reads as
       a single block. */
    .map-details-sidebar #dynamic-sidebar-view p + p {
      margin-top: 0;
    }

    /* Share button: pinned to top-right of the card (timeline AND
       sidebar both).  Previously rendered as a white pill which read
       as an "ugly box."  Now: NO background, NO border, NO shadow by
       default — just the icon floating in place.  On hover, a soft
       translucent grey wash appears so the user knows it's tappable.
       Icon-only at all times (label hidden) so the affordance is
       compact and never overlaps adjacent content. */
    .timeline-card > .share-btn,
    .timeline-card .card-badge-row > .share-btn,
    .details-content-view > .share-btn,
    .details-content-view .share-btn {
      position: absolute;
      top: 6px;
      right: 6px;
      background: transparent !important;
      border: 0 !important;
      box-shadow: none !important;
      padding: 6px !important;
      z-index: 4;
      opacity: 0.55;
      transition: opacity .15s ease, background .15s ease;
    }
    .timeline-card > .share-btn span,
    .timeline-card .card-badge-row > .share-btn span,
    .details-content-view .share-btn span {
      display: none;     /* icon-only — keeps the share affordance compact */
    }
    .timeline-card > .share-btn:hover,
    .timeline-card .card-badge-row > .share-btn:hover,
    .details-content-view .share-btn:hover {
      background: rgba(0, 90, 156, 0.10) !important;
      opacity: 1;
    }
    .timeline-card > .share-btn:hover svg,
    .timeline-card .card-badge-row > .share-btn:hover svg,
    .details-content-view .share-btn:hover svg {
      fill: var(--tenki-blue) !important;
      color: var(--tenki-blue) !important;
    }
    /* Sidebar share-btn needs its container relatively positioned to
       anchor the absolute share-btn correctly. */
    .details-content-view { position: relative; }
    /* Bottom-fade ::after removed — cards are full-height now, so
       there's nothing hidden underneath to hint at. */
    .timeline-card::after { display: none !important; }

    /* Active / focused (click-locked) — soft ring + slightly stronger
       shadow so the user can tell which card the sidebar is showing.
       No transform / translateY, no z-index promotion, no max-height
       change — the layout never shifts. */
    .timeline-block.active .timeline-card,
    .timeline-block.focused .timeline-card {
      box-shadow: 0 8px 22px rgba(0,0,0,0.10), 0 0 0 1px rgba(0,90,156,0.08);
    }
    .timeline-block.active .timeline-card {
      outline: 2px solid rgba(0, 90, 156, 0.4);
      outline-offset: -2px;
    }
  }
  /* Only activate scroll-snap + viewport-clip on TALL desktop screens
     where the dashboard actually fits.  On 15"-16" MacBook viewports
     (~800-900 px) it doesn't, so we let the section grow past the
     viewport and the user scrolls within it — every event reachable,
     no overlap with the map. */
  @media (min-width: 769px) and (min-height: 900px) {
    html { scroll-snap-type: y proximity; }
    .dashboard-container {
      max-height: calc(100vh - 60px);
      overflow: hidden;
    }
    /* In simplified view the active focal card scales up and needs to
       paint OVER the map below it.  The viewport-locked overflow:hidden
       above clips that paint.  Loosen it when the dashboard is in
       simplified mode so the card escapes downward correctly. */
    .dashboard-container.simplified-view {
      overflow: visible;
    }
  }

  .dashboard-header {
    text-align: center;
    margin-bottom: 8px;
    order: 1;
    position: relative;
    z-index: 10;
  }

  .dashboard-header h2 {
    color: var(--tenki-black);
    font-size: 1.5rem;            /* tightened so section heading doesn't dominate the carousel below */
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin: 0 0 2px 0;
    font-weight: 800;
    line-height: 1.1;
  }

  .dashboard-header p {
    color: var(--text-light);
    font-size: 0.9rem;
    max-width: 900px;             /* wider so the single-line lede stays one line on most viewports */
    margin: 0 auto;
    line-height: 1.35;
    white-space: nowrap;          /* hard-force one line; the line is short enough */
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* On narrow viewports allow the lede to wrap rather than ellipsis-clip */
  @media (max-width: 720px) {
    .dashboard-header p {
      white-space: normal;
      font-size: 0.85rem;
    }
  }

  .system-state-container {
    text-align: center;
    padding: 60px 20px;
    order: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 200px;
  }

  .spinner {
    width: 40px;
    height: 40px;
    border: 4px solid rgba(0, 90, 156, 0.1);
    border-left-color: var(--tenki-blue);
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
  }

  @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }

  .state-message { font-weight: 600; color: var(--text-light); font-size: 1.1rem; }
  .error-state { color: #d32f2f; }
  .error-icon { font-size: 2rem; margin-bottom: 10px; }
  .diagnostic-info { display: block; font-size: 0.85rem; color: #555; margin-top: 10px; font-weight: normal; background: #f5f5f5; padding: 10px; border-radius: 6px;}

  /* Control Bar */
  .dashboard-controls {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 15px;
    margin-bottom: 14px;          /* was 30 — tighter to feed more room down */
    order: 3;
    position: relative;
    width: 100%;
    z-index: 150;
  }

  .filters-scroll-wrapper {
    position: relative;
    width: 100%;
    overflow: visible;
  }

  .timeline-filters {
    display: flex;
    justify-content: center;
    gap: 12px;
    flex-wrap: wrap;
    width: 100%;
  }

  .dropdown-wrapper { position: relative; display: inline-block; }

  .filter-btn {
    background-color: var(--tenki-white); border: 2px solid var(--timeline-line);
    color: var(--text-light); padding: 8px 20px; border-radius: 30px;
    font-family: inherit; font-size: 0.85rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.5px; cursor: pointer;
    transition: all 0.2s ease;
    height: 38px;
    line-height: 1.2;
    box-sizing: border-box;
  }
  .filter-btn:hover { border-color: #CCCCCC; color: var(--text-dark); }

  /* "Expand all years" — an action-button variant.  Sits in the chip
     row but reads as a CTA (filled blue, no dropdown chevron) so the
     user spots it as the way out of a heavily-collapsed timeline. */
  .filter-btn.expand-years-btn {
    background: var(--tenki-blue);
    border-color: var(--tenki-blue);
    color: #fff;
  }
  .filter-btn.expand-years-btn:hover {
    background: #00477a;
    border-color: #00477a;
    color: #fff;
  }
  .filter-btn.expand-years-btn[hidden] { display: none !important; }

  /* ── Track-color resting state for filter dropdowns ──────────────────
     Even when a filter chip isn't the active one, tint its border + text
     in its track color so the legend (Clinic = blue, Literacy = green,
     Engine = black, Tenki Org = neutral) is always visible.  Active
     state below switches to the solid-color flip. */
  .dropdown-wrapper[data-filter="Clinic Track"]    .dropdown-toggle-btn:not(.active) {
    border-color: rgba(0,90,156,0.55);
    color: var(--tenki-blue);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23005A9C'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="Clinic Track"]    .dropdown-toggle-btn:not(.active):hover {
    background-color: var(--tenki-blue-light);
    border-color: var(--tenki-blue);
  }
  .dropdown-wrapper[data-filter="Literacy Track"]  .dropdown-toggle-btn:not(.active) {
    border-color: rgba(0,140,69,0.55);
    color: var(--tenki-green);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23008C45'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="Literacy Track"]  .dropdown-toggle-btn:not(.active):hover {
    background-color: var(--tenki-green-light);
    border-color: var(--tenki-green);
  }
  .dropdown-wrapper[data-filter="The Engine"]      .dropdown-toggle-btn:not(.active) {
    border-color: rgba(26,26,26,0.55);
    color: var(--tenki-black);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%231A1A1A'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="The Engine"]      .dropdown-toggle-btn:not(.active):hover {
    background-color: #f1f1f1;
    border-color: var(--tenki-black);
  }
  /* Tenki Organization = the "white" track in event data.  Use a soft
     neutral with dashed border to echo the dashed top-border treatment
     on white-track cards. */
  .dropdown-wrapper[data-filter="Tenki Organization"] .dropdown-toggle-btn:not(.active) {
    border-color: var(--tenki-white-stroke);
    border-style: dashed;
    color: var(--text-dark);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23999999'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="Tenki Organization"] .dropdown-toggle-btn:not(.active):hover {
    background-color: var(--tenki-white-light);
  }

  .dropdown-toggle-btn {
    padding-right: 32px !important;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23555555'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
  }

  .dropdown-menu-options {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    background: var(--tenki-white) !important;
    border: 1px solid var(--timeline-line);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.2) !important;
    min-width: 240px;
    padding: 6px 0;
    max-height: 260px;
    overflow-y: auto;
    z-index: 2010;
  }

  .dropdown-menu-options.open { display: block; }

  .dropdown-opt-item {
    padding: 10px 20px; font-size: 0.85rem; font-weight: 600; color: var(--text-dark);
    cursor: pointer; text-transform: none; transition: background-color 0.15s ease, color 0.15s ease;
  }
  .dropdown-opt-item:hover { background-color: var(--bg-light); color: var(--tenki-blue); }

  .filter-btn.active[data-filter="all"] { background-color: var(--tenki-black); border-color: var(--tenki-black); color: var(--tenki-white); }

  .dropdown-wrapper[data-filter="Clinic Track"] .dropdown-toggle-btn.active {
    background-color: var(--tenki-blue); border-color: var(--tenki-blue); color: var(--tenki-white);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="Literacy Track"] .dropdown-toggle-btn.active {
    background-color: var(--tenki-green); border-color: var(--tenki-green); color: var(--tenki-white);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }
  .dropdown-wrapper[data-filter="The Engine"] .dropdown-toggle-btn.active,
  .dropdown-wrapper[data-filter="Tenki Organization"] .dropdown-toggle-btn.active {
    background-color: var(--tenki-black); border-color: var(--tenki-black); color: var(--tenki-white);
    background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
  }

  /* ── Depth toggle (Simplified ⇆ Full Story) — MOBILE ONLY ─────────────
     Desktop uses the repurposed Map Expand toggle (see .map-expand-toggle
     below) as the depth switch.  On mobile that toggle is hidden, so we
     surface a compact pill in the dashboard-header instead so phone
     visitors can also collapse the heavy detail to a glance view.
     Persists via localStorage shared with the desktop toggle. */
  .dashboard-header { position: relative; }
  .depth-toggle {
    display: none;   /* desktop default — only shows on mobile via media query */
    align-items: center;
    gap: 6px;
    padding: 7px 14px 7px 11px;
    background: var(--tenki-white);
    color: var(--tenki-black);
    border: 1px solid rgba(0,0,0,0.08);
    border-radius: 22px;
    font: 700 0.78rem/1 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    letter-spacing: 0.4px;
    cursor: pointer;
    box-shadow: 0 4px 14px rgba(0,0,0,0.08);
    transition: transform .15s ease, background .15s ease, box-shadow .15s ease;
    z-index: 6;
  }
  .depth-toggle:hover {
    transform: translateY(-1px);
    background: var(--bg-light);
    box-shadow: 0 6px 18px rgba(0,0,0,0.12);
  }
  .depth-toggle-icon { font-size: 0.95rem; line-height: 1; }
  /* Hint badge retired — the Map Expand button (desktop) and the
     header pill (mobile) both carry an obvious label so a separate
     pulsing hint just added clutter. */
  .depth-hint { display: none !important; }

  @media (max-width: 768px) {
    /* Show the depth-toggle as a pill at the top-right of the header
       on phones, with a slightly tighter footprint. */
    .depth-toggle {
      display: inline-flex;
      position: absolute;
      top: 4px;
      right: 12px;
      padding: 6px 12px 6px 10px;
      font-size: 0.72rem;
    }
    /* Headline + lede get a touch of right-padding so a long title
       can't crash into the toggle. */
    .dashboard-header h2,
    .dashboard-header p { padding-right: 132px; }
  }

  /* ── Simplified-view state ────────────────────────────────────────
     Hides the sidebar pane + heavy details from each card, leaving:
       - Year strip + month ticks + spine + node circles
       - Each card: title + date + duration line ONLY (no badges,
         no description, no CTAs, no share button, no map-focus
         button, no mobile-inline-details)
       - Map fills the full width of the map-interface-grid */
  .dashboard-container.simplified-view .map-interface-grid {
    grid-template-columns: 1fr !important;
  }
  .dashboard-container.simplified-view .map-details-sidebar {
    display: none !important;
  }
  /* Cards collapse to compact strips — title + date + nothing else.
     The bottom-fade overlay also goes since there's no hidden content
     to hint at. */
  .dashboard-container.simplified-view .timeline-card {
    max-height: 88px !important;
    padding: 12px 14px !important;
  }
  .dashboard-container.simplified-view .timeline-card::after {
    display: none !important;
  }
  .dashboard-container.simplified-view .timeline-card .card-badge-row,
  .dashboard-container.simplified-view .timeline-card .share-btn,
  .dashboard-container.simplified-view .timeline-card .duration-span-row,
  .dashboard-container.simplified-view .timeline-card p,
  .dashboard-container.simplified-view .timeline-card .click-indicator,
  .dashboard-container.simplified-view .timeline-card .mobile-inline-details {
    display: none !important;
  }
  .dashboard-container.simplified-view .timeline-card h3 {
    font-size: 1.05rem !important;
    line-height: 1.22 !important;
    margin: 0 0 4px 0 !important;
    font-weight: 800 !important;
    display: flex !important;
    align-items: flex-start !important;
    gap: 4px !important;
  }
  .dashboard-container.simplified-view .timeline-card .time-marker {
    font-size: 0.7rem !important;
    color: var(--text-light) !important;
    letter-spacing: 0.4px !important;
  }
  /* Hover-grow removed in simplified mode too (2026-05-28) — see the
     desktop block above for rationale.  Click to activate is the only
     way to expand a card now; hover does nothing visually. */
  /* When a card becomes active in simplified mode, switch BACK to
     full content for THAT card so the user can read it without
     toggling the whole mode.  Other cards stay compact. */
  .dashboard-container.simplified-view .timeline-block.active .timeline-card,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card {
    max-height: 640px !important;
    padding: 16px !important;
  }
  .dashboard-container.simplified-view .timeline-block.active .timeline-card .card-badge-row,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card .card-badge-row,
  .dashboard-container.simplified-view .timeline-block.active .timeline-card .share-btn,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card .share-btn,
  .dashboard-container.simplified-view .timeline-block.active .timeline-card p,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card p,
  .dashboard-container.simplified-view .timeline-block.active .timeline-card .click-indicator,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card .click-indicator {
    display: inherit !important;
  }
  .dashboard-container.simplified-view .timeline-block.active .timeline-card h3,
  .dashboard-container.simplified-view .timeline-block.focused .timeline-card h3 {
    font-size: 1.1rem !important;
  }

  /* Structural Hide Classes */
  .filtered-out { display: none !important; }
  /* Bare `.collapsed-by-year` (used on things like year-divider) still
     hides outright.  But .timeline-block.collapsed-by-year (below) is
     overridden to a "shrunk-to-just-the-node" state so the user can
     SEE the number of events in the collapsed year at a glance. */
  .collapsed-by-year { display: none !important; }
  .year-badge.dimmed { opacity: 0.5; background-color: var(--text-light) !important; color: #FFF; box-shadow: inset 0 2px 4px rgba(0,0,0,0.2) !important; }
  /* Third click on year badge: fully hide even the collapsed chips */
  .year-badge.super-dimmed { opacity: 0.25 !important; background-color: #777 !important; color: #FFF; box-shadow: inset 0 2px 4px rgba(0,0,0,0.25) !important; }
  .timeline-block.super-hidden-by-year { display: none !important; }

  /* ── Year-collapsed block: shrink to just the node circle ─────────
     User wanted to STILL see the number of activities Tenki has done
     in a collapsed year — so each collapsed event renders as a small
     dot on the spine instead of vanishing.  Multiple dots in a row
     read as "Tenki had N events in this year" without taking up the
     full card real estate.
     This rule overrides the bare `.collapsed-by-year` hide above
     because the `.timeline-block.collapsed-by-year` selector has
     higher specificity (two classes vs one).  Cards, duration bars,
     stems, connectors, and labels are all hidden — only the node
     remains.  The block itself shrinks to ~36 px so a year's worth
     of events shows as a tight cluster of small dots. */
  .timeline-block.collapsed-by-year {
    display: flex !important;
    flex: 0 0 auto !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
    padding: 0 !important;
    margin: 0 !important;
    align-items: center !important;
    justify-content: center !important;
    overflow: visible !important;
    cursor: default !important;
    opacity: 0.6;
    transition: opacity .15s ease, transform .15s ease;
  }
  .timeline-block.collapsed-by-year:hover {
    opacity: 1;
    transform: translateY(-1px);
  }
  /* Hide everything inside the collapsed block EXCEPT the node circle */
  .timeline-block.collapsed-by-year .timeline-card,
  .timeline-block.collapsed-by-year .collapsed-label,
  .timeline-block.collapsed-by-year .expanded-title-card,
  .timeline-block.collapsed-by-year .duration-line-ext,
  .timeline-block.collapsed-by-year .duration-stem,
  .timeline-block.collapsed-by-year .duration-dot,
  .timeline-block.collapsed-by-year .node-card-connector {
    display: none !important;
  }
  /* Pin the collapsed node ON THE SPINE rather than letting it sit in
     normal flow (which was dropping it to the block's vertical centre,
     well below the spine — user reported "the circle is dropping off
     the timeline").  Same y-coordinate as a regular node (top: -33px)
     so the dot lines up with the spine across collapsed + expanded
     years; horizontally centred in the 36-px block. */
  .timeline-block.collapsed-by-year .timeline-node {
    position: absolute !important;
    top: -33px !important;
    left: 50% !important;
    transform: translateX(-50%) !important;
    width: 10px !important;
    height: 10px !important;
    border-width: 2px !important;
    opacity: 0.9 !important;
  }
  /* Active-state transform on the node ramps from translateX(-50%)
     to also include scale(1.35) — defined further down — but we don't
     need that here; the hover lift on the collapsed BLOCK below is
     enough feedback. */
  .timeline-block.collapsed-by-year .timeline-node::before,
  .timeline-block.collapsed-by-year .timeline-node::after {
    display: none !important;
  }
  /* Mobile (vertical timeline): collapsed blocks become a thin
     row of dots stacked vertically.  The 36-px block shrinks to
     ~26 px tall and the dot sits centered. */
  @media (max-width: 768px) {
    .timeline-block.collapsed-by-year {
      width: auto !important;
      min-width: 0 !important;
      max-width: none !important;
      min-height: 26px !important;
      height: 26px !important;
    }
  }

  /* Map & Sidebar Desktop Environments */
  .map-interface-grid {
    display: grid;
    grid-template-columns: 1fr 460px;   /* was 420 — sidebar gets +40 more for the merged body */
    gap: 30px;
    height: 500px;
    order: 5;
    position: relative;
    z-index: 10;
  }
  /* Desktop compressed-fit — when the dashboard is locked to viewport
     height, the map + timeline below it need to SHARE that height
     instead of map taking a fixed 500 px + timeline overflowing.
     Map gets ~45 vh, timeline gets the remaining flex space.  Both
     scroll internally if their content exceeds the allotted height. */
  @media (min-width: 769px) {
    .map-interface-grid {
      /* 2026-05-28 (round 4): cards collapsed to ~130 px each thanks
         to the chip-only redesign + auto-height + flex-start.  The
         freed vertical room flows up into the map envelope.  Was
         clamp(220, 33vh, 380); now clamp(300, 44vh, 520) so the map
         feels generous on a laptop viewport without ever pushing the
         cards into a forced scroll.
         When .dashboard-container.map-expanded is set (via the
         Simplified-view button), the map grows to fill — see the
         .map-expanded rules below. */
      height: clamp(300px, 44vh, 520px);
      flex-shrink: 0;
      position: relative;       /* anchor the expand-toggle button below */
      transition: height .35s ease;
    }
    /* When map is expanded (scroll-lock PRESERVED — keeps the
       app-feel the user wanted):
       1. Map-interface-grid loses its 380 px sidebar column — map
          fills full width.  Sidebar card hidden.
       2. Timeline-outer-wrapper shrinks to ~110 px (year strip + a
          single row of title-only chips below it).
       3. The full event CARDS are hidden but each block still shows
          its .collapsed-label — a small track-colored chip with the
          event title — same mobile-style bubble the user asked for.
       4. Map-interface-grid expands to fill the remaining height
          inside the existing scroll-locked container — `flex: 1` so
          the flex parent allocates the leftover space to it.  No
          scroll-lock override needed.
       5. Floating nav stays visible + lifted above the expanded
          map so the user can use ← / → arrows + All Tracks
          dropdown while browsing pins. */
    .dashboard-container.map-expanded .map-interface-grid {
      grid-template-columns: 1fr !important;
      height: auto !important;
      max-height: none !important;
      flex: 1 1 auto !important;          /* take whatever the dashboard envelope has left */
      min-height: 380px !important;       /* never crush below useful */
    }
    .dashboard-container.map-expanded .map-details-sidebar { display: none !important; }
    .dashboard-container.map-expanded .timeline-outer-wrapper {
      max-height: 150px !important;
      min-height: 150px !important;
      flex: 0 0 150px !important;
      overflow: visible !important;       /* let scaled-up active card escape vertically */
      margin-bottom: 8px !important;
      transition: max-height .35s ease, flex-basis .35s ease;
      /* Sit ABOVE the map-interface-grid (z:10) so the active card
         growing past its row paints in front of the map below, not
         behind it.  The growing card also has its own z:30 below. */
      position: relative !important;
      z-index: 30 !important;
    }
    .dashboard-container.map-expanded .timeline-scroll-wrapper,
    .dashboard-container.map-expanded .timeline-track {
      overflow-y: visible !important;     /* don't clip the focal scale */
    }
    /* The inner scroll-wrapper has 25 px vertical padding + 40 px
       margin-bottom in its default styles — fine in the normal
       desktop layout (the spine + duration bars need that room) but
       in expanded mode it caused the title cards to overflow the
       strip and visually touch the map.  Zero out the vertical
       padding + margin so the strip envelope is honest about its
       size. */
    .dashboard-container.map-expanded .timeline-scroll-wrapper {
      padding-top: 0 !important;
      padding-bottom: 0 !important;
      margin-bottom: 0 !important;
    }
    /* 2026-05-28 (round 8): user preferred the prior design — title
       cards take the place of the timeline circles on the spine row
       rather than the spine being a separate rail above the cards.
       So we go back to hiding spine + circles + duration scaffolding;
       the .expanded-title-card IS the visual element on the row. */
    .dashboard-container.map-expanded .duration-line-ext,
    .dashboard-container.map-expanded .duration-stem,
    .dashboard-container.map-expanded .duration-dot,
    .dashboard-container.map-expanded .tl-month-tick,
    .dashboard-container.map-expanded .timeline-block .timeline-node,
    .dashboard-container.map-expanded .timeline-card,
    .dashboard-container.map-expanded .timeline-track::before {
      display: none !important;
    }
    /* The block itself stays visible — but at title-chip size, NOT
       290 px wide.  Track-color tinted chip with the event title.
       Same vocabulary the mobile UI already uses (mobile shows these
       chips when years collapse), promoted to desktop in this state.  */
    .dashboard-container.map-expanded .timeline-block {
      flex: 0 0 auto !important;
      width: auto !important;
      min-width: 0 !important;
      padding: 0 !important;
    }
    /* Hide the old single-line collapsed-label in expanded mode —
       the new richer `.expanded-title-card` replaces it.  (Mobile
       collapsed-by-year UX still uses .collapsed-label so we don't
       repurpose it globally.) */
    .dashboard-container.map-expanded .timeline-block .collapsed-label {
      display: none !important;
    }
    .dashboard-container.map-expanded .timeline-track {
      padding-top: 12px !important;       /* tight strip — cards ARE the row, no separate spine above */
      gap: 14px !important;               /* a bit wider than the original 12 so focal scale has room */
      align-items: center !important;     /* cards sit on the row, centered vertically */
      /* 2026-05-28 (round 6): simplified view now reads as a CAROUSEL.
         Drop the "spread to full width" overrides — items keep their
         intrinsic widths, the row overflows the wrapper, and the
         existing #slide-left / #slide-right buttons (defined at the
         dashboard root) scroll the row by ~350 px per click.  Mouse
         wheel + drag-to-scroll also work via the existing handlers. */
      min-width: max-content !important;
      width: auto !important;
      justify-content: flex-start !important;
    }
    /* Wrapper: horizontal scroll snap so each event chip lands cleanly
       under the next/prev buttons. */
    .dashboard-container.map-expanded .timeline-scroll-wrapper {
      overflow-x: auto !important;
      overflow-y: hidden !important;       /* contain the lifted-card animations to the strip */
      scroll-snap-type: x mandatory;
      scroll-padding: 0 60px;              /* leave room for the carousel arrows on either edge */
      cursor: grab;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;               /* Firefox */
    }
    .dashboard-container.map-expanded .timeline-scroll-wrapper::-webkit-scrollbar {
      display: none;                       /* Chrome/Safari */
    }
    /* Every scrollable item snaps to the start.  collapsed-year dots
       are tiny — they get a wider 90-px slot in carousel mode so the
       dot has visual breathing room and snap-stops feel even. */
    .dashboard-container.map-expanded .timeline-block.collapsed-by-year {
      flex: 0 0 90px !important;
      max-width: 90px !important;
      scroll-snap-align: center;
    }
    .dashboard-container.map-expanded .timeline-block:not(.collapsed-by-year) {
      scroll-snap-align: start;
    }
    .dashboard-container.map-expanded .year-divider {
      flex: 0 0 80px !important;       /* wider than the default 60 so the bubble has its own slot */
      scroll-snap-align: start;
      /* Visual breathing room on both sides — the year bubble itself
         is ~80 px wide so this keeps it from kissing the cards. */
      margin: 0 10px !important;
      position: relative;
    }
    /* Year bubble z-index lifted ABOVE the cards so it's never
       obscured when the next chip sits right against the divider.
       The bubble itself is `position: absolute; top: -37px` inside
       the divider — so it floats above the spine and needs the
       z-index to stay visually on top. */
    .dashboard-container.map-expanded .year-badge {
      z-index: 8 !important;
      /* In simplified view there's no spine above to dock to — drop
         the bubble down so it sits at the same vertical row as the
         cards rather than floating up near the dashboard header. */
      top: 50% !important;
      transform: translateY(-50%) !important;
    }
    /* ── Carousel focal-card effect ───────────────────────────────────
       The "in focus" card (.active / .focused) scales UP and the
       neighbours sit at their natural size — gives the row the
       cover-flow feel the user asked for.  The active card centres
       under the map via the existing smoothScrollToCard() helper
       (already wired). */
    .dashboard-container.map-expanded .expanded-title-card {
      transform: scale(0.96);
      transform-origin: center;
      opacity: 0.85;
      transition: transform .35s ease, opacity .35s ease, box-shadow .35s ease, border-color .35s ease;
    }
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card {
      transform: scale(1.08);
      opacity: 1;
      z-index: 200 !important;            /* well above map (z:10) + neighbour cards */
      position: relative;
      box-shadow: 0 14px 32px rgba(0,0,0,0.18) !important;
      max-height: none !important;
      overflow: visible !important;
      /* Widen the focal card so the merged body has room to read.
         max-width relaxed from the 280 default to 360 for the active
         card — fits ~6 lines of body comfortably. */
      max-width: 360px !important;
      min-width: 280px !important;
    }
    /* Focused-card title: drop the line-clamp ENTIRELY so the user
       always reads the full title (no ellipsis, no hidden text). */
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card .etc-title,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card .etc-title {
      display: block !important;
      -webkit-line-clamp: none !important;
      -webkit-box-orient: initial !important;
      overflow: visible !important;
      max-height: none !important;
      white-space: normal !important;
    }
    /* ── Focused-card body (merged desc + sidebarText) ────────────────
       The detail body lives ON the focal card now — users no longer have
       to flip to detailed view to read what an event was actually about.
       Hidden by default (carousel neighbours stay compact); revealed
       when .active or .focused. */
    .dashboard-container.map-expanded .expanded-title-card .etc-body {
      display: none;
    }
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card .etc-body,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card .etc-body {
      display: block;
      margin-top: 8px;
      padding-top: 8px;
      border-top: 1px solid rgba(0,0,0,0.08);
      max-height: 200px;
      overflow-y: auto;
    }
    .dashboard-container.map-expanded .expanded-title-card .etc-body p {
      margin: 0 0 6px 0;
      font-size: 0.78rem;
      line-height: 1.45;
      color: var(--text-light);
    }
    .dashboard-container.map-expanded .expanded-title-card .etc-body p:last-child {
      margin-bottom: 0;
    }
    /* Lift the active block + outer-wrapper to a stacking layer that
       sits ABOVE the map-interface-grid so the scaled card paints
       cleanly over the map (the screenshot showed the bottom of TBOW
       being clipped behind the pins).  isolation:isolate creates a
       fresh stacking context so the elevated z-index here doesn't
       have to fight ancestor contexts. */
    .dashboard-container.map-expanded .timeline-block.active,
    .dashboard-container.map-expanded .timeline-block.focused {
      z-index: 200 !important;
      position: relative !important;
      isolation: isolate;
    }
    .dashboard-container.map-expanded .timeline-outer-wrapper {
      z-index: 100 !important;
      isolation: isolate;
    }
    /* Carousel arrows: bump them above the title strip so they sit at
       the edges of the simplified-view band rather than disappearing
       behind the map below.  Same .scroll-btn base styling; just
       reposition for the squeezed envelope. */
    .dashboard-container.map-expanded .scroll-btn {
      display: flex !important;
      top: 70px;                            /* mid-card vertically in the 150-px strip */
      transform: none;
      width: 36px; height: 36px;
      background: rgba(255,255,255,0.96);
      border-color: rgba(0,0,0,0.08);
      box-shadow: 0 4px 14px rgba(0,0,0,0.15);
      z-index: 25;
    }
    .dashboard-container.map-expanded .scroll-btn svg { width: 18px; height: 18px; }
    .dashboard-container.map-expanded .prev-btn { left: 6px; }
    .dashboard-container.map-expanded .next-btn { right: 6px; }
    /* Subtle edge-fade hints so the user sees there's more to scroll
       on either side.  Hidden when the wrapper is scrolled all the
       way to that edge (JS toggles .left-hint-active /
       .right-hint-active classes on the scroll-wrapper). */
    .dashboard-container.map-expanded .timeline-outer-wrapper::before,
    .dashboard-container.map-expanded .timeline-outer-wrapper::after {
      content: '';
      position: absolute;
      top: 0; bottom: 0;
      width: 48px;
      pointer-events: none;
      z-index: 20;
      opacity: 0;
      transition: opacity .2s ease;
    }
    .dashboard-container.map-expanded .timeline-outer-wrapper::before {
      left: 0;
      background: linear-gradient(to right, var(--bg-light), transparent);
    }
    .dashboard-container.map-expanded .timeline-outer-wrapper::after {
      right: 0;
      background: linear-gradient(to left, var(--bg-light), transparent);
    }
    .dashboard-container.map-expanded .timeline-outer-wrapper:has(.timeline-scroll-wrapper.left-hint-active)::before  { opacity: 1; }
    .dashboard-container.map-expanded .timeline-outer-wrapper:has(.timeline-scroll-wrapper.right-hint-active)::after  { opacity: 1; }
    /* Squeeze the empty band between the filter chips and the title
       strip in expanded mode — the original 30 px margin made sense
       when the cards needed a duration spine above them, but in
       expanded mode there's no spine, so the gap was just wasted space. */
    .dashboard-container.map-expanded .dashboard-controls {
      margin-bottom: 10px !important;
    }
    .dashboard-container.map-expanded .dashboard-header {
      margin-bottom: 14px !important;
    }
    /* Suppress any 'system state' container in expanded mode — the
       map IS the visible state at this point. */
    .dashboard-container.map-expanded .system-state-container {
      display: none !important;
    }

    /* ── Expanded title card ──────────────────────────────────────────
       Three-row chip per event, surfacing the hardest-hitting punch
       info for at-a-glance reading while the user explores the map:

         Row 1: phase pill + funding pill (track-tinted)
         Row 2: title (bold, ellipsis-trimmed)
         Row 3: date · region (small + muted)

       Hidden by default (display:none) — only ever rendered when
       the dashboard is in .map-expanded.  Track-color tinted card
       body matches the bubble vocabulary of the mobile
       collapsed-label so the visual language stays consistent. */
    .expanded-title-card { display: none; }
    .dashboard-container.map-expanded .expanded-title-card {
      display: flex !important;
      flex-direction: column;
      gap: 4px;
      min-width: 220px;
      max-width: 280px;
      padding: 8px 12px;
      border-radius: 12px;
      border: 1px solid rgba(0, 0, 0, 0.08);
      background: var(--tenki-white);
      cursor: pointer;
      transition: transform .15s ease, box-shadow .15s ease, background .15s ease;
    }
    .dashboard-container.map-expanded .expanded-title-card:hover {
      /* Hover-lift removed — was causing scrunched text + jitter in
         the title strip.  Just deepen the shadow slightly for a
         passive "hoverable" cue. */
      box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    }
    /* Active / focused only — no hover z-index promotion (matches the
       "no hover lift" rule for the in-flow cards above).  Click to
       select; hover does nothing. */
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card {
      z-index: 50;
    }
    /* ── etc-pills as a uniform mini-table ───────────────────────────
       User feedback: the chips on the title card were "too busy" — each
       badge had its own colour + shape so the eye read it as decoration
       instead of data.  Normalize all three (tag, phase, funding) into
       chips of identical size, shape, and weight so they scan as a
       compact data row.  The TEXT inside still carries the meaning;
       the TAG chip alone keeps its track-colour as a subtle category
       cue (left-most). */
    .etc-pills {
      display: flex; flex-wrap: nowrap; gap: 0; align-items: center;
      min-height: 18px;
      border: 1px solid rgba(0,0,0,0.12);
      border-radius: 4px;
      overflow: hidden;
      background: rgba(0,0,0,0.02);
    }
    .etc-pills .details-tag,
    .etc-pills .phase-badge,
    .etc-pills .funding-badge {
      font-size: 0.58rem !important;
      padding: 3px 6px !important;
      letter-spacing: 0.4px !important;
      border-radius: 0 !important;
      border: 0 !important;
      border-right: 1px solid rgba(0,0,0,0.10) !important;
      background: transparent !important;
      color: var(--text-dark) !important;
      font-weight: 700 !important;
      text-transform: uppercase !important;
      box-shadow: none !important;
      flex: 0 0 auto;
      line-height: 1.2 !important;
    }
    /* Last chip in the row drops its right divider so the table reads
       as bounded by the outer border only. */
    .etc-pills > *:last-child {
      border-right: 0 !important;
    }
    /* The TAG chip (left-most) keeps a subtle track-colour wash so the
       category is glanceable without screaming colour at the user. */
    .etc-pills .details-tag.tag-blue   { background: rgba(0, 90,156, 0.10) !important; color: var(--tenki-blue)  !important; }
    .etc-pills .details-tag.tag-green  { background: rgba(0,140, 69, 0.10) !important; color: var(--tenki-green) !important; }
    .etc-pills .details-tag.tag-black  { background: rgba(26, 26, 26, 0.08) !important; color: var(--tenki-black) !important; }
    .etc-pills .details-tag.tag-white  { background: rgba(192,197,204, 0.18) !important; color: var(--tenki-white-stroke) !important; }
    /* ── Click-gate: chips on non-active cards are passive labels.
       The user has to focus the card first before any chip (especially
       the funding-badge funding-modal trigger) becomes interactive.
       This stops accidental modal opens while the user is just
       scrolling the carousel. */
    .dashboard-container.map-expanded .timeline-block:not(.active):not(.focused) .etc-pills * {
      pointer-events: none !important;
    }
    .dashboard-container.map-expanded .timeline-block.active .etc-pills *,
    .dashboard-container.map-expanded .timeline-block.focused .etc-pills * {
      pointer-events: auto !important;
    }

    .etc-title {
      font-size: 1.02rem;   /* was 0.82 — much scannable at a glance now that the sidebar's gone */
      font-weight: 800;
      line-height: 1.22;
      color: var(--text-dark);
      /* 3-line clamp by default so long titles ("First Maternal & Child
         Health Training Conference") wrap cleanly instead of ellipsising
         the meaning away — the focused card removes the clamp entirely
         so the user can ALWAYS read the full title. */
      display: -webkit-box;
      -webkit-line-clamp: 3;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
    .etc-meta {
      display: flex; flex-wrap: wrap; gap: 8px;
      font-size: 0.72rem;   /* was 0.66 — match the bigger title */
      color: var(--text-light);
      font-weight: 600;
      letter-spacing: 0.2px;
    }
    .etc-date { text-transform: uppercase; letter-spacing: 0.4px; }
    .etc-region { color: var(--text-light); }

    /* Track-color tint on the chip body (subtle so the pills inside
     * still pop).  Matches the existing mobile collapsed-label tints. */
    .dashboard-container.map-expanded .timeline-block[data-track="blue"]  .expanded-title-card {
      background: var(--tenki-blue-light);  border-color: rgba(0,90,156,0.2);
    }
    .dashboard-container.map-expanded .timeline-block[data-track="green"] .expanded-title-card {
      background: var(--tenki-green-light); border-color: rgba(0,140,69,0.2);
    }
    .dashboard-container.map-expanded .timeline-block[data-track="black"] .expanded-title-card {
      background: #f5f5f5;                  border-color: rgba(0,0,0,0.12);
    }
    .dashboard-container.map-expanded .timeline-block[data-track="white"] .expanded-title-card {
      background: var(--tenki-white-light); border-color: var(--tenki-white-border);
    }

    /* Active / focused: solid track color, white text.  Same flip
     * pattern the timeline cards use when selected. */
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card {
      color: #fff !important;
      box-shadow: 0 6px 15px rgba(0,0,0,0.18) !important;
      border-color: transparent !important;
    }
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card .etc-title,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card .etc-title {
      color: #fff !important;
    }
    .dashboard-container.map-expanded .timeline-block.active .expanded-title-card .etc-meta,
    .dashboard-container.map-expanded .timeline-block.focused .expanded-title-card .etc-meta {
      color: rgba(255,255,255,0.85) !important;
    }
    .dashboard-container.map-expanded .timeline-block.active[data-track="blue"]  .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused[data-track="blue"] .expanded-title-card {
      background: var(--tenki-blue) !important;
    }
    .dashboard-container.map-expanded .timeline-block.active[data-track="green"]  .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused[data-track="green"] .expanded-title-card {
      background: var(--tenki-green) !important;
    }
    /* Active black-track chip: subtle diagonal gradient instead of a
       solid black slab.  Goes from deep Tenki black at the top-left
       to a slightly lighter blue-tinted black at the bottom-right.
       Adds depth so foundational events like "Tenki is founded" feel
       like a card rather than a punched-out hole, and quietly ties
       the Engine/foundational track to the Tenki blue palette. */
    .dashboard-container.map-expanded .timeline-block.active[data-track="black"]  .expanded-title-card,
    .dashboard-container.map-expanded .timeline-block.focused[data-track="black"] .expanded-title-card {
      background: linear-gradient(135deg,
        #1A1A1A 0%,
        #232B36 55%,
        #2C3A52 100%) !important;
    }
    /* Floating nav stays visible + lifts above the expanded map so
       the user can use the ← / → arrows + All Tracks dropdown while
       browsing pins. */
    .dashboard-container.map-expanded .floating-nav-container {
      display: flex !important;
      z-index: 700;
    }
    /* ── Depth toggle (was "Expand map") ──────────────────────────────
       This button is the canonical Simplified ⇆ Full Story switch on
       desktop.  When `.simplified-view` AND `.map-expanded` are active
       together, the section reads as a glanceable map with title-only
       event cards above the spine.  Clicking the button removes both
       classes → returns to the full sidebar + detailed cards view. */
    .map-expand-toggle {
      position: absolute !important;
      top: 12px !important;
      right: 12px !important;
      z-index: 1000 !important;            /* above leaflet zoom (1000) + everything else */
      background: #1A1A1A !important;      /* literal hex — don't depend on a var that
                                              might not be in scope on every section */
      color: #ffffff !important;
      border: 2px solid #ffffff !important; /* white outline so the pill pops on any map */
      border-radius: 22px !important;
      padding: 9px 16px 9px 14px !important;
      font: 700 0.82rem/1 'Segoe UI', Roboto, sans-serif !important;
      letter-spacing: 0.4px !important;
      cursor: pointer !important;
      box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35) !important;
      transition: background .15s ease, transform .15s ease, box-shadow .15s ease;
      display: inline-flex !important;
      visibility: visible !important;
      opacity: 1 !important;
      pointer-events: auto !important;
      align-items: center;
      gap: 6px;
    }
    .map-expand-toggle:hover {
      transform: translateY(-1px);
      background: #333;
      box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28);
    }
    .map-expand-toggle-icon {
      display: inline-block; font-size: 1rem; line-height: 1;
    }
    /* When the dashboard is already in simplified+expanded mode the
       button label flips to "Full story" (see setupDepthToggle in JS).
       Give it a subtle accent so the user can tell at a glance which
       mode they're in. */
    .dashboard-container.simplified-view .map-expand-toggle {
      background: var(--tenki-blue);
      color: #fff;
      border-color: transparent;
    }
    .dashboard-container.simplified-view .map-expand-toggle:hover {
      background: #00477a;
    }
  }
  @media (max-width: 768px) {
    /* Mobile uses the dedicated mobile-map-view tab toggle for the same
       intent — the desktop "Expand map" button would be redundant + visually
       noisy alongside the map drawer + back-handle UI. */
    .map-expand-toggle { display: none !important; }
  }
  @media (min-width: 769px) {
    /* keep this empty media query open so subsequent rules can fall under
       the same desktop wrapper if needed in the future. */
    .timeline-outer-wrapper {
      flex: 1 1 auto;
      /* 2026-05-28 (round 4): cards are now compact (title + chips +
         optional location button) ≈ 130–150 px each, so the wrapper
         doesn't need the old 320-px floor.  220 keeps the spine +
         year strip comfortable and lets the map+sidebar claim the
         freed vertical room below. */
      min-height: 220px;
      /* `overflow-x: clip` still clips the >100%-wide horizontal
         track without creating a scrollbar, BUT `overflow-y: visible`
         allows hover-expanded event cards to extend DOWNWARD past
         the wrapper's bottom edge — overlaying the map section
         below it (which has z-index: 10 vs. the lifted card's 50).
         Previously `overflow: hidden` was clipping the lifted card
         and the user perceived "cards stuck behind the map." */
      overflow-x: clip;
      overflow-y: visible;
      order: 5;
    }
    .timeline-scroll-wrapper {
      height: 100% !important;
      max-height: 100% !important;
      overflow-y: auto;
    }
  }

  /* Wrap around the leaflet canvas so the desktop tap-to-engage
     overlay can sit absolutely positioned over it without being
     stretched by the .map-interface-grid's grid track sizing. */
  .map-canvas-wrap {
    position: relative;
    height: 100%;
    min-height: 0;
  }
  #leaflet-map-canvas {
    border-radius: 12px; box-shadow: inset 0 2px 10px rgba(0,0,0,0.02);
    border: 1px solid rgba(0,0,0,0.05); height: 100%; z-index: 1;
    background-color: #f1f3f4;
  }

  /* ── Desktop map tap-to-engage overlay ────────────────────────────
     Translucent panel that covers the map until the user explicitly
     clicks it.  Prevents scroll-wheel-zoom from hijacking the page
     scroll for visitors who are just passing through.  Dismissed
     for the rest of the session on tap; refresh restores it.
     Hidden on mobile (mobile uses its own full-dashboard overlay
     at the .tenki-explore-overlay level). */
  .tenki-desktop-map-overlay {
    position: absolute;
    inset: 0;
    z-index: 50;          /* above leaflet (z:1), below floating-nav */
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    padding: 24px;
    background: linear-gradient(135deg,
                rgba(238, 245, 251, 0.82) 0%,
                rgba(252, 252, 252, 0.78) 50%,
                rgba(238, 245, 251, 0.82) 100%);
    backdrop-filter: blur(4px) saturate(1.05);
    -webkit-backdrop-filter: blur(4px) saturate(1.05);
    cursor: pointer;
    border-radius: 12px;
    transition: opacity 0.35s ease, visibility 0s linear 0.35s;
    -webkit-tap-highlight-color: transparent;
    font-family: inherit;
  }
  .tenki-desktop-map-overlay.dismissed {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }
  .tenki-desktop-map-overlay-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 28px 36px;
    background: var(--tenki-white);
    border-radius: 14px;
    box-shadow: 0 14px 40px rgba(0, 90, 156, 0.18);
    border-top: 5px solid var(--tenki-blue);
    max-width: 380px;
    text-align: center;
    pointer-events: none;
    transform: translateY(0);
    animation: tenkiDesktopMapBob 3.4s ease-in-out infinite;
  }
  .tenki-desktop-map-overlay:hover .tenki-desktop-map-overlay-card {
    box-shadow: 0 16px 48px rgba(0, 90, 156, 0.26);
  }
  .tenki-desktop-map-overlay-icon {
    font-size: 2.2rem;
    line-height: 1;
    animation: tenkiDesktopMapPulse 1.9s ease-in-out infinite;
  }
  .tenki-desktop-map-overlay-title {
    font-size: 1.05rem;
    font-weight: 800;
    color: var(--tenki-blue);
    letter-spacing: 0.3px;
  }
  .tenki-desktop-map-overlay-sub {
    font-size: 0.82rem;
    color: var(--text-light);
    font-weight: 600;
    line-height: 1.4;
    max-width: 280px;
  }
  @keyframes tenkiDesktopMapBob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-4px); }
  }
  @keyframes tenkiDesktopMapPulse {
    0%, 60%, 100% { transform: scale(1); opacity: 0.92; }
    30%           { transform: scale(1.10); opacity: 1; }
  }
  @media (prefers-reduced-motion: reduce) {
    .tenki-desktop-map-overlay-card { animation: none; }
    .tenki-desktop-map-overlay-icon  { animation: none; }
  }

  /* Desktop Map Sidebar Detail View */
  .map-details-sidebar {
    background-color: var(--tenki-white); border-radius: 12px; padding: 30px;
    box-shadow: 0 4px 15px rgba(0,0,0,0.02); display: flex; flex-direction: column;
    border: 1px solid rgba(0,0,0,0.05); overflow-y: auto;
  }

  .details-placeholder { margin: auto; color: var(--text-light); padding: 6px 4px; }
  .details-placeholder svg { width: 50px; height: 50px; fill: #CCCCCC; margin-bottom: 12px; }

  /* Welcome card — first-load state of the sidebar before any event /
     pin is selected.  Replaces the previously-empty placeholder that
     only had a single sentence "Select an event…" line — now reads as
     an actual intro to the section. */
  .placeholder-welcome { text-align: left; line-height: 1.5; }
  .placeholder-eyebrow {
    font-size: 0.7rem; font-weight: 800; letter-spacing: 1.2px;
    text-transform: uppercase; color: var(--tenki-blue);
    margin-bottom: 8px;
  }
  .placeholder-title {
    font-size: 1.25rem; font-weight: 800; color: var(--tenki-black);
    margin: 0 0 12px 0; letter-spacing: 0.2px;
  }
  .placeholder-blurb {
    font-size: 0.92rem; color: var(--text-light);
    margin: 0 0 18px 0;
  }
  .placeholder-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    margin-bottom: 18px;
  }
  .placeholder-stat {
    background: var(--bg-light);
    border-left: 3px solid var(--tenki-blue);
    border-radius: 4px;
    padding: 10px 12px;
    text-align: center;
  }
  .placeholder-stat-num {
    display: block; font-size: 1.4rem; font-weight: 800;
    color: var(--tenki-black); line-height: 1.1;
  }
  .placeholder-stat-label {
    display: block; font-size: 0.7rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.6px;
    color: var(--text-light); margin-top: 4px;
  }
  .placeholder-cta-row {
    border-top: 1px dashed rgba(0,0,0,0.1);
    padding-top: 14px;
    margin-top: 4px;
  }
  .placeholder-cta-hint {
    font-size: 0.85rem; color: var(--text-light); margin: 0;
    display: flex; align-items: flex-start; gap: 8px;
  }
  .placeholder-pin {
    flex-shrink: 0; font-size: 1.05rem; line-height: 1.3;
  }

  .details-content-view { display: none; flex-direction: column; height: 100%; }
  .details-content-view.active { display: flex; }

  .details-content-view h3 { margin: 0 0 4px 0; font-size: 1.4rem; color: var(--text-dark); }
  .details-region { font-size: 0.85rem; font-weight: 600; color: var(--text-light); margin-bottom: 15px; text-transform: uppercase; letter-spacing: 0.3px; }
  .details-content-view p { margin: 0 0 20px 0; font-size: 0.95rem; line-height: 1.5; color: var(--text-light); }

  /* Desktop Floating Nav */
  .floating-nav-container {
    display: flex;
    position: absolute;
    bottom: 30px;
    left: 30px;
    z-index: 1000;
    flex-direction: column;
    animation: expandFadeIn 0.3s ease;
  }

  .floating-controls-group { display: flex; align-items: center; gap: 8px; }
  /* Prev / next event-cycling arrows.  Previously hidden on desktop
     (only the All Tracks button showed); user wants them surfaced so
     they can step through pins / events alongside the dropdown.  Mobile
     CSS at @media (max-width: 768px) takes over with its own sizing. */
  .nav-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, 0.08);
    background: var(--tenki-white);
    color: var(--tenki-black);
    font-size: 1.05rem;
    font-weight: 800;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
    transition: transform .15s ease, background .15s ease, color .15s ease;
  }
  .nav-arrow:hover {
    background: var(--tenki-blue);
    color: var(--tenki-white);
    transform: translateY(-1px);
  }
  .nav-arrow:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    box-shadow: none;
  }

  .all-tracks-reset-btn {
    background: var(--tenki-black); color: var(--tenki-white); border: none;
    padding: 10px 20px; border-radius: 30px; font-weight: 700; font-size: 0.85rem;
    text-transform: uppercase; box-shadow: 0 4px 15px rgba(0,0,0,0.18);
    cursor: pointer; display: flex; align-items: center; gap: 10px;
    transition: transform 0.2s ease, background-color 0.2s ease;
  }
  .all-tracks-reset-btn:hover { background: #333; transform: translateY(-2px); }
  .all-tracks-reset-btn img { height: 18px; width: auto; object-fit: contain; border-radius: 2px; }
  .all-tracks-reset-btn svg { transition: transform 0.3s ease; }
  .floating-nav-container.menu-open .all-tracks-reset-btn svg { transform: rotate(180deg); }

  .floating-nav-menu {
    /* Dropdown opens UP-LEFT from the All Tracks button.  Pinning
       BOTH left:0 and a sensible max-width keeps it inside the
       viewport even when the all-tracks button is near the right
       edge of the screen on mobile.  JS in `openFloatingNavMenu`
       flips it to `right: 0; left: auto` when the button's left
       edge is within (menu-width + 16px) of the viewport's right
       edge — preventing the off-screen render the user reported. */
    position: absolute; bottom: calc(100% + 10px); left: 0; background: var(--tenki-white);
    border-radius: 12px; box-shadow: 0 8px 25px rgba(0,0,0,0.15); padding: 8px 0;
    display: flex; flex-direction: column; min-width: 240px; max-width: calc(100vw - 32px);
    max-height: 55vh; overflow-y: auto;
    scrollbar-width: thin; opacity: 0; pointer-events: none; transform: translateY(10px);
    transition: all 0.2s ease; border: 1px solid rgba(0,0,0,0.05);
  }
  /* Right-pinned variant — when JS detects the button is too close to
     the viewport's right edge, this class flips the dropdown so it
     opens leftward instead of rightward. */
  .floating-nav-menu.is-right-pinned { left: auto; right: 0; }
  .floating-nav-menu::-webkit-scrollbar { width: 6px; }
  .floating-nav-menu::-webkit-scrollbar-thumb { background-color: rgba(0,0,0,0.1); border-radius: 4px; }
  .floating-nav-container.menu-open .floating-nav-menu { opacity: 1; pointer-events: auto; transform: translateY(0); }

  .floating-nav-item {
    padding: 12px 20px; font-size: 0.85rem; font-weight: 700; color: var(--text-dark);
    cursor: pointer; transition: background 0.2s; display: flex; align-items: center; gap: 10px;
  }
  .floating-nav-item:hover { background: var(--bg-light); color: var(--tenki-blue); }
  .floating-nav-item.sub-track { padding-left: 42px; font-size: 0.78rem; font-weight: 600; color: var(--text-light); padding-top: 8px; padding-bottom: 8px; }

  /* Map Markers — teardrop pin shape (was circles).  The .custom-map-pin
     wrapper just sizes + animates; the actual pin visual is an inline
     SVG injected via the Leaflet divIcon's HTML (see buildPinSVG() in
     the JS block below).  Track color is baked into the SVG fill at
     build-marker time — no CSS variables on the body, which is needed
     because SVG fill attributes don't reliably resolve var() in older
     WebKit (we hit this in the mini-map drawer originally).  Drop-
     shadow filter follows the actual pin alpha so the shadow hugs the
     teardrop edge rather than a phantom square. */
  .custom-map-pin {
    width: 24px; height: 32px;
    transition: transform 0.18s ease, filter 0.18s ease;
    filter: drop-shadow(0 3px 5px rgba(0,0,0,0.3));
    transform-origin: 50% 100%;     /* scale from the pin's POINT, not center */
  }
  .custom-map-pin svg { display: block; width: 100%; height: 100%; }
  /* Track-color classes kept for backwards compatibility with the
     activeMapPin() helper that may still toggle them — the SVG fill
     drives the actual color, but other code paths still flip these
     class names.  No paint impact since the SVG carries the color. */
  .pin-blue, .pin-green, .pin-white, .pin-black { /* color in SVG */ }

  /* ── Diagonal SVG connector from spine-positioned node to card top
       — desktop-only.  In default view, the node sits at the actual
       date position on the spine (might be far from the block's flex
       slot) while the card body stays at its evenly-spaced flex
       position.  Without a visible connector the two read as
       disconnected; this dashed line traces the relationship. */
  .node-card-connector { display: none; }
  @media (min-width: 769px) {
    .node-card-connector { display: block; color: var(--tenki-black); }
    .node-card-connector.node-blue  { color: var(--tenki-blue); }
    .node-card-connector.node-green { color: var(--tenki-green); }
    .node-card-connector.node-white { color: var(--tenki-white-stroke); }
    .node-card-connector.node-black { color: var(--tenki-black); }
    /* Hide the connector in map-expanded mode — the cards are then
       just title-chips and the spine is hidden, so there's nothing
       meaningful to connect. */
    .dashboard-container.map-expanded .node-card-connector { display: none; }
    /* Hover / active / focused: the line stays DASHED (user feedback:
       "all connectors should have the dotted line feature") — earlier
       version flipped to solid at this state, which made the black
       Tenki Organization track read as a solid line.  We just brighten
       opacity + thicken stroke so the connector still emphasizes
       attention. */
    .timeline-block:hover .node-card-connector line,
    .timeline-block.active .node-card-connector line,
    .timeline-block.focused .node-card-connector line {
      opacity: 0.95;
      stroke-width: 2.8;
    }
  }
  .custom-map-pin.active-pin {
    transform: scale(1.35);
    filter: drop-shadow(0 6px 10px rgba(0,0,0,0.42));
    z-index: 1000 !important;
  }

  /* ── Marker cluster styling — white circle, dark count, colored border
     where border tone reflects the most-common track in the cluster.
     Class names follow Leaflet.markercluster's convention; we override
     the defaults so the badge reads as Tenki-branded.  The inline
     iconCreateFunction (see JS) sets data-track-* attributes which
     these selectors hook into for border color. */
  .tenki-cluster {
    background: #fff;
    border-radius: 50%;
    width: 38px !important;
    height: 38px !important;
    line-height: 38px;
    text-align: center;
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    font-weight: 800;
    font-size: 0.95rem;
    color: var(--tenki-black);
    border: 3px solid var(--tenki-black);
    box-shadow: 0 3px 8px rgba(0,0,0,0.22), 0 0 0 1.5px rgba(255,255,255,0.9);
    transition: transform 0.15s ease;
  }
  .tenki-cluster:hover { transform: scale(1.08); }
  .tenki-cluster[data-track="blue"]  { border-color: var(--tenki-blue);  }
  .tenki-cluster[data-track="green"] { border-color: var(--tenki-green); }
  .tenki-cluster[data-track="black"] { border-color: var(--tenki-black); }
  .tenki-cluster[data-track="white"] { border-color: #888; }
  /* Suppress markercluster default styles — the .marker-cluster wrapper
     has its own bg color we don't want; only our inner .tenki-cluster
     should show. */
  .leaflet-marker-icon.marker-cluster,
  .leaflet-marker-icon.marker-cluster div {
    background: transparent !important;
    box-shadow: none !important;
    border: 0 !important;
  }

  .tag-container { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; flex: 1 1 auto;}
  .details-tag {
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px;
    padding: 3px 8px; border-radius: 4px; display: inline-block; align-self: flex-start;
  }
  .tag-blue { background-color: var(--tenki-blue-light); color: var(--tenki-blue); }
  .tag-green { background-color: var(--tenki-green-light); color: var(--tenki-green); }
  .tag-white { background-color: var(--tenki-white-light); color: var(--tenki-white-stroke); border: 1px solid var(--tenki-white-border); }
  .tag-black { background-color: #F4F4F4; color: var(--tenki-black); }

  .locations-list-container { margin: 15px 0; padding: 12px; background: rgba(0,0,0,0.02); border-radius: 8px; border-left: 3px solid var(--timeline-line); }
  .locations-list-container h4 { margin: 0 0 8px 0; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-light); }
  .locations-list { margin: 0; padding-left: 20px; font-size: 0.85rem; color: var(--text-dark); max-height: 180px; overflow-y: auto; }
  .locations-list li { margin-bottom: 4px; }
  .locations-list li.interactive-loc { cursor: pointer; text-decoration: underline; text-decoration-color: transparent; transition: all 0.2s ease; }
  .locations-list li.interactive-loc:hover { color: var(--tenki-blue); text-decoration-color: var(--tenki-blue); }

  .details-cta-group { margin-top: auto; padding-top: 15px; display: flex; flex-direction: column; gap: 10px; width: 100%; }
  .details-cta-link {
    display: block; width: 100%; padding: 12px;
    background-color: var(--tenki-black); color: var(--tenki-white); text-align: center; text-decoration: none;
    font-weight: 700; font-size: 0.88rem; text-transform: uppercase; letter-spacing: 0.5px; border-radius: 6px;
    transition: all 0.2s ease;
  }
  .details-cta-link.link-blue { background-color: var(--tenki-blue); }
  .details-cta-link.link-green { background-color: var(--tenki-green); }
  .details-cta-link.link-white { background-color: var(--tenki-white-stroke); color: var(--tenki-white); }
  .details-cta-link.secondary-btn { background-color: transparent; color: var(--text-dark); border: 2px solid var(--timeline-line); }
  .details-cta-link.secondary-btn:hover { border-color: var(--tenki-black); color: var(--tenki-black); }

  /* Desktop Timeline Horizontal Rail Section */
  .timeline-outer-wrapper { position: relative; width: 100%; order: 4; overflow: hidden; z-index: 10; }

  .scroll-btn {
    display: none; align-items: center; justify-content: center; position: absolute;
    top: 50%; transform: translateY(-50%); width: 44px; height: 44px;
    background-color: var(--tenki-white); border: 2px solid var(--timeline-line); border-radius: 50%;
    cursor: pointer; z-index: 20; box-shadow: 0 4px 10px rgba(0,0,0,0.05); transition: all 0.2s ease;
  }
  .scroll-btn svg { width: 24px; height: 24px; fill: var(--text-light); transition: fill 0.2s ease; }
  .scroll-btn:hover { background-color: var(--tenki-black); border-color: var(--tenki-black); }
  .scroll-btn:hover svg { fill: var(--tenki-white); }
  .prev-btn { left: -22px; }
  .next-btn { right: -22px; }
  @media (min-width: 769px) { .scroll-btn { display: flex; } }

  .timeline-scroll-wrapper {
    overflow-x: auto;
    /* Explicitly allow vertical ink-overflow so hover-expanded event
       cards (z-index:50) can paint OVER the map below them.  Modern
       Chrome / Safari respect this combo (overflow-x:auto + overflow-y:
       visible) as "scroll horizontally, paint outside vertically" —
       Firefox may treat overflow-y as auto, in which case the JS
       fallback in timeline-block:hover handlers escalates the card to
       position:fixed so it still escapes the clipping ancestor. */
    overflow-y: visible;
    padding: 25px 10px; scrollbar-width: none;
    -webkit-overflow-scrolling: touch; margin-bottom: 40px;
    touch-action: pan-y pinch-zoom; scroll-padding-left: 20px; cursor: grab;
  }
  .timeline-scroll-wrapper::-webkit-scrollbar { display: none; }
  .timeline-scroll-wrapper.is-dragging { cursor: grabbing; user-select: none; }

  .timeline-track { display: flex; gap: 25px; position: relative; padding-top: 65px; min-width: max-content; }
  .timeline-track::before { content: ''; position: absolute; height: 4px; background-color: var(--timeline-line); top: 40px; left: 0; right: 0; z-index: 1; }
  /* ── Month tick marks ────────────────────────────────────────────────
     Bold-enough notches at every month boundary along the spine so the
     eye can read elapsed time at a glance.  Quarter marks (Jan/Apr/Jul/
     Oct = months 0/3/6/9) are noticeably longer + darker so quarters
     pop.  Year boundaries (month 0) are skipped here — the year-badge
     pill serves that role.  Pointer-events: none so they never block
     clicks on events / spine. */
  .tl-month-tick {
    position: absolute;
    width: 2px;
    height: 14px;
    top: 33px;                  /* crosses the spine (top:40, height:4) */
    background: rgba(0, 0, 0, 0.42);
    pointer-events: none;
    z-index: 0;                 /* under everything; spine cuts through it */
    border-radius: 1px;
  }
  .tl-month-tick.is-quarter {
    width: 2px;
    height: 22px;
    top: 29px;
    background: rgba(0, 0, 0, 0.65);
  }

  .year-divider { position: relative; flex: 0 0 60px; display: flex; justify-content: center; scroll-snap-align: start; }
  .year-badge {
    position: absolute; top: -37px; background-color: var(--tenki-black); color: var(--tenki-white);
    font-weight: 800; font-size: 1.05rem; padding: 6px 16px; border-radius: 20px; z-index: 5;
    letter-spacing: 1px; box-shadow: 0 4px 10px rgba(0,0,0,0.15); cursor: pointer; transition: all 0.2s;
  }
  .year-badge:hover { opacity: 0.8; }

  .timeline-block {
    position: relative; flex: 0 0 290px; transition: opacity 0.3s ease; scroll-snap-align: start; cursor: pointer;
  }
  .timeline-block:focus-visible { outline: 3px solid var(--tenki-blue); outline-offset: 4px; border-radius: 10px; }
  .timeline-block * { pointer-events: none; }
  .timeline-block .details-cta-group *, .timeline-block .locations-list-container *, .timeline-block .funding-badge, .timeline-block .share-btn, .timeline-block .click-indicator { pointer-events: auto; }

  /* ── Event marker (node) ─────────────────────────────────────────────
     The colored circle that anchors each event to the spine.  Resized
     down to 12 px (was 18) + 3 px ring (was 4) so it matches the new
     month-tick scale — a giant 26 px outer-diameter node dwarfed the
     spine and made the timeline read as crowded.  --node-color is the
     CSS custom property that drives both the border color and the
     stem-to-card connector below.  Each .node-* class overrides it. */
  .timeline-node {
    --node-color: var(--tenki-black);
    width: 12px; height: 12px; position: absolute; border-radius: 50%;
    background-color: var(--tenki-white); border: 3px solid var(--node-color);
    /* left:20 puts the outer-circle center at x=29 — the same x the
       JS-driven .duration-dot (left:24 + 5 = 29) and .duration-stem
       (left:27 + 2 = 29) center on, so node + dot + stem now share
       a single vertical axis instead of being 4 px out of phase. */
    top: -33px; left: 20px; z-index: 3;
    transition: transform 0.2s ease, background-color 0.2s ease;
    pointer-events: auto !important;
  }
  .timeline-node.node-blue  { --node-color: var(--tenki-blue); }
  .timeline-node.node-green { --node-color: var(--tenki-green); }
  .timeline-node.node-black { --node-color: var(--tenki-black); }
  .timeline-node.node-white { --node-color: var(--tenki-white-stroke); }

  /* The OLD vertical stem (a 2 px straight line dropping from the node
     to the card top) is RETIRED for desktop — the SVG diagonal
     `.node-card-connector` now handles that visual relationship, which
     correctly anchors to the spine-positioned node regardless of where
     the card sits in its flex slot.  Hidden on desktop; mobile still
     uses it (since on mobile cards stack vertically below their node
     and a straight stem is the right shape).
     The CSS rules below were the old stem; they remain so mobile keeps
     its behavior.  Desktop-specific override at the top of the file
     hides the pseudo-element. */
  @media (min-width: 769px) {
    .timeline-node::after { display: none !important; }
  }
  .timeline-node::after {
    content: '';
    position: absolute;
    top: calc(100% + 3px);      /* just below the node ring                       */
    left: 50%;
    transform: translateX(-50%);
    width: 2px;
    height: 13px;               /* lands flush on the card's 5 px top border-color */
    background: var(--node-color);
    opacity: 0.55;
    border-radius: 1px;
    transition: height 0.2s ease, opacity 0.2s ease, width 0.2s ease;
    pointer-events: none;
  }
  .timeline-block:hover .timeline-node::after {
    opacity: 0.85;
    width: 3px;
  }
  .timeline-block.active .timeline-node::after,
  .timeline-block.focused .timeline-node::after {
    opacity: 1;
    width: 3px;
    height: 15px;               /* extends slightly into the card top border-color */
  }

  .timeline-block.focused .timeline-node, .timeline-block.active .timeline-node { transform: scale(1.35); background-color: var(--node-color); }

  .timeline-card {
    background-color: var(--tenki-white); border-radius: 10px; padding: 12px 14px 14px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.03); border-top: 5px solid var(--tenki-black);
    display: flex; flex-direction: column;
    /* height: auto (default) — card sizes to its content so the new
       lean layout (time-marker + title + chip row + optional location
       button) reads as a tight unit instead of being stretched up to
       the row's tallest sibling.  Block stretch is removed below via
       align-self: flex-start on the block itself. */
    transition: transform 0.3s ease, box-shadow 0.3s ease;
  }

  /* ── Track icon (declutter pass) ─────────────────────────────────────
     A small per-track glyph (🩺 / 📚 / 🌱 / 🏢) rendered just before
     the event title.  Pairs with the track-color top border so viewers
     who don't yet know the colour code still get a visual hint about
     which pillar the event belongs to.  Sits to the left of the title
     in BOTH .timeline-card h3 and .expanded-title-card .etc-title. */
  .tenki-track-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.25em;
    height: 1.25em;
    margin-right: 6px;
    font-size: 1em;
    line-height: 1;
    vertical-align: -0.05em;
    flex-shrink: 0;
  }
  .timeline-card h3 .tenki-track-icon,
  .expanded-title-card .etc-title .tenki-track-icon {
    /* keep the glyph aligned with the title's baseline so a long title
       wrapping to a second line doesn't drag the icon downward. */
    align-self: flex-start;
  }

  /* ── Declutter: drop the phase/funding badge row from the timeline
     card so the SAME info doesn't render twice (it's already on the
     sidebar detail card to the right + on the expanded-title card
     when in simplified mode).  Track is now conveyed by icon + color.
     Badges still show in the .mobile-inline-details block on phones
     (where the sidebar doesn't render). */
  @media (min-width: 769px) {
    /* 2026-05-28 redesign (round 3): chips live ON the activity card
       now (tag + phase + funding) instead of in the sidebar.  The card
       becomes "title + date + chips" — clean, glance-able.  The verbose
       description was merged into the sidebar text so the card body is
       just chrome metadata.  See the JS render block for the markup
       change. */
    .timeline-card .card-badge-row {
      margin-bottom: 8px;       /* tighter than the original 10 */
      gap: 6px;                 /* tighter chips */
      align-items: center;
    }
    .timeline-card .card-badge-row .phase-badge,
    .timeline-card .card-badge-row .funding-badge,
    .timeline-card .card-badge-row .details-tag {
      font-size: 0.6rem;        /* the card real-estate is tight */
      padding: 3px 7px;
      letter-spacing: 0.3px;
    }
  }
  .card-blue-track { border-top-color: var(--tenki-blue); }
  .card-green-track { border-top-color: var(--tenki-green); }
  .card-white-track { border-top-color: var(--tenki-white-stroke); border-top-style: dashed; }
  .card-black-track { border-top-color: var(--tenki-black); }

  /* Active card: stays in place (no transform lift) — outline + soft
     shadow defined in the desktop @media block above handle the
     selected-state affordance.  Removed the translateY(-4px) that
     used to nudge the card up on selection — it caused layout shift
     + interacted poorly with the no-hover design. */
  .timeline-block.active .timeline-card { box-shadow: 0 8px 20px rgba(0,0,0,0.08); }

  .card-badge-row { display: flex; justify-content: space-between; align-items: flex-start;
                     margin-bottom: 10px; width: 100%;
                     gap: 14px;          /* bumped 10 → 14 so share-btn never visually kisses the right-most badge */
                     min-width: 0; }     /* allow children to shrink properly */
  .card-badge-row > div { min-width: 0; flex: 1 1 auto; }   /* tag wrapper shrinks too */
  .card-badge-row .share-btn {
    flex-shrink: 0;
    /* Tighter padding + label suppressed at narrow card widths so the
       button doesn't muscle into the badge column.  See media query
       below for the responsive trim. */
    padding: 3px 6px;
    align-self: flex-start;
  }
  /* Narrow desktop cards (~210 px collapsed): hide the "Share" label,
     keep just the icon — eliminates the cramping where the long
     funding-badge text + "Share" label fought for the same row. */
  @media (min-width: 769px) {
    .timeline-block:not(.active):not(.focused) .card-badge-row .share-btn span {
      display: none;
    }
  }

  .phase-badge, .funding-badge {
    display: inline-block; font-size: 0.65rem; font-weight: 800; text-transform: uppercase;
    padding: 4px 8px; border-radius: 4px; letter-spacing: 0.5px;
    align-self: flex-start; text-decoration: none;
  }
  button.funding-badge { transition: transform 0.2s ease, box-shadow 0.2s ease; cursor: pointer; }
  button.funding-badge:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0,0,0,0.08); }

  .share-btn {
    background: none; border: none; color: #555 !important; cursor: pointer;
    display: inline-flex !important; align-items: center; gap: 4px;
    font-size: 0.75rem; font-weight: 600; text-transform: uppercase;
    transition: color 0.2s, background 0.2s;
    padding: 4px 8px; border-radius: 4px;
    pointer-events: auto; visibility: visible !important; opacity: 1 !important;
  }
  .share-btn:hover { color: var(--tenki-blue) !important; background: var(--bg-light); }
  .share-btn:hover svg { fill: var(--tenki-blue) !important; }
  /* Belt-and-suspenders: force the inline SVG inside the share button to
     render at a guaranteed visible size + color even if a Squarespace global
     CSS rule tries to zero them out. */
  .share-btn svg { width: 16px !important; height: 16px !important;
                   display: inline-block !important; flex-shrink: 0;
                   color: #555 !important; }
  .share-btn svg path { fill: currentColor; }

  .phase-accomplished { background-color: var(--phase-accomplished-bg); color: var(--phase-accomplished-text); }
  .phase-planning { background-color: var(--phase-planning-bg); color: var(--phase-planning-text); }
  .phase-execution { background-color: var(--phase-execution-bg); color: var(--phase-execution-text); }
  .phase-future { background-color: var(--phase-future-bg); color: var(--phase-future-text); }

  .funding-seeking { background-color: #fff0f0; color: #d32f2f; border: 1px solid rgba(211, 47, 47, 0.2); }
  .funding-partial { background-color: #fff9e6; color: #e65100; border: 1px solid rgba(230, 81, 0, 0.2); }
  .funding-full { background-color: #e8f5e9; color: #2e7d32; border: 1px solid rgba(46, 125, 50, 0.2); }
  .funding-active { background-color: #e3f2fd; color: #0d47a1; font-weight: 900; border: 1px dashed #0d47a1; }

  .time-marker { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; display: block;}

  /* ── Duration Span ─────────────────────────────── */
  .duration-span {
    margin-bottom: 8px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  }
  .duration-dates {
    font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.4px;
    color: var(--text-light); display: flex; align-items: center; gap: 4px;
  }
  .duration-dates .dur-arrow { color: #bbb; }
  .duration-pill {
    font-size: 0.62rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.3px;
    padding: 2px 7px; border-radius: 10px; background: rgba(0,0,0,0.06); color: var(--text-light);
    white-space: nowrap;
  }
  .card-blue-track .duration-pill { background: var(--tenki-blue-light); color: var(--tenki-blue); }
  .card-green-track .duration-pill { background: var(--tenki-green-light); color: var(--tenki-green); }
  .card-white-track .duration-pill { background: var(--tenki-white-light); color: var(--tenki-white-stroke); border: 1px solid var(--tenki-white-border); }
  /* Chip duration suffix */
  .chip-duration { font-size: 0.72em; font-weight: 600; opacity: 0.7; margin-left: 4px; }

  /* ── Duration Line Extension (desktop: rightward, ABOVE the spine) ──────── */
  /* Track padding-top:65px, spine top:40px → block-relative spine = -25px.   */
  /* Lane 0 at -47px clears year-badge top (≈ -37px) by 5px — no overlap.    */
  /* Bar left/top/width are ALL overridden by updateDurationLines() per event. */
  /* Bar starts at event startDate track-position, ends at endDate position.   */
  .duration-line-ext {
    position: absolute;
    top: -47px;   /* lane-0 default; JS overrides per lane                      */
    left: 0;      /* JS sets to startDate track-pos relative to block left       */
    height: 7px;
    width: 0;     /* set dynamically                                             */
    border-radius: 4px;
    opacity: 0.75;
    transition: opacity 0.3s ease;
    pointer-events: none;
    z-index: 2;
  }
  /* Vertical stem: drops from bar bottom to the spine. left/top/height set by JS. */
  .duration-stem {
    position: absolute;
    width: 4px;
    border-radius: 0 0 3px 3px;
    opacity: 0.9;
    pointer-events: none;
    z-index: 3;
  }
  /* Colored circle at spine level, anchoring bar to event node. left/top set by JS. */
  .duration-dot {
    position: absolute;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    opacity: 0.9;
    pointer-events: none;
    z-index: 3;
  }
  .timeline-block[data-track="blue"]  .duration-line-ext,
  .timeline-block[data-track="blue"]  .duration-stem,
  .timeline-block[data-track="blue"]  .duration-dot  { background: var(--tenki-blue); }
  .timeline-block[data-track="green"] .duration-line-ext,
  .timeline-block[data-track="green"] .duration-stem,
  .timeline-block[data-track="green"] .duration-dot  { background: var(--tenki-green); }
  .timeline-block[data-track="black"] .duration-line-ext,
  .timeline-block[data-track="black"] .duration-stem,
  .timeline-block[data-track="black"] .duration-dot  { background: var(--tenki-black); }
  .timeline-block[data-track="white"] .duration-line-ext,
  .timeline-block[data-track="white"] .duration-stem,
  .timeline-block[data-track="white"] .duration-dot  { background: var(--tenki-white-stroke); }

  /* ── Ongoing bars (End Date = "Ongoing"/"present"/"now") ─────────────
     The trailing edge of an ongoing bar fades to transparent + pulses
     gently so visitors register "this is still going" without text. */
  /* ── Duration-bar end-cap indicators ─────────────────────────────────
     Two visual states for the trailing edge of each bar:
       • data-ongoing="1"  → ▶  arrow at the end (still happening)
       • default (bounded) → ■  small square cap (defined end date)
     Renders via ::after on the bar — desktop AND mobile.  Helps the
     user distinguish "still active" vs "wrapped up" without reading
     the text labels.  Skipped on bars too short to make the cap
     readable (.short-bar — set by JS when duration < 2 months). */
  .duration-line-ext::after {
    content: '';
    position: absolute;
    z-index: 4;
    pointer-events: none;
  }
  /* Bounded — small filled square at the trailing edge */
  .duration-line-ext:not([data-ongoing="1"])::after {
    right: -3px; top: 50%;
    transform: translateY(-50%);
    width: 7px; height: 7px;
    background: inherit;
    border-radius: 1px;
    box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.9);
  }
  /* Ongoing — arrow glyph at the trailing edge.  Uses a CSS triangle
     (border trick) tinted to match the bar.  Sits OVER the bar's
     fading edge so it reads as "still going". */
  .duration-line-ext[data-ongoing="1"]::after {
    right: -6px; top: 50%;
    transform: translateY(-50%);
    width: 0; height: 0;
    border-top: 6px solid transparent;
    border-bottom: 6px solid transparent;
    border-left: 9px solid currentColor;
    color: inherit;
    background: transparent;
    border-radius: 0;
    /* The bar's background-color is inherited by border-left via
       currentColor — we set color: same as bg in the track variants
       below. */
  }
  .timeline-block[data-track="blue"]  .duration-line-ext[data-ongoing="1"]::after { border-left-color: var(--tenki-blue);   color: var(--tenki-blue); }
  .timeline-block[data-track="green"] .duration-line-ext[data-ongoing="1"]::after { border-left-color: var(--tenki-green);  color: var(--tenki-green); }
  .timeline-block[data-track="black"] .duration-line-ext[data-ongoing="1"]::after { border-left-color: var(--tenki-black);  color: var(--tenki-black); }
  .timeline-block[data-track="white"] .duration-line-ext[data-ongoing="1"]::after { border-left-color: var(--tenki-white-stroke); color: var(--tenki-white-stroke); }
  /* On mobile the timeline is VERTICAL — flip the indicators so they
     point DOWN (next month) for ongoing, and the square sits at the
     BOTTOM edge of the bar. */
  @media (max-width: 768px) {
    .duration-line-ext:not([data-ongoing="1"])::after {
      right: 50%; top: auto; bottom: -3px;
      transform: translateX(50%);
      width: 7px; height: 7px;
    }
    .duration-line-ext[data-ongoing="1"]::after {
      right: 50%; top: auto; bottom: -8px;
      transform: translateX(50%);
      border-top: 9px solid currentColor;
      border-bottom: 0;
      border-left: 6px solid transparent;
      border-right: 6px solid transparent;
    }
  }
  /* Hide both indicators on very short bars where they'd dominate */
  .duration-line-ext.short-bar::after { display: none; }

  .duration-line-ext[data-ongoing="1"] {
    mask-image: linear-gradient(to right,
                                rgba(0,0,0,1) 0%,
                                rgba(0,0,0,1) 78%,
                                rgba(0,0,0,0) 100%);
    -webkit-mask-image: linear-gradient(to right,
                                rgba(0,0,0,1) 0%,
                                rgba(0,0,0,1) 78%,
                                rgba(0,0,0,0) 100%);
    animation: tenkiOngoingPulse 2.6s ease-in-out infinite;
  }
  @keyframes tenkiOngoingPulse {
    0%, 100% { opacity: 0.75; }
    50%      { opacity: 0.95; }
  }
  /* "→ present" label gets a subtle italic + tenki-blue so the present-
     ness reads even on the funding popup / sidebar without an icon. */
  .duration-dates .dur-present {
    font-style: italic;
    color: var(--tenki-blue);
    font-weight: 600;
  }
  /* Pill on ongoing events: green-ish "Active" prefix needs the same
     tenki-blue treatment to read as live. */
  .duration-span.is-ongoing .duration-pill {
    background: var(--tenki-blue-light);
    color: var(--tenki-blue);
    font-weight: 700;
  }
  .timeline-block.active  .duration-line-ext,
  .timeline-block.focused .duration-line-ext { opacity: 0.78; }
  .timeline-block.collapsed-by-year    .duration-stem,
  .timeline-block.collapsed-by-year    .duration-dot  { opacity: 0 !important; }
  .timeline-block.super-hidden-by-year .duration-stem,
  .timeline-block.super-hidden-by-year .duration-dot  { display: none !important; }

  /* ── Tab Intro Bounce ──────────────────────────── */
  @keyframes tabIntroBounce {
    0%   { transform: scale(1); }
    20%  { transform: scale(1.18); color: var(--tenki-blue); }
    40%  { transform: scale(0.93); }
    60%  { transform: scale(1.08); }
    80%  { transform: scale(0.97); }
    100% { transform: scale(1); }
  }
  .mobile-tab.intro-bounce { animation: tabIntroBounce 0.7s ease forwards; }
  /* Hidden on desktop; shown via mobile media query */
  .mobile-tab-hint { display: none; }
  /* Tab-attached drawers: mobile-only — desktop never shows them */
  .map-tab-drawer,
  .timeline-tab-drawer { display: none; }
  /* Mobile-map back-handle: mobile-only.  Top-level rule (outside
     any media query) hides it everywhere by default; the mobile
     media query at ~line 2105 then turns it back on conditionally
     when .dashboard-container.mobile-map-view is active.  Without
     this top-level rule the default <button> renders on desktop
     as a horizontal pill with the inner "← timeline" text
     visible — see the rogue bar at the top of the timeline section. */
  .mobile-map-back-handle { display: none; }
  /* Tap-to-explore overlay: mobile-only — desktop never shows it.
     Resets default <button> chrome so the panel reads as a tap target. */
  .tenki-explore-overlay { display: none; background: none; border: 0; padding: 0; font: inherit; color: inherit; }
  /* Immersive-mode exit chips: mobile-only.  Desktop ignores immersive
     state entirely (enterImmersive bails on `window.innerWidth > BP_MOBILE`),
     but defensively hide the chips here so they never flash if they did. */
  .tenki-immersive-exit,
  .tenki-immersive-x { display: none; }

  /* PROTECTED DESKTOP HEADERS */
  .timeline-card h3 { margin: 0 0 6px 0 !important; font-size: 1.1rem !important; color: var(--text-dark) !important; font-weight: 700 !important; line-height: 1.2 !important; }
  .timeline-card p { margin: 0 !important; font-size: 0.88rem !important; line-height: 1.4 !important; color: var(--text-light) !important; }
  .click-indicator { margin-top: auto; padding-top: 15px; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; color: #999999; }
  button.click-indicator { background: none; border: none; display: block; width: 100%; text-align: left; cursor: pointer; }
  button.click-indicator:hover { color: var(--tenki-blue); }

  /* Desktop Protection for Hidden Elements */
  .collapsed-label { display: none !important; }
  .mobile-inline-details { display: none !important; }
  .return-to-map-btn { display: none !important; }
  .mobile-tab-bar { display: none; }

  /* Mobile View Framework Implementations */
  @media (max-width: 950px) {
    .map-interface-grid { grid-template-columns: 1fr; height: auto; gap: 20px; }
    #leaflet-map-canvas { height: 350px; }
    .map-details-sidebar { min-height: 220px; }
    /* Desktop tap-to-engage overlay is hidden below tablet width —
       mobile has its own full-section .tenki-explore-overlay that
       covers the entire dashboard, so we don't want two competing
       overlays at the same time. */
    .tenki-desktop-map-overlay { display: none; }
    .map-canvas-wrap { height: 350px; }
  }

  @media (max-width: 768px) {
    /* 100dvh Layout locks strictly to the screen limits.
       Also: isolate the stacking context (z-index + isolation:isolate) so
       the next Squarespace section below the embed can't visually paint
       OVER the bottom of our timeline (reported case: Maya Angelou quote
       block overlapping the 2027 year area on mobile homepage).  The SQS
       quote block uses position:relative with no z-index, which lets its
       background/border render at a layer that bleeds into our content
       when our z-index stack is muddled.  isolation:isolate creates a new
       stacking context boundary that the parent SQS section can't cross. */
    .dashboard-container {
      padding: 0; margin: 0; border-radius: 0; height: 100dvh !important; max-height: 100dvh !important;
      display: flex; flex-direction: column; overflow: hidden; position: relative;
      border: none; background-color: var(--bg-light);
      isolation: isolate;
      z-index: 1;
      /* Belt-and-suspenders: force the embed's containing Code Block to
         take the full claimed height — Squarespace's Fluid Engine sometimes
         lets the .sqs-block-code wrapper auto-shrink to content height,
         which is what allows the next section to draw on top. */
      min-height: 100dvh !important;
    }
    /* Reach UP into the SQS wrapper so the embed's Code Block claims the
       layout space its child needs.  These selectors are SQS-specific but
       safe to no-op on other markup. */
    .sqs-block-code:has(> div > #dashboard-container),
    .sqs-block.sqs-block-code:has(#dashboard-container) {
      min-height: 100dvh;
      display: block;
    }

    /* ── Immersive mode (mobile only) ────────────────────────────────────
       When the user explicitly engages with the timeline/map (taps an
       event OR opens the Map tab), the dashboard takes over the WHOLE
       viewport so scrolling to other parts of the homepage no longer
       competes with horizontal timeline scroll + map drag.  Feels like a
       dedicated app for the duration of the interaction; an exit chip
       (see `.tenki-immersive-exit`) is the only way out.
       Triggered by adding `.is-immersive` to .dashboard-container and
       `.tenki-immersive` to <body> (the latter locks page scroll). */
    .dashboard-container.is-immersive {
      position: fixed !important;
      top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
      width: 100vw !important;
      height: 100dvh !important; max-height: 100dvh !important;
      z-index: 9999 !important;
      border-radius: 0 !important;
      /* Pad the bottom so the exit chip never overlaps timeline rows or
         map controls — the chip itself is ~52 px tall. */
      padding-bottom: 60px !important;
      box-sizing: border-box !important;
    }
    /* Body scroll lock — applied to <body> by JS.  Belt-and-suspenders:
       overflow:hidden stops natural page scroll, touch-action:none stops
       iOS rubber-band, position:fixed preserves scrollY when re-engaged.
       Documented gotcha: position:fixed on body causes the viewport to
       jump to top — JS captures window.scrollY first and restores it on
       exit by scrolling back to that y. */
    body.tenki-immersive {
      overflow: hidden !important;
      touch-action: none !important;
      overscroll-behavior: none !important;
    }
    /* Exit chip — appears only in immersive mode.  Bottom-center,
       full-width band with a clear call to "release me, let me scroll."
       Tap target ≥ 48 px tall (Apple HIG mobile minimum). */
    .tenki-immersive-exit {
      display: none;
      position: fixed;
      left: 0; right: 0; bottom: 0;
      z-index: 10000;
      padding: 12px 16px calc(12px + env(safe-area-inset-bottom)) 16px;
      background: linear-gradient(to top, var(--tenki-blue) 0%, #0073c4 100%);
      color: #fff;
      font-size: 0.9rem; font-weight: 800;
      text-align: center;
      letter-spacing: 0.4px;
      cursor: pointer;
      border: none;
      width: 100%;
      box-shadow: 0 -4px 16px rgba(0,0,0,0.18);
      -webkit-tap-highlight-color: transparent;
    }
    /* Entry pulse — when immersive engages, the exit chip pulses bright
       blue for ~5 seconds so the user CAN'T miss it.  Replaces the
       earlier subtle dark chip that users were reporting they didn't
       notice.  Goal: bottom-of-screen attention magnet that says
       "here's how you get out" loud and clear. */
    body.tenki-immersive .tenki-immersive-exit { animation: tenkiExitEntrance 5s ease-out 1; }
    @keyframes tenkiExitEntrance {
      0%   { transform: translateY(80%);    box-shadow: 0 -4px 16px rgba(0, 90, 156, 0); }
      18%  { transform: translateY(0);      box-shadow: 0 -6px 26px rgba(0, 90, 156, 0.65); }
      30%  { transform: translateY(-3px);   box-shadow: 0 -8px 32px rgba(0, 90, 156, 0.80); }
      45%  { transform: translateY(0);      box-shadow: 0 -6px 24px rgba(0, 90, 156, 0.55); }
      60%  { transform: translateY(-2px);   box-shadow: 0 -8px 30px rgba(0, 90, 156, 0.70); }
      75%  { transform: translateY(0);      box-shadow: 0 -6px 22px rgba(0, 90, 156, 0.40); }
      100% { transform: translateY(0);      box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.18); }
    }
    @media (prefers-reduced-motion: reduce) {
      body.tenki-immersive .tenki-immersive-exit { animation: none; }
    }
    body.tenki-immersive .tenki-immersive-exit { display: block !important; }
    .tenki-immersive-exit:active { background: linear-gradient(to top, rgba(0,0,0,1) 0%, rgba(0,0,0,0.9) 100%); }
    .tenki-immersive-exit .tenki-immersive-exit-icon {
      display: inline-block; margin-right: 6px; font-size: 0.95rem;
      animation: tenki-immersive-chev 1.5s ease-in-out infinite;
    }
    @keyframes tenki-immersive-chev {
      0%, 100% { transform: translateY(0); opacity: 0.85; }
      50%      { transform: translateY(2px); opacity: 1; }
    }
    /* Subtle top-corner X for users who'd rather close from the top */
    .tenki-immersive-x {
      display: none;
      position: fixed;
      top: calc(8px + env(safe-area-inset-top)); right: 8px;
      z-index: 10001;
      width: 36px; height: 36px;
      border-radius: 50%;
      background: rgba(0,0,0,0.65);
      color: #fff;
      border: none;
      font-size: 1.2rem; line-height: 36px;
      cursor: pointer;
      padding: 0;
      box-shadow: 0 2px 8px rgba(0,0,0,0.25);
      -webkit-tap-highlight-color: transparent;
    }
    body.tenki-immersive .tenki-immersive-x { display: block !important; }
    .tenki-immersive-x:active { background: rgba(0,0,0,0.85); }

    /* Compact Headings */
    .dashboard-header {
      background: #FFFFFF; margin: 0; padding: 10px 15px 5px 15px;
      z-index: 10 !important; position: relative; flex-shrink: 0; border-radius: 10px 10px 0 0;
    }
    .dashboard-header p { display: none !important; }
    .dashboard-header h2 { font-size: 1.2rem; margin: 0; }

    /* Control Bar boosted Z-index for upward dropdown overlays */
    .dashboard-controls {
      padding: 5px 0px 10px 0px; margin-bottom: 0; gap: 10px; z-index: 99999 !important;
      position: relative; background: #FFFFFF; border-bottom: none; flex-shrink: 0;
    }

    .filters-scroll-wrapper { width: 100%; overflow: visible !important; z-index: 99999 !important;}

    .filters-scroll-wrapper::before, .filters-scroll-wrapper::after {
      content: ''; position: absolute; top: 0; width: 45px; height: 38px; z-index: 160; pointer-events: none; transition: opacity 0.2s ease; opacity: 0;
    }
    .filters-scroll-wrapper::before { left: 0; background: linear-gradient(to right, #FFFFFF 40%, rgba(255,255,255,0) 100%); }
    .filters-scroll-wrapper::after { right: 0; background: linear-gradient(to left, #FFFFFF 40%, rgba(255,255,255,0) 100%); }
    .filters-scroll-wrapper.left-hint-active::before { opacity: 1; }
    .filters-scroll-wrapper.right-hint-active::after { opacity: 1; }

    .timeline-filters {
      flex-wrap: nowrap !important; justify-content: flex-start !important; overflow-x: auto !important;
      padding: 0 45px 0 15px !important; gap: 8px; width: 100%; scrollbar-width: none; -webkit-overflow-scrolling: touch; overflow-y: visible !important;
      z-index: 99999 !important;
    }
    .timeline-filters::-webkit-scrollbar { display: none !important; }

    .dropdown-wrapper { flex: 0 0 auto; display: inline-block; overflow: visible !important; }
    .filter-btn { white-space: nowrap; padding: 6px 14px; font-size: 0.78rem; }

    /* Mobile dropdowns — anchor to right edge of the viewport so they
       never clip off-screen.  The filter chips live at the top of the
       timeline section in a horizontal scroll row; when the last chip
       (e.g. "Sort") opens its dropdown, the default left:0 anchor was
       letting the menu render past the right edge of the screen
       (unselectable).  left:auto + right:0 fixes the rightmost
       dropdowns; for safety, also cap max-width to viewport. */
    .dropdown-menu-options {
      z-index: 999999 !important;
      max-width: calc(100vw - 32px) !important;
    }
    /* If the dropdown's parent .dropdown-wrapper is in the right half
       of the filter row, render the menu right-aligned so it stays
       on-screen.  Detected via :nth-last-child(-n+2) — the last 1-2
       dropdowns in the row. */
    .timeline-filters .dropdown-wrapper:nth-last-child(-n+2) .dropdown-menu-options {
      left: auto !important;
      right: 0 !important;
    }

    /* MAP FLEX WRAPPER - Hidden by default, shown via Map tab */
    .map-interface-grid {
      position: relative !important; display: none !important; flex-direction: column !important;
      flex: 1 1 auto !important; height: auto !important; width: 100% !important;
      z-index: 10; padding: 0; margin: 0; transition: flex 0.3s ease;
    }

    #leaflet-map-canvas {
      flex: 1; width: 100%; height: 100% !important; border-radius: 0; border: none; z-index: 1;
    }

    .map-details-sidebar { display: none !important; }

    /* Native Map Controls Moved Down Slightly to clear track buttons */
    .leaflet-top.leaflet-left { top: 75px !important; left: 10px !important; transform: none !important; }

    /* FLOATING NAV COMMAND CENTER (Card bar fixed to bottom) */
    /* ── Mobile floating nav — stacked right-side up/down jumpers ─────
       Was a bottom-centered pill bar that the exit-immersive chip
       covered.  Reworked to a vertical RIGHT-edge stack with the
       prev/next arrows as round buttons + the filter chip BELOW them.
       Sits at vertical center of the timeline area so the user's
       thumb reaches it naturally. */
    .floating-nav-container {
      display: flex !important;
      flex-direction: column !important;
      align-items: stretch !important;
      gap: 10px !important;
      position: absolute !important;
      right: 12px !important;
      left: auto !important;
      bottom: auto !important;
      top: 50% !important;
      transform: translateY(-50%) !important;
      z-index: 9000 !important;
      background: transparent !important;
      padding: 0 !important;
      box-shadow: none !important;
      animation: none !important;
      width: auto !important;
      max-width: none !important;
    }

    /* The arrow group: tight vertical pair so prev/next read as a unit */
    .floating-controls-group {
      display: flex !important;
      flex-direction: column !important;
      align-items: center !important;
      gap: 4px !important;
      width: 48px !important;
      padding: 4px !important;
      background: rgba(255, 255, 255, 0.96) !important;
      border-radius: 24px !important;
      box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.06) !important;
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      border: none;
    }

    .nav-arrow {
      display: flex !important;
      background: transparent !important;
      border: none;
      color: var(--tenki-blue) !important;
      width: 40px !important;
      height: 40px !important;
      border-radius: 20px !important;
      align-items: center;
      justify-content: center;
      font-weight: 900;
      transition: background 0.15s, transform 0.15s;
      cursor: pointer;
      padding: 0;
      font-size: 1.25rem !important;
      pointer-events: auto;
      flex-shrink: 0;
    }
    /* Rotate ← → glyphs so they read as ↑ ↓ on the vertical mobile
       timeline.  prev-btn (top of stack) shows ↑ — earlier events
       are HIGHER UP on the vertical track, so up = back in time.
       next-btn (bottom of stack) shows ↓ — later events are LOWER
       on the track.  CSS:
         &larr; (←) → rotate(90deg)  = ↑   (the prev button)
         &rarr; (→) → rotate(90deg)  = ↓   (the next button)
       Note rotate(90deg) NOT -90 like before — earlier version had
       the arrows backwards (prev pointed down). */
    #floating-prev-btn { transform: rotate(90deg); }
    #floating-next-btn { transform: rotate(90deg); }
    .nav-arrow:active {
      background: rgba(0, 90, 156, 0.12) !important;
      transform: rotate(90deg) scale(0.94);
    }
    .nav-arrow:disabled {
      color: #c0cdd8 !important;
      opacity: 1 !important;
      pointer-events: none;
    }
    /* Small label hint between the buttons — "prev / next event"
       so first-time users grok what they do. */
    .floating-controls-group::before {
      content: '↕';
      display: block;
      width: 0;
      height: 0;
      overflow: hidden;
      position: absolute;
    }

    /* Filter button sits BELOW the arrow stack as a separate pill */
    .all-tracks-reset-btn {
      box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12), 0 0 0 1px rgba(0, 0, 0, 0.06) !important;
      margin: 0 !important;
      background: rgba(255, 255, 255, 0.96) !important;
      color: var(--tenki-black) !important;
      padding: 8px 10px !important;
      width: 48px !important;
      height: auto !important;
      border-radius: 12px !important;
      display: flex !important;
      flex-direction: column !important;
      align-items: center !important;
      justify-content: center !important;
      gap: 3px !important;
      font-size: 0.62rem !important;
      letter-spacing: 0.4px !important;
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      line-height: 1.15 !important;
      text-transform: uppercase;
      font-weight: 700;
    }
    .all-tracks-reset-btn img { display: none !important; }
    .all-tracks-reset-btn:hover { background: rgba(255,255,255,1) !important; transform: none; }
    .all-tracks-reset-btn svg {
      display: inline-flex !important;
      width: 12px !important;
      height: 12px !important;
      color: var(--tenki-blue);
    }
    .floating-nav-menu { display: none !important; }
    /* When the filter dropdown is open, render it as a TALL, SKINNY
       vertical column anchored to the viewport's right edge so it
       can't clip off-screen on any phone.  Each entry is generously
       tall (44px+) for thumb-friendly tapping.  Width is narrow so
       the map stays mostly visible behind it.
       `position: fixed` (not absolute) — anchors to the viewport so
       a wide dashboard-container's right edge doesn't push it off.
       User-reported: prior version popped off the right side of
       the viewport and was unusable. */
    .floating-nav-container.menu-open .floating-nav-menu {
      display: flex !important;
      flex-direction: column !important;
      position: fixed !important;
      right: 70px !important;          /* just left of the floating-nav buttons */
      top: 50% !important;
      bottom: auto !important;
      transform: translateY(-50%) !important;
      max-height: 70vh !important;
      width: 160px !important;          /* skinny */
      max-width: 50vw !important;
      background: #fff !important;
      border-radius: 14px !important;
      box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18), 0 0 0 1px rgba(0,0,0,0.05) !important;
      padding: 6px 0 !important;
      overflow-y: auto !important;
      z-index: 9100 !important;        /* above the floating-nav arrows */
    }
    /* Each entry in the popup — full-width, tall, easy to tap. */
    .floating-nav-container.menu-open .floating-nav-menu > * {
      min-height: 44px !important;
      padding: 10px 14px !important;
      font-size: 0.85rem !important;
      font-weight: 700 !important;
      border-radius: 0 !important;
      width: 100% !important;
      display: flex !important;
      align-items: center !important;
      gap: 8px !important;
      white-space: nowrap !important;
      overflow: hidden !important;
      text-overflow: ellipsis !important;
    }

    /* TIMELINE FLOW CONTAINER: Standard Document Flex Rules below Map */
    .timeline-outer-wrapper {
      position: relative !important; flex: 1 1 auto !important; width: 100% !important;
      display: flex !important; flex-direction: column !important; background: #F8F9FA !important;
      box-shadow: inset 0 4px 10px rgba(0,0,0,0.02) !important; border: none !important; margin: 0 !important;
      transform: none !important; overflow: hidden !important;
    }

    .timeline-scroll-wrapper {
      flex: 1 1 auto !important; width: 100%; overflow-x: hidden !important; overflow-y: auto !important;
      height: auto !important; max-height: none !important;
      /* Edge-to-edge on mobile — was padding:15px 15px 95px 15px which
         inset the timeline 15 px from the viewport edges, making the
         visual NOT match the tap-to-explore overlay (which is full
         width).  Now both are edge-to-edge.  The 28 px spine offset
         that the timeline track needs is moved INTO the track's own
         padding-left below. */
      padding: 15px 0 95px 0 !important;
      -webkit-overflow-scrolling: touch;
    }

    .timeline-track {
      display: flex !important; flex-direction: column !important; gap: 15px !important;
      padding-top: 5px !important; min-width: auto !important;
      /* Track needs left padding for the spine + right padding for
         visual breathing room.  Replaces what the scroll-wrapper was
         doing before, so events still don't kiss the screen edges.
         Bumped 28 → 56 so concurrent events' duration-bar lanes (which
         stack LEFT of the spine at MOB_STEP px each) have room to
         render before clipping the viewport edge — was hiding the
         second lane (TBOW vs OB Ultrasound). */
      padding-left: 56px !important;
      padding-right: 15px !important;
    }

    .timeline-track::before {
      /* Sharper, more distinct spine — was 4 px @ 30 % black, hard to
         distinguish from duration bars sitting next to it.  Now 5 px
         solid black with a thin white outline so it reads as the
         "main axis" while the colored bars are secondary tracks. */
      width: 5px !important; height: 100% !important; top: 0 !important; bottom: 0 !important; left: 10px !important;
      background-color: var(--tenki-black) !important;
      box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.95) !important;
      z-index: 1;
      border-radius: 2px;
    }
    /* ── Spine-to-node connector stem ──────────────────────────────
       Bridges the visual gap between the vertical spine (left:10) and
       each event's circle (left:-32 relative to its block, but the
       block starts after a 28 px track padding-left → node ends up
       roughly at the same horizontal position as the spine on the
       left side of the card row).  A small horizontal pill connects
       them so the eye reads "node BELONGS to spine" instead of
       "floating dots beside the line". */
    .timeline-block .timeline-node::before {
      content: '';
      position: absolute;
      left: 100%;
      top: 50%;
      width: 14px;
      height: 2px;
      background: var(--tenki-black);
      border-radius: 1px;
      transform: translateY(-50%);
      pointer-events: none;
      z-index: 1;
    }
    .timeline-block[data-track="blue"]  .timeline-node::before { background: var(--tenki-blue); }
    .timeline-block[data-track="green"] .timeline-node::before { background: var(--tenki-green); }
    .timeline-block[data-track="black"] .timeline-node::before { background: var(--tenki-black); }
    .timeline-block[data-track="white"] .timeline-node::before { background: var(--tenki-white-stroke); }
    /* Mobile: ticks rotate 90° — horizontal strokes crossing the vertical spine.
       Bolder than the old subtle 1 px version so they read on small screens
       without forcing the user to lean in. */
    .tl-month-tick {
      width: 14px !important; height: 2px !important;
      top: auto !important; left: 5px !important;
      background: rgba(0, 0, 0, 0.42) !important;
      border-radius: 1px !important;
    }
    .tl-month-tick.is-quarter {
      width: 22px !important; height: 2px !important; left: 1px !important;
      background: rgba(0, 0, 0, 0.65) !important;
    }
    /* On mobile the node sits to the LEFT of the card row (left:-32px) — a
       downward stem from the node doesn't connect to anything visible,
       so hide it.  The mobile layout uses the bubble itself as the
       visual link. */
    .timeline-node::after { display: none !important; }

    .timeline-block {
      position: relative !important; flex: 0 0 auto !important; width: 100% !important; max-width: none !important;
      display: flex !important; align-items: center !important; scroll-snap-align: none !important; min-height: 40px !important; margin-bottom: 12px !important; cursor: pointer;
    }

    /* Hide card when not active */
    .timeline-block:not(.active) .timeline-card { display: none !important; }

    /* Show card when active */
    .timeline-block.active .timeline-card { display: flex !important; margin-top: 5px; }

    .timeline-block:not(.active) { display: flex !important; align-items: center !important; cursor: pointer; }

    .timeline-block .timeline-node { top: 50% !important; transform: translateY(-50%) !important; left: -32px !important; position: absolute !important;}
    .timeline-block.active .timeline-node, .timeline-block.focused .timeline-node { transform: translateY(-50%) scale(1.3) !important; }

    .year-divider {
      position: relative; flex: 0 0 auto !important; display: flex !important; justify-content: flex-start !important;
      margin-left: -28px !important; margin-top: 15px !important; margin-bottom: 5px !important;
    }

    .year-badge { position: relative !important; top: 0 !important; font-size: 0.95rem !important; }

    .timeline-card { background: var(--tenki-white) !important; box-shadow: 0 4px 15px rgba(0,0,0,0.05) !important; padding: 15px !important; }
    /* 2026-05-28: chip suppression LIFTED.  Now that the timeline-tab-
       drawer is retired, the activity card is the only place these
       chips can live on mobile.  Show tag + phase + funding inline
       just like on desktop, with the same compact sizing. */
    .card-badge-row .details-tag,
    .card-badge-row .phase-badge,
    .card-badge-row .funding-badge {
      font-size: 0.6rem !important;
      padding: 3px 7px !important;
      letter-spacing: 0.3px !important;
    }
    .card-badge-row { display: flex !important; flex-direction: row !important;
                       align-items: center !important; gap: 8px !important;
                       justify-content: space-between !important;   /* was flex-end — chips left, share right */
                       flex-wrap: wrap !important; }
    /* Chip wrapper (the inline-styled div around tag/phase/funding)
       takes the lead column and is allowed to wrap onto a second line
       on very narrow phones — it grows with available space, share
       button stays pinned right via flex:0 0 auto. */
    .card-badge-row > div {
      flex: 1 1 auto !important;
      min-width: 0 !important;
      display: flex !important;
      flex-wrap: wrap !important;
      justify-content: flex-start !important;
      gap: 6px !important;
    }
    .card-badge-row .share-btn {
      flex: 0 0 auto !important;
      align-self: flex-start !important;
      margin-left: auto !important;
    }

    /* Bubble Formatting Protected & Colored by Track */
    .timeline-block:not(.active) .collapsed-label {
       display: block !important; font-size: 0.85rem !important; font-weight: 700 !important; padding: 10px 16px !important;
       border-radius: 20px !important; white-space: normal !important; overflow: hidden !important; text-overflow: ellipsis !important;
       max-width: 90% !important; border: 1px solid rgba(0,0,0,0.1) !important; transition: all 0.2s ease !important; line-height: 1.4 !important;
    }

    .timeline-block.active .collapsed-label, .timeline-block.focused .collapsed-label { color: #fff !important; box-shadow: 0 6px 15px rgba(0,0,0,0.15) !important; border-color: transparent !important;}
    .timeline-block.active[data-track="blue"] .collapsed-label, .timeline-block.focused[data-track="blue"] .collapsed-label { background: var(--tenki-blue) !important; }
    .timeline-block.active[data-track="green"] .collapsed-label, .timeline-block.focused[data-track="green"] .collapsed-label { background: var(--tenki-green) !important; }
    .timeline-block.active[data-track="black"] .collapsed-label, .timeline-block.focused[data-track="black"] .collapsed-label { background: var(--tenki-black) !important; }
    .timeline-block.active[data-track="white"] .collapsed-label, .timeline-block.focused[data-track="white"] .collapsed-label { background: var(--tenki-white-stroke) !important; }

    /* Non-active bubbles carry a soft tint of their track color */
    .timeline-block:not(.active):not(.focused)[data-track="blue"] .collapsed-label { background: var(--tenki-blue-light) !important; color: var(--tenki-blue) !important; border-color: rgba(0,90,156,0.2) !important; }
    .timeline-block:not(.active):not(.focused)[data-track="green"] .collapsed-label { background: var(--tenki-green-light) !important; color: var(--tenki-green) !important; border-color: rgba(0,140,69,0.2) !important; }
    .timeline-block:not(.active):not(.focused)[data-track="black"] .collapsed-label { background: #f0f0f0 !important; color: var(--tenki-black) !important; border-color: rgba(0,0,0,0.12) !important; }
    .timeline-block:not(.active):not(.focused)[data-track="white"] .collapsed-label { background: var(--tenki-white-light) !important; color: var(--tenki-white-stroke) !important; border-color: var(--tenki-white-border) !important; }

    /* STRICT LOCAL YEAR COLLAPSE CSS overrides */
    html body .timeline-block.collapsed-by-year .collapsed-label,
    html body .timeline-block.collapsed-by-year[data-track] .collapsed-label,
    html body .timeline-block.collapsed-by-year:not(.active)[data-track] .collapsed-label {
        display: none !important;
    }
    .timeline-block.collapsed-by-year { min-height: 20px !important; margin-bottom: 5px !important; }

    /* ── Duration Line (mobile: downward from node bottom) ── */
    /* Sibling of .timeline-node; node sits at top:50%/left:-32px/18×18.      */
    /* Node visual bottom = 50%+9px; left/height overridden by JS per lane.   */
    .duration-line-ext {
      top: calc(50% + 9px);  /* node visual bottom — JS also sets top:auto    */
      left: -25px;            /* lane-0 default; JS overrides per lane         */
      width: 5px;
      height: 0;              /* set dynamically by updateDurationLines()      */
      border-radius: 0 0 3px 3px;
      opacity: 0.28;
    }
    /* Stem and dot are desktop-only — hide on mobile */
    .duration-stem,
    .duration-dot { display: none; }

    /* ── Mobile lane-stem connector ──────────────────────────────────
       Connects each duration bar's TOP to the event's node circle
       when the bar is offset into lane 1, 2, 3+ (i.e. when this
       event's duration overlaps another concurrent event so the bar
       gets shifted LEFT of the node to its own lane).  Lane 0 bars
       sit roughly under the node — no stem needed there.
       Width is set via the --tl-mob-stem-w CSS variable from the
       lane-assignment JS pass.  Color uses currentColor inherited
       from per-track rules below. */
    .duration-line-ext::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: var(--tl-mob-stem-w, 0);
      height: 3px;
      background-color: currentColor;
      border-radius: 2px;
      pointer-events: none;
    }
    /* currentColor on a pseudo reads the CASCADED `color` value, so
       set color to the track tint via per-track selectors below. */
    .timeline-block[data-track="blue"]  .duration-line-ext { color: var(--tenki-blue); }
    .timeline-block[data-track="green"] .duration-line-ext { color: var(--tenki-green); }
    .timeline-block[data-track="black"] .duration-line-ext { color: var(--tenki-black); }
    .timeline-block[data-track="white"] .duration-line-ext { color: var(--tenki-white-stroke); }
    .timeline-block.active  .duration-line-ext,
    .timeline-block.focused .duration-line-ext { opacity: 0.75; }
    .timeline-block.collapsed-by-year   .duration-line-ext { opacity: 0 !important; }
    .timeline-block.super-hidden-by-year .duration-line-ext { display: none !important; }

    /* Expanded Details Mobile Logic */
    .timeline-block.active .mobile-inline-details {
      display: block !important; margin-top: 12px !important; padding-top: 12px !important; border-top: 1px solid rgba(226, 232, 240, 0.6) !important;
    }
    .mobile-inline-details p { margin-bottom: 10px !important; font-size: 0.9rem !important; color: var(--text-light) !important; line-height: 1.4 !important; white-space: normal !important; }
    .mobile-inline-details .details-region { font-size: 0.8rem !important; font-weight: 700 !important; color: var(--text-light) !important; margin-bottom: 8px !important; text-transform: uppercase !important; letter-spacing: 0.5px !important; }
    .mobile-inline-details .locations-list { max-height: 140px !important; }
    .mobile-inline-details .details-cta-group { margin-top: 5px !important; flex-direction: row !important; flex-wrap: wrap !important; gap: 8px !important; }
    .mobile-inline-details .details-cta-link { padding: 8px 12px !important; font-size: 0.75rem !important; width: auto !important; flex: 1; min-width: 120px; }
    /* The OLD in-card mini-map is suppressed on mobile in favor of the new
       map-tab-drawer below the Map tab button.  Keeping the markup in DOM
       so any external CSS targeting it doesn't error — just hide it. */
    .mobile-mini-map-host { display: none !important; }

    .return-to-map-btn { display: none !important; } /* Stripped completely from Mobile UI */

    /* ── Tap-to-explore overlay (mobile only) ───────────────────────────
       Shown over the timeline section by default.  User must tap this
       overlay to enter immersive mode — the deliberate gesture replaces
       the earlier "tap any event → suddenly fullscreen" surprise.
       Reset button styling so the <button> looks like a panel. */
    .tenki-explore-overlay {
      display: flex !important;
      position: absolute;
      /* JS sets `top` to the bottom of the mobile-tab-bar.  Filter
         chips + title sit ABOVE the overlay so they remain interactive.
         (Filter clicks also call enterImmersive — see the click-
         capture in positionTabDrawers below.)  Falls back to 240 px
         while JS is booting. */
      top: 240px;
      left: 0; right: 0; bottom: 0;
      width: 100%;
      /* Card is anchored LOWER in the overlay so it doesn't crowd the
         filter chips visually.  Was `align-items: flex-start` with
         padding-top:32 which still landed it near the top.  Now
         align-items:center pushes it to vertical middle of the
         remaining viewport. */
      align-items: center;
      justify-content: center;
      padding: 20px;
      background: linear-gradient(to bottom,
                  rgba(248, 249, 250, 0.88) 0%,
                  rgba(248, 249, 250, 0.74) 40%,
                  rgba(248, 249, 250, 0.50) 70%,
                  rgba(248, 249, 250, 0.25) 100%);
      backdrop-filter: blur(2px);
      -webkit-backdrop-filter: blur(2px);
      border: none;
      cursor: pointer;
      z-index: 99998;
      transition: opacity 0.25s ease, visibility 0s linear 0.25s;
      -webkit-tap-highlight-color: transparent;
    }
    /* When immersive mode is active, hide the overlay (user has already
       "entered" — no more invitation needed).  Visibility flip is delayed
       so the fade-out finishes before the overlay leaves the layout. */
    .dashboard-container.is-immersive .tenki-explore-overlay,
    .tenki-explore-overlay.dismissed {
      opacity: 0;
      visibility: hidden;
      pointer-events: none;
    }
    .tenki-explore-overlay-card {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 8px;
      padding: 22px 26px;
      background: var(--tenki-white);
      border-radius: 18px;
      box-shadow: 0 12px 36px rgba(0, 90, 156, 0.18);
      border-top: 4px solid var(--tenki-blue);
      max-width: 320px;
      text-align: center;
      pointer-events: none;     /* parent button captures the tap */
      transform: translateY(0);
      animation: tenkiExploreBob 2.6s ease-in-out infinite;
    }
    .tenki-explore-overlay-icon {
      font-size: 1.8rem;
      line-height: 1;
      animation: tenkiExploreTap 1.6s ease-in-out infinite;
    }
    .tenki-explore-overlay-title {
      font-size: 1rem;
      font-weight: 800;
      color: var(--tenki-blue);
      letter-spacing: 0.3px;
    }
    .tenki-explore-overlay-sub {
      font-size: 0.78rem;
      font-weight: 600;
      color: var(--text-light);
      line-height: 1.35;
    }
    @keyframes tenkiExploreBob {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-4px); }
    }
    @keyframes tenkiExploreTap {
      0%, 60%, 100% { transform: scale(1); opacity: 0.9; }
      30%           { transform: scale(1.15); opacity: 1; }
    }

    /* ── Map preview drawer (attached to the Map tab) ──────────────────
       Hidden by default — populated + expanded when a timeline event is
       activated and has at least one pin.  Non-interactive country-zoom
       Leaflet render; the whole drawer is the click target → opens the
       full Map tab.  Lives between the tab bar (order:4) and the
       timeline scroll area (order:5) so it visually emerges from BELOW
       the Map tab button. */
    /* ── Tab-attached drawers (overlay model) ──────────────────────────
       Both drawers float OVER the top of the timeline content using
       position:absolute anchored to the dashboard-container's clip
       region.  They do NOT push the timeline rows down — the user keeps
       reading the timeline below them.  Bottom corners rounded so each
       drawer reads as a fabric pull / bookmark hanging from the tab bar.

       Position math:
         The dashboard's mobile layout (top-down) is:
           dashboard-header  (~60 px)
           dashboard-controls (~60 px filters)
           mobile-tab-bar     (~46 px Timeline | Map)
           timeline-outer-wrapper  ← drawers hang OVER the top of this
         The drawers' `top` is JS-driven (positionTabDrawers()) so they
         always sit immediately below the tab bar regardless of how the
         filter row scrolls horizontally. */
    /* ── Three-state drawer system ─────────────────────────────────────
       hidden  = max-height:0, opacity:0  (no event selected)
       is-low  = small (~40 px), shows label only
       is-full = tall, shows rich content (map / tags)
       Drawer handle (.drawer-handle) at the bottom lets the user
       step DOWN: full → low → hidden.  Tab buttons show event-derived
       labels in is-full state (see the .has-event-label rules). */
    /* 2026-05-28: timeline-tab-drawer RETIRED.  In the original mobile
       design it pulled down from the Timeline tab button carrying the
       active event's title (LOW) and tags (FULL) — duplicate of what
       the activity card AND the tab-button event-label were already
       showing.  Now hidden in all states; the map-tab-drawer (which
       carries the unique mini-map preview) is the only drawer left. */
    .timeline-tab-drawer,
    .timeline-tab-drawer.is-low,
    .timeline-tab-drawer.is-full { display: none !important; }
    /* Hide the title-in-tab-button on the CURRENTLY-ACTIVE tab — its
       value is "what's selected on the OTHER tab" so the user can see
       what event they're returning to.  Showing it on the active tab
       just repeats the title already on the card / drawer below.  */
    .mobile-tab.active .mobile-tab-event-label { display: none !important; }
    .mobile-tab.active.has-event-label > :not(.mobile-tab-event-label) {
      display: inline-flex !important;
    }
    /* The .expanded-title-card is a desktop-only construct (simplified
       view's title chip strip).  On mobile it was leaking into the
       active card column when the user toggled Simplified mode,
       double-printing every event title.  Hard-hide on mobile. */
    .expanded-title-card { display: none !important; }
    .map-tab-drawer {
      position: absolute;
      left: 0; right: 0;
      top: 0;
      z-index: 30;
      background: rgba(255, 255, 255, 0.82);
      backdrop-filter: blur(8px) saturate(1.05);
      -webkit-backdrop-filter: blur(8px) saturate(1.05);
      overflow: hidden;
      opacity: 0;
      max-height: 0;
      pointer-events: none;
      transition: max-height 0.32s ease, opacity 0.22s ease, transform 0.32s ease;
      transform: translateY(-4px);
      border-radius: 0 0 16px 16px;
      border: 1px solid rgba(226, 232, 240, 0.7);
      border-top: none;
      box-shadow: 0 12px 24px rgba(0, 0, 0, 0.10);
      display: block !important;
    }
    .map-tab-drawer.is-low,
    .timeline-tab-drawer.is-low,
    .map-tab-drawer.is-full,
    .timeline-tab-drawer.is-full {
      opacity: 1;
      pointer-events: auto;
      transform: translateY(0);
    }
    /* Hide all the FULL-only content elements while in is-low */
    .map-tab-drawer.is-low .map-tab-drawer-frame,
    .timeline-tab-drawer.is-low .timeline-tab-drawer-tags { display: none !important; }
    /* Hide the LOW-only label while in is-full */
    .map-tab-drawer.is-full .tab-drawer-low-label,
    .timeline-tab-drawer.is-full .tab-drawer-low-label { display: none !important; }

    /* Drawer handle — small grip pill at the bottom edge.  Tap to
       cycle the drawer's state down (full → low → hidden).  Sized as
       a 44 px tappable hit target.  Now animated with a subtle pulse
       so users see it AS an interactive element instead of dismissing
       it as a passive divider. */
    .drawer-handle {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 22px;
      background: transparent;
      cursor: pointer;
      border: none;
      padding: 0;
      position: relative;
    }
    .drawer-handle::after {
      content: '';
      display: block;
      width: 44px;
      height: 4px;
      border-radius: 2px;
      background: rgba(100, 116, 139, 0.45);
      transition: background 0.15s ease, width 0.15s ease;
      /* Finite pulse — 3 cycles total = 8.4 s, then stops.  Earlier
         `infinite` was nauseating combined with the CTA pulse below.
         The pulse only fires when the drawer FIRST appears (via the
         keyframe start), so users still get the "interactive" cue. */
      animation: drawerHandlePulse 2.8s ease-in-out 3;
    }
    .drawer-handle:hover::after,
    .drawer-handle:focus-visible::after {
      background: var(--tenki-blue);
      width: 52px;
      animation: none;
    }
    .drawer-handle:focus-visible { outline: none; }
    /* Gentle pulse so the user sees the handle is interactive.  Steps
       through opacity + width so the pill "breathes" — never gets so
       big it crowds the drawer below, never so small it disappears. */
    @keyframes drawerHandlePulse {
      0%, 100% {
        opacity: 0.55;
        width: 44px;
        background: rgba(100, 116, 139, 0.45);
      }
      50% {
        opacity: 1;
        width: 56px;
        background: rgba(0, 90, 156, 0.55);
      }
    }
    @media (prefers-reduced-motion: reduce) {
      .drawer-handle::after { animation: none; }
    }
    /* Low-state label row — shown only in is-low.  Compact pill with
       the contextual label (event title / region / "Sierra Leone"). */
    .tab-drawer-low-label {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 14px 8px;
      font-size: 0.78rem;
      font-weight: 700;
      color: var(--tenki-blue);
      letter-spacing: 0.15px;
      line-height: 1.25;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .tab-drawer-low-label-icon { font-size: 0.9rem; flex-shrink: 0; }
    .tab-drawer-low-label-text {
      flex: 1;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    /* ── Right drawer (Map tab) ─────────────────────────────
       Holds the country-zoom mini-map.  Caption is now just the visual
       under-frame border-radius — the pin-count + region/location
       label moves UP onto the Map tab button itself (see the
       map-tab-loc-chip rule + JS populateMapTabDrawer). */
    .map-tab-drawer {
      width: 50%;
      left: auto;
      right: 0;
      cursor: pointer;
    }
    .map-tab-drawer.is-low  { max-height: 50px; }
    .map-tab-drawer.is-full { max-height: 175px; }
    .map-tab-drawer:hover { background: rgba(255, 255, 255, 0.92); }
    .map-tab-drawer:focus-visible { outline: 2px solid var(--tenki-blue); outline-offset: -2px; }
    .map-tab-drawer-frame {
      width: 100%; height: 140px;
      background: rgba(243, 244, 246, 0.65);
      position: relative;
      pointer-events: none;
    }
    .map-tab-drawer-frame .leaflet-control-container,
    .map-tab-drawer-frame .leaflet-control-zoom,
    .map-tab-drawer-frame .leaflet-control-attribution { display: none !important; }
    /* Map-tab BUTTON label overlay — when an event with locations is
       active, this chip overlays the "Map" text in the tab bar showing
       "📍 Bo Region" / "📍 Kambia" / "📍 3 pins" (whichever is most
       descriptive — see populateMapTabDrawer for the JS logic).  This
       replaces the pin-count caption that USED to sit under the mini-
       map; freed-up vertical space lets the map render taller. */
    /* ── Tab-button event-label overlay ─────────────────────────────
       Both tabs gain an absolute-positioned chip that swaps in over
       the original "Timeline" / "Map" text when the drawer is in
       is-full state.  Shows the event-derived label (title for
       Timeline, region/location for Map).  CSS replaces the entire
       button content via opacity/visibility — no layout shift. */
    .mobile-tab-event-label {
      display: none;
      position: absolute;
      left: 4px; right: 4px;
      top: 0; bottom: 0;
      align-items: center;
      justify-content: center;
      gap: 5px;
      font-size: 0.72rem; font-weight: 800;
      color: var(--tenki-blue);
      background: rgba(238, 245, 251, 0.96);
      letter-spacing: 0.1px;
      padding: 4px 6px;
      line-height: 1.15;
      text-align: center;
      overflow: hidden;
      pointer-events: none;
      border-radius: 5px;
    }
    /* Container has-event-label on each tab.  Toggled by drawer JS. */
    .mobile-tab.has-event-label { position: relative; }
    .mobile-tab.has-event-label .mobile-tab-event-label { display: flex; }
    .mobile-tab.has-event-label > svg,
    .mobile-tab.has-event-label > :not(.mobile-tab-event-label) {
      opacity: 0;
      transition: opacity .15s ease;
    }
    .mobile-tab-event-label-icon { font-size: 0.85rem; flex-shrink: 0; align-self: flex-start; margin-top: 1px; }
    .mobile-tab-event-label-text {
      overflow: hidden;
      max-width: 100%;
      /* Allow up to 2 lines so long event names don't get truncated with
         "…" in the tab button.  -webkit-line-clamp:2 + word-break give
         a graceful fallback if the name is still too long for two lines. */
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      white-space: normal;
      word-break: break-word;
      line-height: 1.15;
    }
    /* Legacy alias for the old map-tab-loc-chip selector (left here
       in case any inline JS still toggles it). */
    .map-tab-loc-chip { display: none !important; }

    /* ── Left drawer (Timeline tab) ────────────────────────
       Carries the active event's title (wrappable across 2 lines) + a
       row of phase/funding tags BELOW it.  These tags used to live on
       the activity card itself — moving them here declutters the card
       and gives the drawer real content to justify its size. */
    .timeline-tab-drawer {
      width: 50%;
      left: 0;
      right: auto;
      cursor: default;
      background: rgba(247, 251, 255, 0.85);
    }
    .timeline-tab-drawer.is-low  { max-height: 50px; }
    /* Full state now matches the map drawer's 175 px so the two visually
       balance — and the inner row scrolls if the content is taller than
       the drawer (more tags / updated-on line / future expansion). */
    .timeline-tab-drawer.is-full { max-height: 195px; }
    .timeline-tab-drawer-row {
      display: flex;
      flex-direction: column;
      gap: 6px;
      padding: 10px 14px 22px;        /* extra bottom padding clears the handle */
      font-size: 0.86rem;
      font-weight: 800;
      color: var(--tenki-blue);
      line-height: 1.2;
      letter-spacing: 0.15px;
      max-height: 175px;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
    }
    /* Scrollbar styling — slim + tenki-blue tinted, only shows when
       there's overflow.  Webkit + Firefox both supported. */
    .timeline-tab-drawer-row::-webkit-scrollbar { width: 4px; }
    .timeline-tab-drawer-row::-webkit-scrollbar-thumb {
      background: rgba(0, 90, 156, 0.3);
      border-radius: 2px;
    }
    .timeline-tab-drawer-row { scrollbar-width: thin; scrollbar-color: rgba(0,90,156,0.3) transparent; }
    .timeline-tab-drawer-title-row {
      display: flex;
      align-items: flex-start;
      gap: 6px;
    }
    .timeline-tab-drawer-icon {
      flex-shrink: 0;
      font-size: 0.92rem;
      line-height: 1.3;
    }
    .timeline-tab-drawer-text {
      flex: 1;
      /* Allow up to 2 lines with ellipsis fallback for very long titles */
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      white-space: normal;
      word-break: break-word;
      line-height: 1.2;
    }
    .timeline-tab-drawer-tags {
      display: flex;
      flex-wrap: wrap;
      gap: 4px 6px;
      padding-left: 19px;        /* aligns under the title text */
    }
    .timeline-tab-drawer-tags:empty { display: none; }
    /* Compact pill versions of phase/funding badges for the drawer.
       Use the same color tokens as the card-side badges so the visual
       language stays consistent across the two locations. */
    .ttd-tag {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      font-size: 0.78rem;          /* was 0.58 — bumped for readability */
      font-weight: 800;
      text-transform: uppercase;
      letter-spacing: 0.4px;
      padding: 5px 11px;            /* was 2px 7px — more breathing room */
      border-radius: 5px;
      /* Allow text to wrap when the badge would otherwise clip past
         the drawer's right edge — "PARTIALLY FUNDED · SCOPE LIMITED"
         was being truncated to "PARTIALLY FUNDED »" on narrow phone
         drawers.  word-break ensures the · separator can break a
         line cleanly without splitting individual words. */
      white-space: normal;
      word-break: break-word;
      max-width: 100%;
      line-height: 1.3;
    }
    .ttd-tag-icon { font-size: 0.85rem; line-height: 1; }
    .ttd-tag.phase-accomplished { background: var(--phase-accomplished-bg); color: var(--phase-accomplished-text); }
    .ttd-tag.phase-planning     { background: var(--phase-planning-bg);     color: var(--phase-planning-text); }
    .ttd-tag.phase-execution    { background: var(--phase-execution-bg);    color: var(--phase-execution-text); }
    .ttd-tag.phase-future       { background: var(--tenki-blue-light);      color: var(--tenki-blue); }
    .ttd-tag.funding-seeking    { background: #fff0f0; color: #d32f2f; border: 1px solid rgba(211, 47, 47, 0.2); }
    .ttd-tag.funding-partial    { background: #fff9e6; color: #e65100; border: 1px solid rgba(230, 81, 0, 0.2); }
    .ttd-tag.funding-full       { background: #e8f5e9; color: #2e7d32; border: 1px solid rgba(46, 125, 50, 0.2); }
    .ttd-tag.funding-active     { background: #e3f2fd; color: #0d47a1; border: 1px dashed #0d47a1; font-weight: 900; }
    /* When the funding tag is rendered as a clickable <button> (mobile
       drawer — see populateTimelineTabDrawer), strip the browser's
       default button chrome and add cursor + subtle hover so it reads
       as a tappable chip rather than a label. */
    button.ttd-tag {
      font-family: inherit; font-size: inherit; line-height: inherit;
      cursor: pointer;
      transition: transform .15s ease, box-shadow .15s ease, filter .15s ease;
      -webkit-tap-highlight-color: rgba(0, 90, 156, 0.15);
    }
    button.ttd-tag:active { transform: scale(0.95); }
    button.ttd-tag:hover  { filter: brightness(0.96); box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
    /* Tiny "tap me" affordance — a chevron after the funding badge
       text on mobile so the click target reads as actionable */
    button.ttd-tag.funding-badge::after {
      content: ' ›';
      font-weight: 800;
      opacity: 0.7;
      margin-left: 2px;
    }

    /* ── Pillar pill — shows which pillar this event belongs to ───────
       Colored by track (blue/green/black/white).  Sits alongside the
       phase + funding tags in the full drawer state.  Tap → eventually
       routes to the pillar's deep-dive page (not wired yet — placeholder). */
    .ttd-pillar-pill {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      font-size: 0.78rem;
      font-weight: 800;
      letter-spacing: 0.3px;
      padding: 5px 11px;
      border-radius: 5px;
      white-space: nowrap;
      line-height: 1.3;
      border: 1px solid transparent;
    }
    .ttd-pillar-pill.track-blue  { background: var(--tenki-blue-light);  color: var(--tenki-blue);  border-color: rgba(0,90,156,0.15); }
    .ttd-pillar-pill.track-green { background: var(--tenki-green-light); color: var(--tenki-green); border-color: rgba(0,140,69,0.15); }
    .ttd-pillar-pill.track-black { background: #f4f4f4;                  color: var(--tenki-black); border-color: rgba(0,0,0,0.10); }
    .ttd-pillar-pill.track-white { background: var(--tenki-white-light); color: var(--tenki-white-stroke); border-color: var(--tenki-white-border); }
    .ttd-pillar-pill-icon { font-size: 0.95rem; line-height: 1; }
    /* Pillar pill is a link → underline-on-hover + subtle shift */
    a.ttd-pillar-pill {
      text-decoration: none;
      transition: transform 0.15s ease, box-shadow 0.15s ease;
    }
    a.ttd-pillar-pill:hover,
    a.ttd-pillar-pill:active {
      transform: translateY(-1px);
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    }

    /* Updated/freshness chip — neutral gray pill with clock icon */
    .ttd-updated {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      font-size: 0.72rem;
      font-weight: 700;
      color: var(--text-light);
      background: rgba(0, 0, 0, 0.04);
      padding: 4px 10px;
      border-radius: 5px;
      letter-spacing: 0.2px;
      white-space: nowrap;
      line-height: 1.3;
    }
    .ttd-updated-icon { font-size: 0.85rem; }

    /* ── Engagement CTA — surfaced in the full drawer when the event
         has unfunded scope OR when the event's pillar has a Givebutter
         campaign.  Bold accent color (track-tinted) so it pulls the
         eye toward action.  Subtle pulse on first appearance so the
         user knows it's new + interactive. */
    .ttd-cta {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      width: 100%;
      padding: 9px 14px;
      margin-top: 4px;
      background: linear-gradient(135deg, var(--tenki-blue) 0%, #0073c4 100%);
      color: #fff;
      font-size: 0.85rem;
      font-weight: 800;
      letter-spacing: 0.4px;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      text-decoration: none;
      box-shadow: 0 3px 10px rgba(0, 90, 156, 0.22);
      transition: transform 0.15s ease, box-shadow 0.15s ease;
      -webkit-tap-highlight-color: transparent;
    }
    .ttd-cta:hover, .ttd-cta:active {
      transform: translateY(-1px);
      box-shadow: 0 5px 14px rgba(0, 90, 156, 0.32);
    }
    .ttd-cta-arrow { font-size: 1rem; font-weight: 900; }
    .ttd-cta.track-green { background: linear-gradient(135deg, var(--tenki-green) 0%, #00a352 100%);
                           box-shadow: 0 3px 10px rgba(0, 140, 69, 0.22); }
    .ttd-cta.track-green:hover { box-shadow: 0 5px 14px rgba(0, 140, 69, 0.32); }
    .ttd-cta.track-black { background: linear-gradient(135deg, var(--tenki-black) 0%, #333 100%);
                           box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22); }
    .ttd-cta.track-white { background: linear-gradient(135deg, #5f6c75 0%, #788591 100%);
                           box-shadow: 0 3px 10px rgba(95, 108, 117, 0.22); }
    /* First-appearance pulse to catch the eye when an event newly opens */
    @keyframes ttdCtaPulse {
      0%, 100% { transform: scale(1); box-shadow: 0 3px 10px rgba(0, 90, 156, 0.22); }
      50%      { transform: scale(1.03); box-shadow: 0 5px 16px rgba(0, 90, 156, 0.40); }
    }
    /* CTA pulse — single cycle then stops.  Two cycles + the drawer
       handle pulse combined was overwhelming.  One pulse is enough
       to draw the eye on first appearance. */
    .ttd-cta.is-fresh { animation: ttdCtaPulse 1.6s ease-in-out 1; }
    @media (prefers-reduced-motion: reduce) {
      .ttd-cta.is-fresh { animation: none; }
    }

    /* When map view is active, hide BOTH drawers — full map is visible. */
    .dashboard-container.mobile-map-view .map-tab-drawer,
    .dashboard-container.mobile-map-view .timeline-tab-drawer { display: none !important; }

    /* ── Mobile Tab Bar ──────────────────────────────── */
    .mobile-tab-bar {
      display: flex; flex-shrink: 0; position: relative; z-index: 20;
      background: #fff; border-bottom: 1px solid var(--timeline-line);
      order: 4; /* Sits between controls (order 3) and timeline content */
    }
    .mobile-tab {
      flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
      padding: 10px 8px; border: none; background: none; cursor: pointer;
      font-size: 0.85rem; font-weight: 600; color: var(--text-light);
      border-bottom: 3px solid transparent;
      transition: color 0.2s ease, border-color 0.2s ease;
    }
    .mobile-tab.active { color: var(--tenki-blue); border-bottom-color: var(--tenki-blue); }
    .mobile-tab svg { flex-shrink: 0; }

    /* Map tab active: show map, hide timeline */
    .dashboard-container.mobile-map-view .map-interface-grid {
      display: flex !important;
      flex: 1 1 auto !important;
      /* Was height:auto — but auto in flex contexts can collapse if no
         child has intrinsic height.  100% so it fills the parent
         dashboard-container's allotted vertical space. */
      height: 100% !important;
      min-height: 0 !important;
      position: relative !important;
    }
    .dashboard-container.mobile-map-view .map-canvas-wrap {
      /* canvas-wrap was added recently for the DESKTOP tap-to-engage
         overlay.  On mobile it now also wraps the leaflet canvas
         when the map view is active — make it fill the parent. */
      flex: 1 1 auto !important;
      width: 100% !important;
      height: 100% !important;
      min-height: 0 !important;
    }
    .dashboard-container.mobile-map-view #leaflet-map-canvas {
      width: 100% !important;
      height: 100% !important;
      min-height: 280px !important;
    }
    .dashboard-container.mobile-map-view .timeline-outer-wrapper { display: none !important; }

    /* "Back to Timeline" affordance on the mobile map view.  Pinned at
       the top-left of the map area with a pulsing right-arrow + label,
       plus a one-time "Swipe right to go back" hint that fades after a
       few seconds.  Combined, this teaches the visitor BOTH the visual
       click affordance AND the gesture so they don't get stuck on map. */
    /* The old top-left "← Back to Timeline" chip + hint were replaced
       by the tall vertical drag-handle declared in HTML at the top of
       the dashboard-container.  Keeping these pseudo-element rules
       commented (not deleted) so a future tweak can revive them.
       .dashboard-container.mobile-map-view::before { … }
       .dashboard-container.mobile-map-view::after  { … } */

    /* Mobile-map back-handle — thick vertical bar pinned to the left
       edge of the viewport, visible ONLY in mobile-map-view.  This is
       now a SIMPLE tap target: tap anywhere on the bar to return to
       the timeline.  Removed the drag-to-dismiss gesture (was getting
       hijacked by the mobile browser's back-swipe), the grip lines,
       and the continuous nudge animation (was annoying).  The label
       reads clearly "TAP TO RETURN" so the affordance is obvious. */
    .mobile-map-back-handle {
      display: none;
      position: fixed;
      top: 50%;
      left: 0;
      transform: translateY(-50%);
      width: 44px;                     /* was 22 — twice the tap target width */
      height: 60vh;
      max-height: 420px;
      min-height: 240px;
      padding: 14px 4px;
      border: 0;
      border-radius: 0 14px 14px 0;
      background: linear-gradient(135deg, rgba(0, 90, 156, 0.96) 0%, rgba(0, 115, 196, 0.96) 100%);
      color: #fff;
      cursor: pointer;
      z-index: 9500;
      box-shadow: 4px 0 18px rgba(0, 0, 0, 0.28), 0 0 0 1px rgba(0, 0, 0, 0.08);
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      align-items: center;
      justify-content: center;
      flex-direction: column;
      gap: 8px;
      /* touch-action: manipulation = enable taps, suppress the
         browser's double-tap zoom / edge-swipe-back interpretation. */
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
      transition: background .15s ease, box-shadow .15s ease,
                  width .15s ease;
      /* No animation — the user found the periodic nudge annoying. */
    }
    .mobile-map-back-handle:active {
      background: linear-gradient(135deg, rgba(0, 80, 140, 1) 0%, rgba(0, 100, 175, 1) 100%);
      width: 48px;                     /* subtle press feedback */
    }
    .dashboard-container.mobile-map-view .mobile-map-back-handle { display: flex; }
    /* When the user scrolls AWAY from the timeline section, hide the
       back-handle even if mobile-map-view is still active.  The handle
       is position:fixed, so without this it stayed pinned to the
       viewport on every other section of the page (the user-reported
       bug).  Body class is toggled by the IntersectionObserver in
       bindMapViewBackAffordances. */
    body.tenki-back-handle-out .mobile-map-back-handle {
      display: none !important;
    }

    /* Big vertical "TAP TO RETURN" label so the affordance is obvious.
       Bigger + bolder than the old grip-line + label combo. */
    .mobile-map-back-handle-label {
      writing-mode: vertical-rl;
      transform: rotate(180deg);
      font-size: 0.82rem;
      font-weight: 900;
      letter-spacing: 1.6px;
      text-transform: uppercase;
      color: #fff;
      white-space: nowrap;
      text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    }

    /* Map slides DOWN onto the screen when entering mobile-map-view.
       Same direction as the explore-overlay's downward motion, so the
       gesture vocabulary is consistent: down = enter, right-swipe = exit. */
    @keyframes mobileMapSlideDown {
      from { transform: translateY(-100%); }
      to   { transform: translateY(0); }
    }
    .dashboard-container.mobile-map-view .map-interface-grid {
      animation: mobileMapSlideDown .32s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    }
    /* And when we leave map mode → slide RIGHT off the screen so the
       motion mirrors what the back-handle promises.  CSS class added
       just before classList.remove('mobile-map-view') (see the JS at
       line ~3415). */
    @keyframes mobileMapSlideRight {
      from { transform: translateX(0); }
      to   { transform: translateX(110%); }
    }
    .dashboard-container.mobile-map-leaving .map-interface-grid {
      animation: mobileMapSlideRight .28s cubic-bezier(0.55, 0.06, 0.68, 0.19) both;
    }

    @media (prefers-reduced-motion: reduce) {
      .mobile-map-back-handle { animation: none; }
      .dashboard-container.mobile-map-view .map-interface-grid,
      .dashboard-container.mobile-map-leaving .map-interface-grid { animation: none; }
    }

    /* Map tab: keep the nav bar visible so users can navigate pins + filter tracks.
       Re-enable the dropdown menu and chevron (both suppressed on the timeline tab). */
    .dashboard-container.mobile-map-view .floating-nav-menu { display: flex !important; }
    .dashboard-container.mobile-map-view .all-tracks-reset-btn svg { display: inline-flex !important; }

    /* Map tab discovery hint — sits between tab bar and timeline content */
    .mobile-tab-hint {
      display: block; font-size: 0.68rem; font-weight: 700; color: var(--tenki-blue);
      letter-spacing: 0.3px; text-align: center; padding: 4px 0 3px;
      opacity: 0; transition: opacity 0.4s ease; pointer-events: none;
      flex-shrink: 0; order: 4; width: 100%; background: #f0f4f8;
      border-bottom: 1px solid rgba(0,90,156,0.1);
    }
    .mobile-tab-hint.visible { opacity: 1; }

    /* Push timeline below the hint strip (hint order: 4, tab bar: 4 earlier in DOM, timeline: 5) */
    .timeline-outer-wrapper { order: 5 !important; }
  }

  /* ── Mobile Landscape Layout ─────────────────────── */
  @media (max-width: 1024px) and (max-height: 500px) and (orientation: landscape) {
    .mobile-tab-bar { display: none !important; }

    .dashboard-container {
      display: grid !important;
      grid-template-areas: "header map" "controls map" "timeline map";
      grid-template-columns: 52% 48%;
      grid-template-rows: auto auto 1fr;
      height: 100dvh !important; max-height: 100dvh !important;
      overflow: hidden; padding: 0 !important; border-radius: 0 !important;
    }

    .dashboard-header { grid-area: header; padding: 6px 12px 4px !important; }
    .dashboard-header h2 { font-size: 0.95rem !important; }
    .dashboard-controls { grid-area: controls; }
    #system-state { grid-column: 1 / -1; }

    .map-interface-grid {
      grid-area: map; display: flex !important; flex-direction: column !important;
      height: 100% !important; flex: none !important; width: 100% !important;
      position: relative !important; border-bottom: none !important;
      border-left: 2px solid var(--timeline-line);
    }

    .timeline-outer-wrapper {
      grid-area: timeline; display: flex !important; position: relative !important;
      height: 100% !important; overflow: hidden !important; flex: none !important;
    }

    .floating-nav-container { display: none !important; }

    /* In landscape, always show both panels regardless of tab state */
    .dashboard-container.mobile-map-view .map-interface-grid { display: flex !important; }
    .dashboard-container.mobile-map-view .timeline-outer-wrapper { display: flex !important; }
    .dashboard-container.mobile-map-view .floating-nav-container { display: none !important; }
  }

  /* ── Funding Detail Modal ──────────────────────── */
  .funding-modal-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 9100; display: none;
    align-items: center; justify-content: center;
    padding: 20px;
  }
  .funding-modal-overlay.open { display: flex; }
  .funding-modal-panel {
    background: #fff; border-radius: 16px;
    max-width: 560px; width: 100%;
    max-height: 85vh; overflow-y: auto;
    padding: 28px; position: relative;
    box-shadow: 0 8px 40px rgba(0,0,0,0.2);
  }
  .funding-modal-close {
    position: absolute; top: 16px; right: 16px;
    background: none; border: none; cursor: pointer;
    font-size: 1.5rem; color: #555; line-height: 1;
  }
  .funding-modal-title { font-size: 1.1rem; font-weight: 700; margin: 0 0 4px; }
  .funding-modal-subtitle { font-size: 0.85rem; color: var(--text-light); margin: 0 0 20px; }
  .funding-chart-wrap { margin: 0 0 20px; }
  .funding-chart-label {
    display: flex; justify-content: space-between;
    font-size: 0.8rem; color: var(--text-light); margin-bottom: 6px;
  }
  .funding-bar-track {
    background: #e2e8f0; border-radius: 6px; height: 18px;
    overflow: hidden; margin-bottom: 8px;
  }
  .funding-bar-fill { height: 100%; border-radius: 6px; transition: width 0.8s ease; }
  .funding-bar-fill.raised { background: var(--tenki-green); }
  .funding-bar-fill.goal { background: var(--tenki-blue); }
  .funding-bar-fill.donated { background: #f59e0b; }
  .impact-metrics-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 12px; margin-bottom: 20px;
  }
  .impact-metric-card {
    background: var(--bg-light); border-radius: 10px;
    padding: 12px 10px; text-align: center;
  }
  .impact-metric-icon { font-size: 1.4rem; }
  .impact-metric-value { font-size: 1.15rem; font-weight: 700; color: var(--text-dark); }
  .impact-metric-label { font-size: 0.72rem; color: var(--text-light); }
  .impact-section-heading {
    font-size: 0.85rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.5px; color: var(--text-light); margin: 0 0 8px;
  }
  .impact-bullets { list-style: none; padding: 0; margin: 0 0 20px; }
  .impact-bullets li {
    padding: 4px 0 4px 20px; position: relative;
    font-size: 0.875rem; color: var(--text-dark);
  }
  .impact-bullets li::before { content: '✓'; position: absolute; left: 0; color: var(--tenki-green); font-weight: 700; }
  /* Team-members-involved strip on the event detail / funding popout.
     Photos are sourced from the cross-joined Team sheet; clicking jumps
     to /about-1#team-member-<slug>.  Tightly packed avatars with the
     name underneath — same shape as the org chart thumbnails. */
  .event-team-strip { display: flex; flex-wrap: wrap; gap: 12px;
                       margin: 0 0 20px; }
  .event-team-member { display: flex; flex-direction: column; align-items: center;
                        gap: 4px; padding: 6px 4px; border-radius: 8px;
                        text-decoration: none; color: inherit; cursor: pointer;
                        min-width: 78px;
                        transition: transform .25s cubic-bezier(0.175, 0.885, 0.32, 1.275),
                                    background-color .2s ease; }
  .event-team-member:hover { transform: translateY(-2px); background-color: #f0f7fb; }
  .event-team-member img,
  .event-team-avatar-fallback {
    width: 56px; height: 56px; border-radius: 50%;
    object-fit: cover; object-position: center top;
    background: #ddd; border: 2px solid #fff;
    box-shadow: 0 2px 6px rgba(0,0,0,0.15);
  }
  .event-team-avatar-fallback { display: flex; align-items: center; justify-content: center;
                                  background: #3BA9E8; color: #fff; font-weight: 700;
                                  font-size: 18px; }
  .event-team-name { font-size: 11px; font-weight: 600; color: #333;
                      text-align: center; max-width: 76px; line-height: 1.2; }
  /* Members in the sheet without a matching Team row — render dimmed so
     the editor sees the typo / rename without breaking the rest of the strip. */
  .event-team-member.missing img,
  .event-team-member.missing .event-team-avatar-fallback {
    opacity: 0.4; filter: grayscale(1);
  }
  .event-team-member.missing .event-team-name { color: #b91c1c; font-style: italic; }

  /* "Powered By" partner strip — sibling of the team strip but with org
     logos in a wider plate.  Clicking opens the partner's website. */
  .event-partners-strip { display: flex; flex-wrap: wrap; gap: 14px;
                            margin: 0 0 20px; }
  .event-partner { display: flex; flex-direction: column; align-items: center;
                     gap: 4px; padding: 8px 10px; border-radius: 8px;
                     text-decoration: none; color: inherit; cursor: pointer;
                     min-width: 110px; max-width: 150px;
                     background: #fafafa; border: 1px solid #f0f0f0;
                     transition: transform .25s cubic-bezier(0.175, 0.885, 0.32, 1.275),
                                 box-shadow .25s ease, background-color .2s ease; }
  .event-partner:hover { transform: translateY(-2px);
                          box-shadow: 0 6px 14px rgba(0,0,0,.06);
                          background: #fff; }
  .event-partner img,
  .event-partner-logo-fallback {
    width: 88px; height: 50px; object-fit: contain;
    background: #fff; border-radius: 4px;
    padding: 4px;
  }
  .event-partner-logo-fallback {
    display: flex; align-items: center; justify-content: center;
    color: #1A1A1A; font-weight: 700; font-size: 11px;
    text-align: center; line-height: 1.1;
  }
  .event-partner-name { font-size: 11px; font-weight: 600; color: #333;
                          text-align: center; max-width: 130px; line-height: 1.2; }
  .event-partner-tier { font-size: 9px; font-weight: 700;
                          text-transform: uppercase; letter-spacing: .4px;
                          color: #888; }
  .event-partner.missing img,
  .event-partner.missing .event-partner-logo-fallback {
    opacity: 0.4; filter: grayscale(1);
  }
  .event-partner.missing .event-partner-name { color: #b91c1c; font-style: italic; }
  .scope-missed-box {
    background: #fff5f5; border-left: 4px solid #e53e3e;
    border-radius: 6px; padding: 12px 14px; margin-bottom: 20px;
  }
  .scope-missed-box h4 { font-size: 0.85rem; color: #c53030; margin: 0 0 6px; }
  .scope-missed-box p { font-size: 0.85rem; color: var(--text-dark); margin: 0; }
  .funding-modal-cta {
    display: flex; align-items: center; justify-content: center;
    width: 100%; padding: 14px;
    border: none; border-radius: 10px; cursor: pointer;
    font-size: 1rem; font-weight: 700; color: #fff;
    text-align: center; text-decoration: none; margin: 8px auto 0;
    box-sizing: border-box;
  }
  .funding-modal-cta.donate { background: var(--tenki-blue); }
  .funding-modal-cta.collaborate { background: var(--tenki-green); }
  .funding-disclaimer {
    font-size: 0.71rem; color: var(--text-light);
    text-align: center; margin: 10px 0 4px; font-style: italic;
  }

  /* ── CTA Return Toast ─────────────────────── */
  .link-toast {
    position: fixed; bottom: 30px; left: 50%;
    transform: translateX(-50%) translateY(20px);
    background: var(--tenki-black); color: #fff;
    border-radius: 30px; padding: 11px 22px;
    display: flex; align-items: center; gap: 12px;
    font-size: 0.82rem; font-weight: 600;
    box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    z-index: 9500; opacity: 0; pointer-events: none;
    transition: transform 0.3s ease, opacity 0.3s ease;
    max-width: 92vw;
  }
  .link-toast.visible { transform: translateX(-50%) translateY(0); opacity: 1; pointer-events: auto; }
  .link-toast-close { background: none; border: none; color: rgba(255,255,255,0.65); cursor: pointer; font-size: 1.1rem; line-height: 1; padding: 0; }
  .link-toast-close:hover { color: #fff; }

  /* ── CTA hint below button group ─────────── */
  .cta-hint {
    font-size: 0.71rem; color: var(--text-light);
    text-align: center; margin: 6px 0 0; letter-spacing: 0.2px;
  }

/* ─── funnel: funnel/funnel.html (sha=2707e08) ─── */
/* Base Container Setup */
  .tenki-funnel-container {
    max-width: 1200px;
    /* Tighter bottom margin so the social section's photo background
       (which extends UP behind us via a negative top margin) reads as
       a continuous bleed instead of a wide empty gap. */
    margin: 32px auto 18px;
    padding: 30px;
    background-color: #F8F9FA;
    border-radius: 12px;
    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    box-sizing: border-box;
    box-shadow: 0 8px 25px rgba(0,0,0,0.02);
    position: relative;
    /* Bottom-edge mask: fades the funnel card transparent over the
       last ~8% so the social-section photo behind it (extending up
       via negative margin) reads THROUGH the bottom of the funnel
       — creating the soft blend the user requested.  The rounded
       corners at the bottom disappear when mask fades them out, but
       the dissolve INTO the photo actually reads as more intentional
       than a hard-edged card sitting on the bleed. */
    -webkit-mask-image: linear-gradient(to bottom,
                          black 0%, black 92%, transparent 100%);
            mask-image: linear-gradient(to bottom,
                          black 0%, black 92%, transparent 100%);
  }

  .tenki-funnel-container *,
  .tenki-funnel-container *::before,
  .tenki-funnel-container *::after {
    box-sizing: inherit;
  }

  /* Build chip — hidden by default, shown only on ?debug=1.  The shared
     showBuildChip() script (added inline below) flips it on. */
  .tenki-funnel-build-chip {
    position: absolute; top: 8px; right: 12px;
    font-size: 10px; font-family: ui-monospace, SFMono-Regular, monospace;
    color: #6b7280; background: #fff; padding: 2px 6px; border-radius: 4px;
    border: 1px solid #e5e7eb; display: none;
  }
  .tenki-funnel-container.show-build .tenki-funnel-build-chip { display: inline-block; }

  /* Funnel Header Structure */
  .funnel-header {
    text-align: center;
    margin-bottom: 30px;
  }

  /* Logo banner — respects the Tenki mark's natural 3:1 aspect ratio
     (1536 × 512 source).  No inner padding since the new logo already
     has its own black background + green rounded border baked in, so
     it fills the box edge-to-edge.  object-fit:contain prevents any
     distortion if the underlying asset's aspect ratio is ever swapped. */
  .funnel-logo-box {
    position: relative;
    width: clamp(180px, 24vw, 240px);
    aspect-ratio: 3 / 1;
    margin: 0 auto 14px auto;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.10);
    isolation: isolate;            /* contain z-index stack */
  }
  .funnel-logo-box img {
    position: relative;
    z-index: 2;
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
  }

  /* ── Sierra-Leone flag backdrop (hint layer) ──────────────────────
     The original Tenki mark didn't show its SL-flag-color heritage so
     we drew a fluttering green/white/blue banner behind it for
     ambience.  The NEW mark bakes those colors directly into the
     letters (green T-e, white n, blue ki) on a solid black ground,
     so the banner behind it is mostly hidden.  Kept at very low
     opacity as a hint layer that peeks around if a future logo asset
     uses transparency — invisible otherwise.  Trivial to crank back
     up to 0.18 if you ever want the flag to read through the logo. */
  .funnel-flag-bg {
    position: absolute;
    inset: -10%;
    z-index: 1;
    background: linear-gradient(
      to bottom,
      #1eb53a 0%,    #1eb53a 33.3%,
      #ffffff 33.3%, #ffffff 66.6%,
      #0072c6 66.6%, #0072c6 100%
    );
    opacity: 0.08;                 /* faint hint — logo's bg covers it */
    filter: blur(0.8px);
    transform-origin: center;
    animation: tenkiFlagFlutter 7s ease-in-out infinite;
    pointer-events: none;
  }
  /* Tiny shear + scale loop simulates fabric sway.  Kept under ±2°
     and ±1.5% so it's "subtle" — not a swinging banner. */
  @keyframes tenkiFlagFlutter {
    0%   { transform: skewX(-1.5deg) skewY(0.3deg) scale(1.015); }
    25%  { transform: skewX( 0.8deg) skewY(-0.4deg) scale(1.008); }
    50%  { transform: skewX( 1.6deg) skewY(0.5deg) scale(1.020); }
    75%  { transform: skewX(-0.4deg) skewY(-0.3deg) scale(1.010); }
    100% { transform: skewX(-1.5deg) skewY(0.3deg) scale(1.015); }
  }
  /* Honor the user's reduced-motion preference — flag stays static */
  @media (prefers-reduced-motion: reduce) {
    .funnel-flag-bg { animation: none; }
  }

  .funnel-header h2 {
    color: #1A1A1A;
    font-size: 2rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin: 0 0 10px 0;
    font-weight: 800;
  }

  .funnel-header-divider {
    width: 50px;
    height: 4px;
    background-color: #008C45;
    margin: 0 auto 15px auto;
    border-radius: 2px;
  }

  .funnel-intro {
    color: #555555;
    font-size: 1.05rem;
    max-width: 680px;
    margin: 0 auto;
    line-height: 1.6;
  }

  .funnel-inline-link {
    color: #005A9C;
    text-decoration: underline;
    font-weight: 700;
    transition: color 0.2s ease;
  }

  .funnel-inline-link:hover {
    color: #008C45;
  }

  /* Grid Layout mechanics */
  .funnel-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
    margin-bottom: 30px;
  }

  /* Compact Card Rules */
  .funnel-card {
    background-color: #FFFFFF;
    border-radius: 10px;
    padding: 24px;
    text-decoration: none;
    display: flex;
    flex-direction: column;
    position: relative;
    border-top: 5px solid #1A1A1A;
    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), box-shadow 0.3s ease;
  }

  /* Theme Mapping */
  .card-blue-track {
    border-top-color: #005A9C;
    background: linear-gradient(180deg, rgba(0, 90, 156, 0.03) 0%, rgba(255, 255, 255, 1) 120px);
    box-shadow: 0 4px 15px rgba(0, 90, 156, 0.04);
  }
  .card-green-track {
    border-top-color: #008C45;
    background: linear-gradient(180deg, rgba(0, 140, 69, 0.03) 0%, rgba(255, 255, 255, 1) 120px);
    box-shadow: 0 4px 15px rgba(0, 140, 69, 0.04);
  }
  .card-black-track {
    border-top-color: #1A1A1A;
    background: linear-gradient(180deg, rgba(26, 26, 26, 0.02) 0%, rgba(255, 255, 255, 1) 120px);
    box-shadow: 0 4px 15px rgba(26, 26, 26, 0.03);
  }

  .funnel-card:hover {
    transform: translateY(-4px);
  }
  .card-blue-track:hover  { box-shadow: 0 10px 20px rgba(0, 90, 156, 0.1); }
  .card-green-track:hover { box-shadow: 0 10px 20px rgba(0, 140, 69, 0.1); }
  .card-black-track:hover { box-shadow: 0 10px 20px rgba(26, 26, 26, 0.08); }

  /* ── Pillar logo placeholder slot ─────────────────────────────────
     Reserved spot at the top of each pillar card for a future per-
     pillar logo.  The slot is sized to gracefully accept either a
     circle badge OR a banner-style mark like the Tenki master logo:
       • Square / circle:  72 × 72 px (centered, max 64 × 64 inner)
       • Wide banner:      up to ~140 × 56 px (logo's natural ratio)
     Today the slot displays the existing pillar emoji styled as a
     soft tinted circle.  When real logos arrive, replace the inner
     placeholder span with <img class="pillar-logo-img" ...> and the
     slot styles do the rest. */
  .pillar-logo-slot {
    position: relative;
    width: 72px;
    height: 72px;
    margin: 0 auto 14px auto;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    /* Subtle inner border + ring so the slot reads as "intentional
       brand mark" not "empty container".  Track color comes from the
       card's parent .card-*-track override below. */
    background: rgba(0, 0, 0, 0.02);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.04),
                0 2px 6px rgba(0, 0, 0, 0.04);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
  }
  .funnel-card:hover .pillar-logo-slot { transform: scale(1.04); }
  /* Per-track tints for the placeholder background */
  .card-blue-track  .pillar-logo-slot { background: rgba(0, 90, 156, 0.07); }
  .card-green-track .pillar-logo-slot { background: rgba(0, 140, 69, 0.07); }
  .card-black-track .pillar-logo-slot { background: rgba(26, 26, 26, 0.05); }

  /* Inner placeholder — the emoji that lives in the slot until a real
     logo arrives.  Sized + colored so it doesn't look like a hack. */
  .pillar-logo-placeholder {
    font-size: 1.9rem;
    line-height: 1;
    filter: saturate(0.95);
    pointer-events: none;
    user-select: none;
  }
  /* Future <img> replacement — use this class once the asset lands.
     `object-fit: contain` works for both circle badges and banner
     marks because the slot's 72 × 72 frame has equal inner padding. */
  .pillar-logo-slot .pillar-logo-img {
    max-width: 88%;
    max-height: 88%;
    object-fit: contain;
    display: block;
  }
  /* If the future asset is explicitly a wide banner (like Tenki's
     master mark), opt it into a wider crop with the .pillar-logo-img
     --wide modifier. */
  .pillar-logo-slot .pillar-logo-img.wide {
    max-width: 94%;
    max-height: 56%;
  }

  .card-top-accent { margin-bottom: 12px; }

  .pill-amount {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 3px 8px;
    border-radius: 4px;
  }

  .card-blue-track .pill-amount  { background-color: #EEF5FB; color: #005A9C; }
  .card-green-track .pill-amount { background-color: #E8F5E9; color: #008C45; }
  .card-black-track .pill-amount { background-color: #F4F4F4; color: #1A1A1A; }

  .funnel-card h3 {
    margin: 0 0 8px 0;
    font-size: 1.15rem;
    color: #1A1A1A;
    font-weight: 700;
    line-height: 1.3;
  }

  .impact-desc {
    font-size: 0.9rem;
    line-height: 1.45;
    color: #555555;
    margin: 0 0 15px 0;
  }

  .card-footer-action {
    margin-top: auto;
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: flex;
    align-items: center;
    gap: 4px;
    transition: gap 0.2s ease;
  }

  .card-blue-track .card-footer-action  { color: #005A9C; }
  .card-green-track .card-footer-action { color: #008C45; }
  .card-black-track .card-footer-action { color: #1A1A1A; }

  .card-footer-action::after { content: '→'; font-size: 0.9rem; }
  .funnel-card:hover .card-footer-action { gap: 8px; }

  /* Master CTA Layout block */
  .funnel-cta-banner {
    background-color: #1A1A1A;
    border-radius: 10px;
    padding: 25px 30px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 25px;
    box-shadow: 0 6px 20px rgba(0,0,0,0.12);
  }

  .banner-text { flex: 1; }

  .banner-text h4 {
    margin: 0 0 6px 0;
    font-size: 1.25rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #008C45;
    font-weight: 800;
  }

  .banner-text p {
    margin: 0;
    font-size: 0.92rem;
    line-height: 1.5;
    color: #E2E8F0;
  }

  /* Payment methods affordance — text only, no logos so it stays small
     and doesn't trigger trademark issues.  Lives under the trust copy. */
  .banner-methods {
    margin-top: 6px !important;
    font-size: 0.82rem !important;
    color: #A0AEC0 !important;
    letter-spacing: 0.3px;
  }

  .master-donate-btn {
    display: inline-block;
    padding: 12px 28px;
    background-color: #008C45;
    color: #FFFFFF;
    text-decoration: none;
    border-radius: 30px;
    font-weight: 800;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
    border: 2px solid #008C45;
  }

  .master-donate-btn:hover {
    transform: translateY(-2px);
    background-color: transparent;
    color: #008C45;
    box-shadow: 0 4px 12px rgba(0, 140, 69, 0.25);
  }

  /* Mobile responsive adaptations */
  @media (max-width: 900px) {
    .funnel-cta-banner {
      flex-direction: column;
      text-align: center;
      padding: 20px;
    }
    .master-donate-btn { width: 100%; text-align: center; }
  }

  @media (max-width: 768px) {
    .tenki-funnel-container { padding: 20px 15px; margin: 25px auto; }
    .funnel-header h2 { font-size: 1.5rem; }
    .funnel-intro    { font-size: 0.92rem; }
    .funnel-grid     { grid-template-columns: 1fr; gap: 15px; }
    .banner-methods  { font-size: 0.76rem !important; }
    /* Card CTAs get a 44px touch target on phones */
    .funnel-card { padding: 22px 18px; min-height: 44px; }
    .master-donate-btn { padding: 14px 28px; }
  }

/* ─── social: social/social.html (sha=2707e08) ─── */
/* Tenki — Social & Feeds section
     Lives below the timeline embed on the homepage.  Self-contained
     scoped styles (prefix `tenki-social-` and `fb-`) so it can't bleed
     into other Code Blocks. */
  .tenki-social-parallax-section {
    --tenki-blue: #005A9C;
    --tenki-green: #008C45;
    --tenki-black: #1A1A1A;
    --tenki-white: #FFFFFF;
    --text-light: #555555;

    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    position: relative;
    /* Tight internal padding so the section visually butts up against
       the timeline above without a chasm.  Top padding minimal (20 px)
       so the photographer's "pointing into the sky" composition lands
       right under the timeline; bottom padding slightly larger so the
       fade-out doesn't crash into whatever comes next.

       NO negative top margin.  We had `margin: -16px -50vw 0` for a
       while to close a perceived gap between this section and the
       timeline above, but because this section has
       `isolation: isolate; z-index: 1` (creating its own stacking
       context) AND the masked background image fills the section
       absolutely from top:0, the 16-pixel pull-up caused the bg image
       to *paint over* the bottom of the timeline.  The Squarespace
       section above already provides comfortable padding; if a gap
       reappears it's better solved via the `docs/squarespace-page-
       overrides.md` PHCI snippet (zero out section padding for the
       timeline + social block-IDs) than by overlapping siblings here.

       `overflow: hidden` is belt-and-suspenders: even if a child ever
       tries to extend past the top edge (e.g. an absolutely-positioned
       decoration), it gets clipped at the section bounds. */
    /* Padding-top + negative margin-top combo extends the section
       upward so the photo background bleeds UP behind the bottom of
       the funnel section above.  The funnel container has a bottom
       mask-fade (see funnel.html) so its #F8F9FA backdrop dissolves
       INTO this photo in the overlap, instead of painting over it.
       Bottom padding unchanged. */
    padding: 160px 20px 40px;
    margin: -150px -50vw 0;
    box-sizing: border-box;
    width: 100vw;
    left: 50%;
    right: 50%;
    overflow: hidden;
    /* Isolate stacking context so the next section below can't paint
       into ours (same defensive pattern as .dashboard-container). */
    isolation: isolate;
    z-index: 1;
  }

  .tenki-social-masked-bg {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    background-repeat: no-repeat;
    background-size: cover;
    background-position: center center;
    z-index: 1;
    /* Critical for the top overlap with the funnel section: even
       though the mask makes the top 15 % transparent, the div is
       still a hit target.  Without this the masked-out region
       would intercept clicks on whatever funnel content sits in
       the same coordinate space (donate CTA, pillar chips). */
    pointer-events: none;
    /* Mask: very subtle top softening (top 4 % fades in) so the photo
       has a soft edge where it meets the funnel's bottom-fade above,
       but the photo is OPAQUE throughout the bulk of the overlap
       region — that's the change vs. the previous 15 % top fade.
       Previously the overlap zone was masked transparent, making the
       photo invisible exactly where we wanted it visible behind the
       funnel.  Bottom 12 % fade is unchanged — section still blends
       down into the newsletter below. */
    -webkit-mask-image: linear-gradient(to bottom,
                          transparent 0%, black 4%, black 88%, transparent 100%);
            mask-image: linear-gradient(to bottom,
                          transparent 0%, black 4%, black 88%, transparent 100%);
  }

  .tenki-social-inner-content {
    position: relative;
    z-index: 2;
    max-width: 1000px;
    margin: 0 auto;
  }

  /* Inline mini-Tenki-logo inside the FB/IG card subtitles.
     Replaces the old large logo banner that used to sit at the top
     of the section — that banner was redundant against the footer
     logo on the same page and competed with the photo background
     for attention.  The inline placement brands the feed cards as
     specifically Tenki's (vs. generic Facebook / Instagram) while
     taking up only ~22 px of vertical space and letting the photo
     above breathe.

     The drop-shadow gives the logo a little lift off the white
     card surface — same "depth" treatment we used to apply to the
     banner version, just scaled down. */
  .fb-inline-tenki {
    display: inline-block;
    height: 22px;
    width: auto;
    vertical-align: -6px;        /* baseline tweak — matches the cap height of the subtitle */
    margin: 0 4px;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.22))
            drop-shadow(0 1px 1px rgba(0, 0, 0, 0.14));
    /* PNG is already RGBA with transparent corners post-crop, so no
       background / border-radius needed — the alpha channel handles
       the rounded look against the card's white surface. */
  }
  @media (max-width: 768px) {
    .fb-inline-tenki { height: 18px; vertical-align: -5px; }
  }

  /* Quote card */
  .tenki-social-quote-container {
    background-color: var(--tenki-white);
    border-radius: 16px;
    padding: 32px 40px;
    box-shadow: 0 15px 45px rgba(0, 0, 0, 0.06);
    text-align: center;
    position: relative;
    margin-bottom: 30px;
    border-top: 5px solid var(--tenki-blue);
  }
  .tenki-social-quote-container::before {
    content: '"';
    position: absolute;
    top: -15px; left: 40px;
    font-size: 140px;
    color: rgba(0, 90, 156, 0.04);
    font-family: Georgia, serif;
    line-height: 1;
  }
  .tenki-social-quote-text {
    font-size: 1.6rem;
    line-height: 1.5;
    color: var(--tenki-black);
    font-weight: 600;
    font-style: italic;
    margin: 0 0 10px 0;
  }
  .tenki-social-quote-author {
    font-size: 1.1rem;
    color: var(--tenki-blue);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1px;
    margin: 0;
  }

  /* Feed columns */
  .tenki-feeds-grid-wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    align-items: start;
  }
  .tenki-feed-column-card {
    background-color: var(--tenki-white);
    border-radius: 16px;
    box-shadow: 0 15px 40px rgba(0, 0, 0, 0.05);
    box-sizing: border-box;
    width: 100%;
    border-top: 6px solid var(--tenki-blue);
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
  }
  .tenki-feed-column-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 18px 45px rgba(0, 0, 0, 0.08);
  }
  .tenki-feed-column-card.ig-card { border-top-color: var(--tenki-green); }

  .fb-card-header-trigger {
    display: flex;
    align-items: center;
    gap: 15px;
    padding: 25px;
    cursor: pointer;
    user-select: none;
    transition: background-color 0.2s ease;
    min-height: 48px;  /* touch target */
  }
  .fb-card-header-trigger:hover { background-color: #FAFAFA; }
  .fb-icon-circle {
    width: 45px;
    height: 45px;
    border-radius: 50%;
    background-color: #eef5fb;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    flex-shrink: 0;
  }
  /* Platform icons rendered via CSS `content:` instead of literal
     emoji characters in the HTML markup.  Lives in the CDN-served
     CSS (correct UTF-8 headers always) so Squarespace's Code Block
     paste can't mojibake the icons even when it mangles inline
     unicode in the shell.  \1F4F1 = 📱, \1F4F8 = 📸. */
  .fb-icon-circle[data-platform-icon="facebook"]::before  { content: '\1F4F1'; }
  .fb-icon-circle[data-platform-icon="instagram"]::before { content: '\1F4F8'; }
  .tenki-feed-column-card.ig-card .fb-icon-circle { background-color: #eefbf3; }
  .fb-header-text { flex-grow: 1; }
  .fb-header-text h3 {
    margin: 0;
    color: var(--tenki-black);
    font-size: 1.15rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
  }
  .fb-header-text p {
    margin: 2px 0 0 0;
    color: var(--text-light);
    font-size: 0.85rem;
    font-weight: 600;
    transition: color 0.2s ease;
  }
  .tenki-feed-column-card:hover .fb-header-text p { color: var(--tenki-blue); }
  .tenki-feed-column-card.ig-card:hover .fb-header-text p { color: var(--tenki-green); }

  /* Expandable drawer */
  .fb-iframe-container-drawer {
    display: none;
    opacity: 0;
    width: 100%;
    justify-content: center;
    overflow-y: auto;
    border-top: 1px dashed #EAEAEA;
    height: 550px;
    padding: 15px 10px;
    position: relative;
    background-color: #FAFAFA;
    box-sizing: border-box;
    transition: opacity 0.35s ease;
  }
  .tenki-feed-column-card.active .fb-iframe-container-drawer {
    display: flex;
    opacity: 1;
  }
  .fb-iframe-container-drawer iframe {
    width: 340px;
    height: 500px;
    border: none;
    overflow: hidden;
    pointer-events: auto;
  }
  .fb-iframe-container-drawer .elfsight-app-a618fcce-28d6-49e5-87f9-efbfd761ba01 {
    width: 100%;
    height: auto;
  }

  /* Build chip — visible only with ?debug=1 (toggled via .show-build) */
  .tenki-social-build-chip {
    position: absolute;
    top: 8px;
    right: 12px;
    z-index: 5;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: #fff;
    background: rgba(0, 90, 156, 0.85);
    padding: 3px 8px;
    border-radius: 10px;
    letter-spacing: 0.3px;
    display: none;
  }
  .tenki-social-parallax-section.show-build .tenki-social-build-chip { display: inline-block; }

  @media (max-width: 900px) {
    .tenki-feeds-grid-wrapper {
      grid-template-columns: 1fr;
      gap: 30px;
    }
    .tenki-social-parallax-section {
      /* Mobile keeps the same blend pattern as desktop (funnel bottom
         fades into photo) but at smaller scale.  Top padding still
         leaves ~80 px of photo above the quote card so the lush-jungle
         backdrop reads as the visual lead-in. */
      padding: 100px 15px 30px;
      margin-top: -80px;
    }
    .tenki-social-quote-container {
      padding: 30px 20px;
    }
    .tenki-social-quote-text {
      font-size: 1.3rem;
    }
  }

/* ─── newsletter: newsletter/newsletter.html (sha=8adc40a) ─── */
/* Tenki newsletter section — full-bleed below the social section.
     The visual goal: airy white space, oversized brand statement on
     the left, focused signup card on the right.  No competing photo
     background — leaves the social section above as the visual
     bookend so this reads as the "next step" rather than another
     decorative photo block. */
  .tenki-newsletter-section {
    --tn-blue:    #005A9C;
    --tn-blue-deep: #003a66;
    --tn-black:   #1A1A1A;
    --tn-ink:     #222;
    --tn-muted:   #6b7280;
    --tn-line:    #e2e8f0;

    font-family: 'proxima-nova', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    position: relative;
    margin: 0 -50vw;        /* full-bleed */
    width: 100vw;
    left: 50%;
    right: 50%;
    padding: 80px 24px 96px;
    background: #fff;
    box-sizing: border-box;
    /* Same defensive pattern as the social section: own stacking
       context so the next sibling can't paint into ours, and so we
       don't paint into the timeline above us. */
    isolation: isolate;
    z-index: 1;
  }

  .tenki-newsletter-build-chip {
    position: absolute; top: 8px; right: 12px;
    font: 600 10px/1 monospace; padding: 3px 8px; border-radius: 4px;
    background: var(--tn-black); color: #fff; opacity: .55;
    display: none; pointer-events: none; z-index: 2;
  }
  [data-debug="1"] .tenki-newsletter-build-chip,
  body.tenki-debug .tenki-newsletter-build-chip { display: block; }

  .tenki-newsletter-inner {
    max-width: 1180px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    gap: 64px;
    align-items: center;
  }

  .tenki-newsletter-headline {
    margin: 0 0 12px;
    /* Softer than the original 800-weight tightly-tracked treatment —
       this is an inspirational statement, not a battle cry.  Serif
       face + medium weight + relaxed line-height gives it the warmth
       of a hand-written letter rather than an enterprise call-out. */
    font-family: Georgia, 'Iowan Old Style', 'Palatino Linotype', serif;
    font-size: clamp(1.55rem, 2.8vw, 2.35rem);
    line-height: 1.35;
    font-weight: 500;
    color: #2a2a2a;
    letter-spacing: 0.005em;
    text-wrap: balance;
  }
  .tenki-newsletter-attribution {
    margin: 0;
    font-size: 0.95rem;
    color: var(--tn-muted);
    font-style: italic;
  }
  .tenki-newsletter-attribution:empty { display: none; }

  .tenki-newsletter-right {
    background: #fafbfc;
    border: 1px solid var(--tn-line);
    border-radius: 16px;
    padding: 28px 28px 24px;
    box-shadow: 0 1px 3px rgba(0,0,0,.04), 0 6px 18px rgba(0,0,0,.04);
  }
  .tenki-newsletter-signup-title {
    margin: 0 0 8px;
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--tn-blue);
  }
  .tenki-newsletter-signup-title:empty { display: none; }
  .tenki-newsletter-signup-body {
    margin: 0 0 18px;
    font-size: 1rem;
    line-height: 1.5;
    color: var(--tn-ink);
  }

  .tenki-newsletter-form { display: flex; flex-direction: column; gap: 10px; }
  .tenki-newsletter-label {
    /* Visually hidden — kept for screen readers */
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0,0,0,0); border: 0;
  }
  .tenki-newsletter-form input[type="email"] {
    padding: 13px 16px;
    border: 1.5px solid var(--tn-line);
    border-radius: 10px;
    font-size: 15px;
    font-family: inherit;
    outline: none;
    background: #fff;
    color: var(--tn-ink);
    transition: border-color .15s, box-shadow .15s;
    min-height: 44px;
  }
  .tenki-newsletter-form input[type="email"]:focus {
    border-color: var(--tn-blue);
    box-shadow: 0 0 0 3px rgba(0, 90, 156, .12);
  }
  .tenki-newsletter-form input[type="email"].is-invalid {
    border-color: #dc2626;
  }
  .tenki-newsletter-btn {
    padding: 13px 22px;
    border: 0;
    border-radius: 10px;
    background: var(--tn-blue-deep);
    color: #fff;
    font-size: 15px;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    transition: background .15s, transform .12s;
    min-height: 44px;
    letter-spacing: .01em;
  }
  .tenki-newsletter-btn:hover { background: var(--tn-blue); transform: translateY(-1px); }
  .tenki-newsletter-btn:active { transform: translateY(0); }
  .tenki-newsletter-btn:disabled { opacity: .6; cursor: not-allowed; transform: none; }
  .tenki-newsletter-btn.is-success { background: #16a34a; }

  .tenki-newsletter-status {
    margin-top: 4px;
    font-size: 0.9rem;
    line-height: 1.4;
    min-height: 1.2em;
  }
  .tenki-newsletter-status.is-ok  { color: #16a34a; font-weight: 600; }
  .tenki-newsletter-status.is-err { color: #dc2626; }

  /* Mobile — stack into a single column, tighten the vertical rhythm */
  @media (max-width: 768px) {
    .tenki-newsletter-section {
      padding: 48px 16px 64px;
    }
    .tenki-newsletter-inner {
      grid-template-columns: 1fr;
      gap: 32px;
    }
    .tenki-newsletter-headline {
      font-size: clamp(1.4rem, 6vw, 1.85rem);
    }
  }
