/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* Public-facing frontend styles. Kept separate from app.css so the admin
   panel and the marketing site can evolve independently. */

/* ───────────────────────────────────────────────────────────────────
   Public-detail minimum heights — every event / announcement / archive
   / story / blog / meeting detail surface + the literature library list
   now floors at 80% of the viewport height (less the sticky header
   stack). Short posts no longer leave a strip of footer floating
   under the page fold; longer posts grow naturally beyond the floor.
   The `--fe-header-full-h` variable is set by the header chrome and
   already reflects utility-bar / alert-band heights, so the calc
   subtracts the actual visible header — not just the brand row.
   ───────────────────────────────────────────────────────────────── */
.fe-event-detail,
.fe-event-time,
.fe-event-min,
.fe-event-poster,
.fe-meeting-detail,
.fe-meeting-min,
.fe-meeting-stack,
.fe-meeting-mag,
.fe-story-paper,
.fe-story-anth,
.fe-story-letter,
.fe-story-journal,
.fe-story-mag,
.fe-blog-post {
  min-height: calc(80vh - var(--fe-header-full-h, 108px));
}
/* Literature library list floors at the FULL viewport (less the
   header stack) — the page is the canonical landing surface for
   public literature, so even a one-section result still fills the
   browser. Compound selector beats the generic .fe-mlist 50vh
   floor lower in this file on specificity (0,2,0 vs 0,1,0)
   without relying on source order. */
.fe-mlist.fe-library {
  min-height: calc(100vh - var(--fe-header-full-h, 108px));
}
/* Fellowships Index list floors at 80vh - header. Matches the
   meetings / stories / blog detail floor; the sidebar + compact
   layouts both carry the .fe-mlist + .fe-fellowships pair so the
   compound selector (0,2,0) beats the generic .fe-mlist 50vh rule
   regardless of source order. */
.fe-mlist.fe-fellowships {
  min-height: calc(80vh - var(--fe-header-full-h, 108px));
}

/* Self-hosted Libre Baskerville (SIL OFL) — used by the announcements
   page's GSR Summary view to give the stripped-down list a paper /
   editorial feel. Only the latin subset is bundled. */
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/libre-baskerville/LibreBaskerville-Regular.woff2") format("woff2");
}
@font-face {
  font-family: 'Libre Baskerville';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/libre-baskerville/LibreBaskerville-Italic.woff2") format("woff2");
}
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/libre-baskerville/LibreBaskerville-Bold.woff2") format("woff2");
}

:root {
  --fe-bg: #0e111b;
  --fe-bg-2: #141a2b;
  --fe-panel: #ffffff;
  --fe-panel-soft: #f4f7fb;
  --fe-ink: #0f172a;
  --fe-ink-muted: #475569;
  --fe-ink-soft: #64748b;
  --fe-border: #e2e8f0;
  --fe-accent: #5164ff;
  --fe-accent-2: #2ad4c3;
  --fe-accent-3: #ff5daa;
  --fe-gradient: linear-gradient(135deg, #5164ff 0%, #2ad4c3 65%, #9a7cff 120%);
  --fe-shadow: 0 12px 40px rgba(15, 23, 42, 0.10);
  --fe-shadow-lg: 0 24px 60px rgba(15, 23, 42, 0.18);
}

/* Frontend pages also load app.css (for shared modals etc.), and app.css
   sets `html, body { height: 100% }` to give admin layouts a fixed-height
   anchor for flex containers. That same rule on the public site clamps
   body to viewport height — every section past the fold overflows body's
   box, and `position: sticky` on the header has nothing to pin against
   past the first viewport. Restore natural height on frontend pages so
   sticky has the full document to work with. */
html:has(body.frontend-body) { height: auto; }
body.frontend-body { height: auto; min-height: 100vh; }
body.frontend-body {
  background: #fff;
  color: var(--fe-ink);
  /* --fe-font-{display,heading,body} are inlined on <body> by the
     server, layered theme-default → admin-override per role. The body
     declares the body-text fallback; per-role rules below pull from
     --fe-font-display and --fe-font-heading. */
  --fe-font-display: "Fraunces", "Inter", Georgia, serif;
  --fe-font-heading: "Fraunces", "Inter", Georgia, serif;
  --fe-font-body:    "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-family: var(--fe-font-body);
  font-weight: 400;
  line-height: 1.55;
  margin: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.frontend-body .fe-container {
  max-width: var(--fe-container-max, 1160px);
  margin: 0 auto;
  padding: 0 var(--fe-container-pad-desktop, 0);
}
/* Mobile gutter — admin-tunable via the Design page's "Container
   padding — mobile" token. Defaults to 5vw so content doesn't crash
   into the screen edges on phones. The 768px breakpoint covers
   tablets-and-down, matching the rest of the public site's "mobile"
   media queries. */
@media (max-width: 768px) {
  .frontend-body .fe-container {
    padding: 0 var(--fe-container-pad-mobile, 5vw);
  }
}

/* Site-wide design-token consumers. Each rule reads a CSS custom
   property emitted by app/design.py. Theme defaults flow through
   automatically; admin overrides update the variable on <body> and
   every consumer below picks up the new value. Larger rules in this
   file (.fe-section, buttons, cards) will migrate to these tokens in
   a follow-up pass. */
.frontend-body { color: var(--fe-color-text, inherit); font-size: var(--fe-text-size-base, 1rem); }
.frontend-body p { line-height: var(--fe-text-line-height, 1.6); }
/* Scoped to body content (.fe-page) so the universal link colour does
   NOT bleed into chrome (header utility bar, nav, footer, mega menu).
   Each chrome surface owns its own colour token below. */
.fe-page a { color: var(--fe-color-link, currentColor); text-decoration: var(--fe-link-decoration, underline); }
.fe-page a:hover { color: var(--fe-color-link-hover, currentColor); text-decoration: var(--fe-link-decoration-hover, underline); }
/* Buttons opt out of link styling — covers every button class used by
   the public site (.btn = admin-shared, .fe-btn = public hero/CTA,
   .fe-nav-cta = sticky-header CTA, .fe-recovery-nav-cta = DCCMA header
   pill). text-decoration tokenized so admins can opt back in. */
.fe-page .btn, .frontend-body .btn,
.fe-page .fe-btn, .frontend-body .fe-btn,
.frontend-body .fe-nav-cta,
.frontend-body .fe-recovery-nav-cta { text-decoration: var(--fe-btn-decoration, none); }
.fe-page .btn:hover, .frontend-body .btn:hover,
.fe-page .fe-btn:hover, .frontend-body .fe-btn:hover,
.frontend-body .fe-nav-cta:hover,
.frontend-body .fe-recovery-nav-cta:hover { text-decoration: var(--fe-btn-decoration, none); }
/* Mega-menu links read their own colour + decoration tokens so they
   don't inherit the body-link colour. */
.fe-megamenu-link { text-decoration: var(--fe-megamenu-link-decoration, none); }
.fe-megamenu-link:not(.fe-megamenu-link-colored) { color: var(--fe-color-megamenu-link, inherit); }
.fe-megamenu-link:hover { text-decoration: var(--fe-megamenu-link-decoration-hover, none); }
.fe-megamenu-link:hover:not(.fe-megamenu-link-colored) { color: var(--fe-color-megamenu-link-hover, inherit); }
/* Dark-mode swap: hue-preserving slightly-dimmed variant so pure-white
   admin choices read as soft off-white instead of harsh #fff against
   the mega-menu's dark surface. The dark variant is auto-emitted by
   design.py via the colors.dark_variant helper. */
html[data-theme="dark"] .fe-megamenu-link:not(.fe-megamenu-link-colored) {
  color: var(--fe-color-megamenu-link-dark, var(--fe-color-megamenu-link, inherit));
}
html[data-theme="dark"] .fe-megamenu-link:hover:not(.fe-megamenu-link-colored) {
  color: var(--fe-color-megamenu-link-hover-dark, var(--fe-color-megamenu-link-hover, inherit));
}

/* ===== Utility bar (above-header slim row) =====
   Three-column flex: left items pinned to the start, right items to the
   end, centre slot reserved for the optional Live Meeting badge. The
   bar carries its idle palette inline (admin's bg/text choice); the
   `is-live` modifier overrides to a yellow band with black text so the
   Live Meeting state is unmistakable regardless of the admin's idle
   palette. */
.fe-utility-bar { width: 100%; font-size: 0.875rem; font-weight: 500;
  border-bottom: 1px solid rgba(0,0,0,0.08);
  transition: background-color 220ms ease, color 220ms ease; }
.fe-utility-bar-inner { display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: 16px; padding: 0 20px; min-height: 36px; }
.fe-utility-bar-side { display: flex; flex-wrap: wrap; align-items: center;
  gap: 14px; min-width: 0; }
.fe-utility-bar-left { justify-content: flex-start; }
.fe-utility-bar-right { justify-content: flex-end; }
.fe-utility-bar-center { display: flex; align-items: center; justify-content: center;
  min-width: 0; }
.fe-utility-link, .fe-utility-btn, .fe-utility-text, .fe-utility-icon-only {
  display: inline-flex; align-items: center; gap: 6px;
  color: inherit; line-height: 1; }
/* Wrap each item so the side flex-row centres them by their actual
   content edges. The default block-level wrapper inherits
   line-height: normal (~1.2), which adds invisible half-leading above
   and below the inline-flex item — visually pushing the content down
   ~3 px so text/pill items appear lower than the fixed-size theme
   toggle. Making the wrapper an inline-flex centring container with
   line-height: 1 collapses that ghost space. */
.fe-utility-mobile-item {
  display: inline-flex; align-items: center; line-height: 1;
}
/* Container — group of related items rendered as one logical unit.
   Inner items lay out horizontally with a small gap; the container
   itself is the snap target on mobile so swiping lands on the whole
   group instead of mid-way through it. */
.fe-utility-container {
  display: inline-flex; align-items: center; gap: 8px;
}
/* Collapsed container — when the admin has set a `collapsed_icon` and
   the live meetings bar is active, the whole container shrinks to a
   single round icon button so the centre live message has room to
   breathe. Chrome matches `.fe-theme-toggle` / `.fe-utility-search-btn`
   so the three pill-icons read as a matched set wherever the admin
   drags them. */
.fe-utility-container--collapsed {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  background: rgba(255, 255, 255, 0.12); color: inherit;
  border: 0; padding: 0; border-radius: 999px;
  text-decoration: none; gap: 0;
  transition: background 120ms ease, transform 120ms ease;
}
.fe-utility-container--collapsed:hover {
  background: rgba(255, 255, 255, 0.22);
  transform: scale(1.04);
  text-decoration: none;
}
.fe-utility-container--collapsed .icon { width: 16px; height: 16px; }
/* Live-meeting state forces black-on-yellow on the bar; mirror the
   theme/search button overrides so the collapsed icon stays legible. */
.fe-utility-bar.is-live .fe-utility-container--collapsed { color: #111;
  background: rgba(0, 0, 0, 0.12); }
.fe-utility-bar.is-live .fe-utility-container--collapsed:hover {
  background: rgba(0, 0, 0, 0.22); }
.fe-utility-link { text-decoration: none; }
.fe-utility-link:hover { text-decoration: underline; }
.fe-utility-btn { padding: 4px 12px; border-radius: 999px;
  background: rgba(255,255,255,0.16); color: inherit;
  text-decoration: none; font-weight: 600; transition: background 160ms ease; }
.fe-utility-btn:hover { background: rgba(255,255,255,0.28); }
.fe-utility-bar.is-live .fe-utility-btn { background: rgba(0,0,0,0.12); }
.fe-utility-bar.is-live .fe-utility-btn:hover { background: rgba(0,0,0,0.22); }
.fe-utility-icon { display: inline-flex; flex: 0 0 auto; }
.fe-utility-icon .icon, .fe-utility-icon-only .icon { width: 16px; height: 16px; }
.fe-utility-icon-only { padding: 4px; }

/* Live-meeting state — forced yellow band with adaptive text colour.
   Light mode uses a vivid #ffd400; dark mode dims to a slightly darker,
   less-saturated amber so the band doesn't glare against a dark page. */
.fe-utility-bar.is-live { background: #ffd400; color: #111 !important; }
html[data-theme="dark"] .fe-utility-bar.is-live { background: #d6b000; }
.fe-utility-bar.is-live a, .fe-utility-bar.is-live .fe-utility-text { color: #111; }
.fe-utility-live { display: inline-flex; align-items: center; gap: 10px;
  font-weight: 600; flex-wrap: wrap; justify-content: center; }
/* On mobile the centre column is constrained to a single viewport-wide
   snap point — the LIVE / meeting name / Join button row often has to
   wrap, and 10 px row-gaps land too tall against the slim utility bar.
   Tighten to 4 px so the wrapped lines sit closer together; desktop
   keeps the original 10 px since nothing wraps there. The pulse-dot →
   LIVE: pairing reads better with a roomier gap, so the dot keeps its
   own 6 px right margin (which lands on top of the 4 px container
   gap to total 10 px between dot and label). */
@media (max-width: 720px) {
  .fe-utility-live { gap: 4px; }
  .fe-utility-live-pulse { margin-right: 6px; }
}
.fe-utility-live-pulse { width: 9px; height: 9px; border-radius: 999px;
  background: #d10000; box-shadow: 0 0 0 0 rgba(209, 0, 0, 0.6);
  animation: fe-utility-live-pulse 1.6s infinite; flex: 0 0 auto; }
@keyframes fe-utility-live-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(209, 0, 0, 0.6); }
  70%  { box-shadow: 0 0 0 12px rgba(209, 0, 0, 0); }
  100% { box-shadow: 0 0 0 0 rgba(209, 0, 0, 0); }
}
.fe-utility-live-label { font-weight: 700; letter-spacing: 0.04em; }
.fe-utility-live-name { font-weight: 700; }
.fe-utility-live-cta { display: inline-flex; align-items: center;
  padding: 4px 12px; border-radius: 999px;
  background: #0b5cff; color: #fff;
  font-weight: 700; text-decoration: none; transition: background 160ms ease; }
.fe-utility-live-cta:hover { background: #094bd0; color: #fff; text-decoration: none; }
/* The bar carries `is-live` which sets `color: #111 !important;` on
   the entire band — re-assert white inside the CTA pill so its label
   stays readable on the blue background, and keep the underline off
   on hover so the pill doesn't pick up a stray underline from any
   cascading `a:hover` rule. */
.fe-utility-bar.is-live .fe-utility-live-cta,
.fe-utility-bar.is-live .fe-utility-live-cta:hover { color: #fff; text-decoration: none; }
/* Theme toggle on the yellow live band: invert the white-on-white
   chrome to dark-on-translucent-dark so the icon stays legible. */
.fe-utility-bar.is-live .fe-theme-toggle { color: #111;
  background: rgba(0,0,0,0.12); }
.fe-utility-bar.is-live .fe-theme-toggle:hover { background: rgba(0,0,0,0.22); }

/* ── Mobile layout ────────────────────────────────────────────────────
   On narrow viewports the bar collapses to a single horizontal swipe
   strip: one snap point per item (or per whole side, depending on the
   admin's "mobile collapsed default" choice). The strip scrolls
   independently and snaps so each swipe lands a fresh item in view.
   When the Live Meetings Bar is active the centre column carries the
   LIVE: <meeting> message and becomes its own snap point — the JS
   overrides the admin default to land on it first, but visitors can
   still swipe left and right to reveal the side items around it. */
@media (max-width: 720px) {
  /* Inner row → horizontal swipe strip. Hide the scrollbar; rely on
     scroll-snap to land on a clean snap point after each swipe. */
  .fe-utility-bar-inner {
    display: flex; flex-direction: row;
    grid-template-columns: none;
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    gap: 0; padding: 0; min-height: 36px;
    /* Width clamp from the desktop inline style (max-width / fe-container)
       loses meaning when the strip is allowed to overflow horizontally;
       remove it so swipes can travel the whole viewport. */
    max-width: none !important;
  }
  .fe-utility-bar-inner::-webkit-scrollbar { display: none; }

  .fe-utility-bar-side {
    flex: 0 0 auto;
    min-width: 0;
    gap: 0;
    flex-wrap: nowrap;
  }

  /* The centre column — when it carries the live message it's a snap
     point of its own (full viewport, centred); when empty (no live
     meeting) it collapses so the side snap points sit flush. */
  .fe-utility-bar-center {
    flex: 0 0 100%;
    scroll-snap-align: center;
    display: flex; align-items: center; justify-content: center;
    padding: 6px 14px; min-height: 36px;
  }
  /* When no meeting is live the centre column has nothing to show.
     Drop it from the strip entirely (display: none) so it doesn't sit
     as a 0-width snap point between the left and right sides — that
     phantom snap would force an extra swipe in the middle of the
     strip before the next real item came into view. With it gone the
     swipe count matches the visible item count exactly. */
  .fe-utility-bar:not(.is-live) .fe-utility-bar-center {
    display: none;
  }

  /* ── ITEM mode ──
     Each item is its own snap point that fills the full viewport width
     so exactly one item is visible at a time. Side wrappers act as
     flow containers — items become the actual snap targets. */
  .fe-utility-bar--mobile-item .fe-utility-bar-side { display: contents; }
  .fe-utility-bar--mobile-item .fe-utility-mobile-item {
    flex: 0 0 100%;
    scroll-snap-align: center;
    display: flex; align-items: center; justify-content: center;
    padding: 6px 14px; min-height: 36px;
  }

  /* ── GROUP mode ──
     Each side is one snap point — the entire left side scrolls in as a
     single unit, then the entire right side. Items inside a side flow
     horizontally with normal gaps. */
  .fe-utility-bar--mobile-group .fe-utility-bar-side {
    flex: 0 0 100%;
    scroll-snap-align: center;
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; gap: 12px;
    padding: 6px 14px; min-height: 36px;
  }
  .fe-utility-bar--mobile-group .fe-utility-mobile-item {
    display: inline-flex; align-items: center;
  }

  /* The Recovery Blue theme toggle is part of the right side's flex —
     keep it inline with the rest of the right items so it scrolls with
     them rather than disappearing past the strip's edge. */
  .fe-utility-bar-right .fe-theme-toggle { flex: 0 0 auto; }
}


/* ===== Alert bars ===== */
.fe-alertbar { width: 100%; font-size: 0.9375rem; font-weight: 500;
  border-bottom: 1px solid rgba(0,0,0,0.08); }
.fe-alertbar-inner { display: flex; align-items: center; justify-content: center;
  gap: 10px; padding: 10px 20px; min-height: 40px; text-align: center; }
.fe-alertbar-icon { display: inline-flex; align-items: center;
  flex: 0 0 auto; }
.fe-alertbar-icon .icon { width: 18px; height: 18px; }
.fe-alertbar-msg { flex: 0 1 auto; min-width: 0; line-height: 1.4; }
.fe-alertbar a { color: inherit; text-decoration: underline; }
@media (max-width: 560px) {
  .fe-alertbar-inner { padding: 8px 14px; font-size: 0.875rem; gap: 8px; }
  .fe-alertbar-icon .icon { width: 16px; height: 16px; }
}

/* Admin-only preview banner shown on the public frontend when the global
   toggle is off but a signed-in editor/admin is viewing. */
.fe-admin-preview-bar { width: 100%; background: #fde68a; color: #78350f;
  border-bottom: 1px solid rgba(120, 53, 15, 0.18); font-size: 0.9375rem;
  font-weight: 500; }
.fe-admin-preview-inner { display: flex; align-items: center; justify-content: center;
  gap: 16px; padding: 10px 20px; min-height: 40px; text-align: center; }
.fe-admin-preview-msg { flex: 0 1 auto; line-height: 1.4; }
.fe-admin-preview-cta { flex: 0 0 auto; color: inherit; text-decoration: underline;
  font-weight: 600; white-space: nowrap; }
@media (max-width: 640px) {
  .fe-admin-preview-inner { flex-direction: column; gap: 4px;
    padding: 8px 14px; font-size: 0.875rem; }
}

/* ===== Header ===== */
.fe-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid transparent;
  transition: transform 220ms ease, background 200ms ease,
              border-color 200ms ease, box-shadow 200ms ease;
}
/* Headroom auto-hide: slide off the top when the user has scrolled past
   one viewport and is moving down, slide back in on any upward motion.
   The class is added/removed by the scroll handler in
   templates/frontend/base.html. */
.fe-header.fe-header-hidden { transform: translateY(-100%); }
.fe-header.fe-header-scrolled {
  background: rgba(255, 255, 255, 0.92);
  border-bottom-color: var(--fe-border);
  box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
}
.fe-header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; min-height: var(--fe-header-h, 72px);
}
/* Fixed 74px mobile header, regardless of admin-configured desktop height. */
@media (max-width: 880px) {
  .fe-header-inner { min-height: 74px !important; }
}
.fe-header.fe-header-full .fe-header-inner {
  width: 100%;
  margin-left: 0; margin-right: 0;
}
.fe-brand {
  display: inline-flex; align-items: center; gap: 12px;
  color: var(--fe-ink); text-decoration: none; font-weight: 700;
  font-size: 1.0625rem; letter-spacing: -0.01em;
  flex: 0 1 auto; min-width: 0;
}
.fe-brand-logo { height: auto; object-fit: contain;
  max-width: 100%; max-height: 56px; }
/* On mobile the logo renders at a fixed 160px wide regardless of the inline
   style that carries the admin-configured desktop width. */
@media (max-width: 880px) {
  .fe-brand-logo { width: 160px !important; max-width: 160px !important; max-height: 58px !important; }
}
.fe-brand-name { font-family: var(--fe-font-heading); font-weight: 700; font-size: 1.125rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fe-nav {
  display: flex; align-items: center; gap: 4px;
  flex: 0 0 auto;
}
.fe-nav a {
  color: var(--fe-color-nav-link, var(--fe-ink-muted)); padding: 8px 14px; border-radius: 8px;
  font-weight: 500; font-size: 1rem; text-decoration: none;
  transition: color 150ms ease, background 150ms ease;
}
.fe-nav a:hover { color: var(--fe-color-nav-link-hover, var(--fe-ink)); background: var(--fe-panel-soft); text-decoration: none; }
.fe-nav-cta {
  margin-left: 8px; padding: 9px 18px !important; border-radius: 999px;
  background: var(--fe-ink); color: #fff !important; font-weight: 600;
  /* Same admin-toggleable primary-button recipe used elsewhere — drop
     shadow at rest, lift + glow on hover. Each effect resolves to
     `none` when the matching toggle is off in the Design tokens admin. */
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
  transition: color 150ms ease, background 150ms ease,
              transform 120ms ease, box-shadow 180ms ease;
}
.fe-nav-cta:hover {
  background: var(--fe-accent) !important; color: #fff !important;
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}
.fe-menu-btn {
  display: none; background: transparent; border: 0; padding: 8px;
  margin-left: auto; cursor: pointer;
}
.fe-menu-btn span {
  display: block; width: 22px; height: 2px; background: var(--fe-ink);
  border-radius: 2px; margin: 4px 0;
  transition: transform 200ms ease; transform-origin: center;
}
/* Transform the two lines into an X whenever a menu is open — either the
   mobile hamburger dropdown OR the full-width mega menu. The two bars sit
   ~10px apart center-to-center (2px height + 4+4px vertical margins, margins
   don't collapse inside a flex parent), so each moves 5px toward the middle
   so their centres meet and the X crosses cleanly. */
.fe-header.fe-nav-open .fe-menu-btn span:first-child,
body.fe-megamenu-open .fe-menu-btn span:first-child {
  transform: translateY(5px) rotate(45deg);
}
.fe-header.fe-nav-open .fe-menu-btn span:last-child,
body.fe-megamenu-open .fe-menu-btn span:last-child {
  transform: translateY(-5px) rotate(-45deg);
}

/* ========================================================================
   HEADER TEMPLATE — DCCMA
   Two rows: blue utility strip + white main nav with wide logotype.
   ======================================================================== */
:root { --fe-recovery-blue: #0B5CFF; --fe-recovery-blue-2: #3d6dff;
        --fe-recovery-ink: #0f172a; --fe-recovery-ink-soft: #475569; }
.fe-header-recovery {
  position: sticky; top: 0; z-index: 40;
  background: #fff; border-bottom: 1px solid #e5e9f0;
  box-shadow: 0 1px 0 rgba(15,23,42,0.03);
  transition: transform 220ms ease;
}
/* Headroom auto-hide for the Recovery Blue header — same JS class, same
   slide-up/slide-down behaviour as the classic header above. */
.fe-header-recovery.fe-header-hidden { transform: translateY(-100%); }

/* Utility strip */
.fe-recovery-util { background: var(--fe-recovery-blue); color: #fff;
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.01em; }
.fe-recovery-util-inner { display: flex; align-items: center; justify-content: space-between;
  gap: 18px; min-height: 36px; padding-top: 6px; padding-bottom: 6px; }
.fe-recovery-util-link { color: #fff; text-decoration: none; opacity: 0.92;
  transition: opacity 120ms ease; padding: 4px 0; }
.fe-recovery-util-link:hover { opacity: 1; text-decoration: underline; }
.fe-recovery-util-phone { display: inline-flex; align-items: center; gap: 10px;
  color: #fff; text-decoration: none; }
.fe-recovery-util-phone strong { font-weight: 600; letter-spacing: 0.01em; }
.fe-recovery-util-phone-num { background: rgba(255,255,255,0.14); padding: 3px 10px;
  border-radius: 999px; font-weight: 700; font-size: 0.8125rem;
  letter-spacing: 0.03em; transition: background 120ms ease; }
.fe-recovery-util-phone:hover .fe-recovery-util-phone-num { background: rgba(255,255,255,0.24); }
.fe-recovery-util-right { display: inline-flex; align-items: center; gap: 14px; }

/* Theme toggle button — sits in the utility bar, icon swaps on state. */
.fe-theme-toggle { display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.12); color: #fff; border: 0; cursor: pointer;
  width: 30px; height: 30px; border-radius: 999px; padding: 0;
  transition: background 120ms ease, transform 120ms ease; }
.fe-theme-toggle:hover { background: rgba(255,255,255,0.22); transform: scale(1.04); }
.fe-theme-toggle .icon { width: 16px; height: 16px; }
.fe-theme-toggle-icon { display: none; }
html:not([data-theme="dark"]) .fe-theme-toggle-moon { display: inline-flex; }
html[data-theme="dark"]           .fe-theme-toggle-sun  { display: inline-flex; }

/* Main row */
.fe-recovery-main { background: #fff; }
.fe-recovery-main-inner { display: flex; align-items: center; justify-content: space-between;
  gap: 24px; min-height: var(--fe-header-h, 72px); }

.fe-recovery-brand { display: inline-flex; align-items: center;
  color: var(--fe-recovery-ink); text-decoration: none; flex: 0 1 auto; min-width: 0; }
.fe-recovery-logo { height: auto; display: block; object-fit: contain;
  max-width: 100%; max-height: 52px; }
.fe-recovery-logo-default { width: auto; height: 44px; }
/* Mobile: fixed 74px main row + 160px-wide logo, overriding the admin-
   configured desktop height and any inline width on the logo. */
@media (max-width: 880px) {
  .fe-recovery-main-inner { min-height: 74px !important; }
  .fe-recovery-logo { width: 160px !important; max-width: 160px !important; max-height: 58px !important; }
  .fe-recovery-logo-default { width: 160px; height: auto; }
}

.fe-recovery-nav { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.fe-recovery-nav-link { color: var(--fe-color-nav-link, var(--fe-recovery-ink)); font-size: 1rem; font-weight: 500;
  text-decoration: none; padding: 9px 14px; border-radius: 6px;
  transition: color 120ms ease, background 120ms ease; }
.fe-recovery-nav-link:hover { color: var(--fe-color-nav-link-hover, var(--fe-recovery-blue)); background: #eef3ff; text-decoration: none; }
.fe-nav-caret { display: inline-flex; align-items: center; margin-left: 6px;
  transition: transform 140ms ease; vertical-align: middle; }
.fe-nav-caret .icon { width: 14px; height: 14px; }
.fe-megaparent:hover .fe-nav-caret,
.fe-megaparent.mega-active .fe-nav-caret { transform: rotate(180deg); }

.fe-recovery-nav-newcomer { display: inline-flex; flex-direction: column; align-items: flex-start;
  padding: 4px 10px; gap: 0; line-height: 1.1; }
.fe-recovery-nav-newcomer-lead { font-size: 0.75rem; color: var(--fe-recovery-ink-soft);
  font-weight: 500; }
.fe-recovery-nav-newcomer-cta { font-size: 1rem; color: var(--fe-recovery-blue);
  font-weight: 700; }
.fe-recovery-nav-newcomer:hover { background: #eef3ff; text-decoration: none; }

.fe-recovery-nav-cta {
  background: var(--fe-color-btn-primary-bg, var(--fe-recovery-blue));
  color: var(--fe-color-btn-primary-text, #fff) !important;
  font-weight: 700; padding: 9px 18px !important; border-radius: 999px;
  margin-left: 4px; font-size: 1rem;
  /* Same admin-toggleable primary-button recipe — keeps the header
     CTA's affordance consistent with primary buttons in the body. */
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
  transition: background 140ms ease, transform 120ms ease, box-shadow 180ms ease;
}
.fe-recovery-nav-cta:hover {
  background: color-mix(in srgb, var(--fe-color-btn-primary-bg, var(--fe-recovery-blue)) 88%, black);
  text-decoration: none;
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}

.fe-recovery-nav-btn-rounded {
  background: var(--fe-color-btn-primary-bg, var(--fe-recovery-blue));
  color: var(--fe-color-btn-primary-text, #fff) !important;
  font-weight: 700; padding: 9px 18px !important; border-radius: 1rem;
  margin-left: 4px; font-size: 1rem;
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
  transition: background 140ms ease, transform 120ms ease, box-shadow 180ms ease;
}
.fe-recovery-nav-btn-rounded:hover {
  background: color-mix(in srgb, var(--fe-color-btn-primary-bg, var(--fe-recovery-blue)) 88%, black);
  text-decoration: none;
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}
/* Header CTA buttons (button + button-rounded styles) dim in dark mode
   to the same muted navy as the meeting-card "Join Zoom" pill + the
   utility strip — keeps every blue affordance on the page in the same
   moody-dark register instead of glowing #0B5CFF over the dark surface. */
/* Dark-mode primary-button recipe shared with the Hero CTA: a 40%
   mix of the admin's `--fe-btn-bg` colour against black at rest, 60%
   on hover. Replaces the previously-hardcoded #1e3a8a/#2747a3 so the
   Classic + Recovery Blue header CTAs (and the Recovery Blue rounded
   variant) all track the admin's chosen primary-button colour and
   match the look of the hero "Find a Meeting" button. `--fe-btn-bg`
   falls back through the same chain as the hero rule. */
html[data-theme="dark"] .fe-nav-cta,
html[data-theme="dark"] .fe-recovery-nav-cta,
html[data-theme="dark"] .fe-recovery-nav-btn-rounded,
body.fe-frontend-force-dark .fe-nav-cta,
body.fe-frontend-force-dark .fe-recovery-nav-cta,
body.fe-frontend-force-dark .fe-recovery-nav-btn-rounded {
  background: color-mix(in srgb,
                        var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 40%,
                        #000) !important;
  color: #f8fafc !important;
  border-color: color-mix(in srgb,
                          var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 40%,
                          #000) !important;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-nav-cta:hover,
html[data-theme="dark"] .fe-recovery-nav-cta:hover,
html[data-theme="dark"] .fe-recovery-nav-btn-rounded:hover,
body.fe-frontend-force-dark .fe-nav-cta:hover,
body.fe-frontend-force-dark .fe-recovery-nav-cta:hover,
body.fe-frontend-force-dark .fe-recovery-nav-btn-rounded:hover {
  background: color-mix(in srgb,
                        var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 60%,
                        #000) !important;
  color: #ffffff !important;
  border-color: color-mix(in srgb,
                          var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 60%,
                          #000) !important;
}

.fe-recovery-nav-portal { color: var(--fe-recovery-ink-soft); font-weight: 500; font-size: 0.875rem;
  margin-left: 4px; }

.fe-recovery-menu-btn { display: none; background: transparent; border: 0; padding: 8px;
  cursor: pointer; margin-left: auto; }
.fe-recovery-menu-btn span { display: block; width: 22px; height: 2px;
  background: var(--fe-recovery-ink); border-radius: 2px; margin: 4px 0;
  transition: transform 200ms ease; transform-origin: center; }
/* Same hamburger → X transform as the classic header, triggered whenever the
   hamburger dropdown or any mega menu is open. */
.fe-header-recovery.fe-nav-open .fe-recovery-menu-btn span:first-child,
body.fe-megamenu-open .fe-recovery-menu-btn span:first-child {
  transform: translateY(5px) rotate(45deg);
}
.fe-header-recovery.fe-nav-open .fe-recovery-menu-btn span:last-child,
body.fe-megamenu-open .fe-recovery-menu-btn span:last-child {
  transform: translateY(-5px) rotate(-45deg);
}

/* Responsive */
@media (max-width: 980px) {
  .fe-recovery-nav-newcomer { display: none; }
}
@media (max-width: 880px) {
  .fe-recovery-nav { display: none; position: absolute; left: 0; right: 0;
    top: 100%; flex-direction: column; gap: 0; background: #fff;
    border-bottom: 1px solid #e5e9f0; padding: 8px 20px 16px;
    box-shadow: 0 10px 24px rgba(15,23,42,0.10); z-index: 5; }
  .fe-header-recovery.fe-nav-open .fe-recovery-nav { display: flex; align-items: stretch; }
  .fe-recovery-nav-link { padding: 12px 14px; }
  .fe-recovery-nav-cta { margin: 6px 14px 0; text-align: center; padding: 12px 18px !important; }
  .fe-recovery-menu-btn { display: inline-flex; flex-direction: column; justify-content: center; }
  .fe-recovery-util-inner { font-size: 0.75rem; gap: 8px; min-height: 32px; }
}
@media (max-width: 480px) {
  .fe-recovery-util-link { display: none; }
  .fe-recovery-util-inner { justify-content: center; }
}

/* ========================================================================
   DCCMA homepage & footer styles
   ======================================================================== */
.fe-recovery-hero { padding: 72px 0 64px; background: #fff;
  border-bottom: 1px solid #e5e9f0; text-align: center; }
.fe-recovery-hero-inner { max-width: 900px; margin: 0 auto; }
.fe-recovery-hero-h1 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: clamp(1.75rem, 4.2vw, 2.75rem); line-height: 1.15; margin: 0 0 18px;
  color: var(--fe-recovery-ink); letter-spacing: -0.02em; }
.fe-recovery-hero-lede { color: var(--fe-recovery-ink-soft); font-size: 1.0625rem;
  line-height: 1.65; margin: 0 auto 28px; max-width: 700px; }
.fe-recovery-hero-cta { display: inline-flex; flex-wrap: wrap; justify-content: center; gap: 12px; }

.fe-recovery-btn { display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 24px; border-radius: 8px; font-weight: 600; font-size: 0.9375rem;
  text-decoration: none; border: 2px solid transparent;
  transition: background 140ms ease, border-color 140ms ease, transform 120ms ease; }
.fe-recovery-btn-primary,
.fe-page .fe-recovery-btn-primary,
.frontend-body .fe-recovery-btn-primary {
  background: var(--fe-color-btn-primary-bg, var(--fe-recovery-blue));
  color: var(--fe-color-btn-primary-text, #fff);
  /* Same toggleable recipe as `.fe-btn-primary` so Recovery Blue's
     primary buttons share the meeting-page Zoom-button feel by
     default — drop shadow at rest, lift + glow on hover. Each effect
     is admin-toggleable in the Design tokens admin (Buttons group). */
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
  transition: background 140ms ease, border-color 140ms ease,
              transform 120ms ease, box-shadow 180ms ease;
}
.fe-recovery-btn-primary:hover,
.fe-page .fe-recovery-btn-primary:hover,
.frontend-body .fe-recovery-btn-primary:hover {
  background: color-mix(in srgb, var(--fe-color-btn-primary-bg, var(--fe-recovery-blue)) 88%, black);
  color: var(--fe-color-btn-primary-text, #fff);
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}
.fe-recovery-btn-ghost,
.fe-page .fe-recovery-btn-ghost,
.frontend-body .fe-recovery-btn-ghost {
  background: var(--fe-color-btn-secondary-bg, transparent);
  color: var(--fe-color-btn-secondary-text, var(--fe-recovery-blue));
  border-color: var(--fe-color-btn-secondary-text, var(--fe-recovery-blue));
}
.fe-recovery-btn-ghost:hover,
.fe-page .fe-recovery-btn-ghost:hover,
.frontend-body .fe-recovery-btn-ghost:hover {
  background: color-mix(in srgb, var(--fe-color-btn-secondary-bg, var(--fe-recovery-blue)) 90%, black);
  color: var(--fe-color-btn-secondary-text, var(--fe-recovery-blue));
}

.fe-recovery-threeup { padding: 64px 0; background: #f6f9ff; border-bottom: 1px solid #e5e9f0; }
.fe-recovery-threeup-grid { display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.fe-recovery-threeup-card { padding: 28px 26px; border-radius: 12px;
  background: var(--fe-color-card-primary-bg, #fff);
  border: var(--fe-card-primary-border-width, 1px) solid var(--fe-color-card-primary-border, #e5e9f0);
  transition: transform 180ms ease, box-shadow 180ms ease; }
.fe-recovery-threeup-card:hover { transform: translateY(-3px);
  box-shadow: 0 10px 28px rgba(15,23,42,0.08); }
.fe-recovery-threeup-icon { width: 52px; height: 52px; display: grid; place-items: center;
  border-radius: 14px; margin-bottom: 18px; background: color-mix(in srgb, var(--fe-ic, var(--fe-recovery-blue)) 12%, transparent);
  color: var(--fe-ic, var(--fe-recovery-blue)); }
.fe-recovery-threeup-icon .icon { width: 24px; height: 24px; }
.fe-recovery-threeup-card h2 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: 1.375rem; margin: 0 0 8px; letter-spacing: -0.01em; color: var(--fe-recovery-ink); }
.fe-recovery-threeup-card p { margin: 0 0 14px; color: var(--fe-recovery-ink-soft);
  font-size: 0.9375rem; line-height: 1.55; }
.fe-recovery-cardlink { color: var(--fe-recovery-blue); font-weight: 600; text-decoration: none;
  font-size: 0.9375rem; }
.fe-recovery-cardlink:hover { text-decoration: underline; }

.fe-recovery-meetings { padding: 64px 0; background: #fff; }
.fe-recovery-section-head { text-align: center; max-width: 680px; margin: 0 auto 40px; }
.fe-recovery-section-head h2 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem); margin: 0 0 10px;
  letter-spacing: -0.015em; color: var(--fe-recovery-ink); }
.fe-recovery-section-head p.muted { color: var(--fe-recovery-ink-soft); font-size: 1rem; margin: 0; }
.fe-recovery-section-foot { text-align: center; margin-top: 32px; }

.fe-recovery-meeting-grid { display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.fe-recovery-meeting-card { padding: 20px 22px; border: 1px solid #e5e9f0; border-radius: 12px;
  background: #fff; transition: box-shadow 180ms ease, transform 180ms ease; }
.fe-recovery-meeting-card:hover { transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(15,23,42,0.08); }
.fe-recovery-meeting-head { margin-bottom: 10px; }
.fe-recovery-meeting-type { display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.fe-recovery-meeting-type-in_person { background: #dcfce7; color: #14532d; }
.fe-recovery-meeting-type-online    { background: #dbeafe; color: #1e3a8a; }
.fe-recovery-meeting-type-hybrid    { background: #fef3c7; color: #78350f; }
.fe-recovery-meeting-card h3 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: 1.1875rem; margin: 0 0 10px; letter-spacing: -0.01em;
  color: var(--fe-recovery-ink); line-height: 1.2; }
.fe-recovery-meeting-sched { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.9375rem; color: var(--fe-recovery-ink-soft); }
.fe-recovery-meeting-sched .day { font-weight: 600; color: var(--fe-recovery-ink); }

.fe-recovery-fellowship { padding: 72px 0; background: var(--fe-recovery-blue); color: #fff; }
.fe-recovery-fellowship-inner { display: grid; gap: 48px; align-items: center;
  grid-template-columns: 1.2fr 0.8fr; }
.fe-recovery-fellowship h2 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: clamp(1.75rem, 4vw, 2.5rem); margin: 0 0 18px;
  letter-spacing: -0.02em; line-height: 1.15; }
.fe-recovery-fellowship p { font-size: 1.0625rem; line-height: 1.65; margin: 0 0 24px;
  color: rgba(255,255,255,0.88); }
.fe-recovery-fellowship-body { font-size: 1.0625rem; line-height: 1.65;
  color: rgba(255,255,255,0.88); margin: 0 0 24px; }
.fe-recovery-fellowship-body > :first-child { margin-top: 0; }
.fe-recovery-fellowship-body > :last-child { margin-bottom: 0; }
.fe-recovery-fellowship-body p { margin: 0 0 1em; color: rgba(255,255,255,0.88); }
.fe-recovery-fellowship-body h3 { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  color: #fff; margin: 1.2em 0 .4em; font-size: 1.35rem; }
.fe-recovery-fellowship-body a { color: #fff; text-decoration: underline; }

.fe-recovery-helpline-body { color: rgba(255,255,255,0.8);
  margin: 16px auto 0; max-width: 560px; font-size: 0.9375rem; line-height: 1.6; }
.fe-recovery-helpline-body > :first-child { margin-top: 0; }
.fe-recovery-helpline-body > :last-child { margin-bottom: 0; }
.fe-recovery-helpline-body a { color: var(--fe-recovery-blue-2); }
.fe-recovery-fellowship .fe-recovery-btn-primary {
  background: #fff; color: var(--fe-recovery-blue); }
.fe-recovery-fellowship .fe-recovery-btn-primary:hover { background: #e0e7ff; }
.fe-recovery-fellowship-side { display: grid; gap: 16px;
  grid-template-columns: 1fr 1fr 1fr; }
.fe-recovery-stat { padding: 18px 14px; background: rgba(255,255,255,0.1); border-radius: 12px;
  text-align: center; }
.fe-recovery-stat-num { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: 2rem; line-height: 1; }
.fe-recovery-stat-lbl { font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: rgba(255,255,255,0.78); margin-top: 6px; }

.fe-recovery-helpline { padding: 72px 0; background: #0a1026; color: #fff; text-align: center; }
.fe-recovery-helpline-eyebrow { font-size: 0.875rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--fe-recovery-blue-2); margin-bottom: 16px; }
.fe-recovery-helpline-num { display: inline-block; font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600; font-size: clamp(2.5rem, 6vw, 4rem); letter-spacing: 0.01em;
  color: #fff; text-decoration: none; line-height: 1;
  background: linear-gradient(135deg, #fff 0%, #cbd5e1 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; }
.fe-recovery-helpline-num:hover { filter: brightness(1.1); }
.fe-recovery-helpline-sub { color: rgba(255,255,255,0.75); margin: 12px 0 0; font-size: 0.9375rem; }
.fe-recovery-helpline-email { margin-top: 16px; color: rgba(255,255,255,0.9); font-size: 0.9375rem; }
.fe-recovery-helpline-email a { color: var(--fe-recovery-blue-2); }

@media (max-width: 880px) {
  .fe-recovery-fellowship-inner { grid-template-columns: 1fr; gap: 28px; }
  .fe-recovery-fellowship-side { grid-template-columns: 1fr 1fr 1fr; }
}
@media (max-width: 560px) {
  .fe-recovery-hero { padding: 56px 0 48px; }
  .fe-recovery-threeup, .fe-recovery-meetings, .fe-recovery-fellowship, .fe-recovery-helpline { padding: 48px 0; }
  .fe-recovery-fellowship-side { grid-template-columns: 1fr; }
}

/* ===== DCCMA Footer ===== */
.fe-footer-recovery { background: #0a1026; color: #cbd5e1; font-size: 0.9375rem;
  line-height: 1.6; }
.fe-footer-recovery-locations { background: #0b1429; padding: 48px 0 32px; border-bottom: 1px solid rgba(255,255,255,0.06); }
.fe-footer-recovery-heading { font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: 1rem; color: #fff; letter-spacing: 0.02em;
  text-transform: uppercase; margin: 0 0 20px; }
.fe-footer-recovery-loc-grid { display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.fe-footer-recovery-loc { display: block; padding: 18px 20px; border-radius: 12px;
  background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.06);
  color: inherit; text-decoration: none; transition: background 140ms ease, transform 140ms ease; }
.fe-footer-recovery-loc:hover { background: rgba(255,255,255,0.08); transform: translateY(-2px); }
.fe-footer-recovery-loc-name { font-weight: 700; font-size: 0.875rem; letter-spacing: 0.08em;
  color: #fff; margin-bottom: 10px; }
.fe-footer-recovery-loc-addr { color: #cbd5e1; font-size: 0.9375rem; line-height: 1.5; }
.fe-footer-recovery-loc-note { color: rgba(203,213,225,0.65); font-size: 0.8125rem; margin-top: 6px; }
.fe-footer-recovery-loc-world .fe-footer-recovery-loc-note { color: var(--fe-recovery-blue-2); font-weight: 600; }

.fe-footer-recovery-contact { padding: 40px 0; border-bottom: 1px solid rgba(255,255,255,0.06); }
.fe-footer-recovery-contact-inner { display: grid; gap: 32px;
  grid-template-columns: 1fr 1fr; }
.fe-footer-recovery-contact p { margin: 0 0 10px; }
.fe-footer-recovery-contact a { color: var(--fe-recovery-blue-2); text-decoration: none; }
.fe-footer-recovery-contact a:hover { text-decoration: underline; }

.fe-footer-recovery-bottom { padding: 28px 0; }
.fe-footer-recovery-bottom-inner { display: grid; gap: 18px; align-items: center;
  grid-template-columns: auto 1fr auto; }
.fe-footer-recovery-logo { max-width: 160px; height: auto; filter: brightness(0) invert(1) brightness(0.85); }
.fe-footer-recovery-logo-default { max-width: 200px; }
.fe-footer-recovery-copy { color: rgba(203,213,225,0.65); font-size: 0.8125rem; text-align: center; }
.fe-footer-recovery-links { display: flex; gap: 18px; justify-content: flex-end; }
.fe-footer-recovery-links a { color: rgba(203,213,225,0.8); text-decoration: none;
  font-size: 0.875rem; transition: color 120ms ease; }
.fe-footer-recovery-links a:hover { color: #fff; }

@media (max-width: 880px) {
  .fe-footer-recovery-contact-inner { grid-template-columns: 1fr; gap: 20px; }
  .fe-footer-recovery-bottom-inner { grid-template-columns: 1fr; text-align: center; gap: 12px; }
  .fe-footer-recovery-links { justify-content: center; }
}

/* ===== Hero ===== */
.fe-hero {
  position: relative; overflow: hidden;
  padding: 120px 0 110px;
  background: linear-gradient(180deg, #fff 0%, var(--fe-panel-soft) 100%);
  /* Optional admin-controlled vh height. The custom property is set
     inline by the template only when the admin has dialled a non-zero
     value; otherwise the fallback (auto) keeps the natural padding
     behaviour and existing installs render unchanged. Flex column +
     centre justification only matters when min-height stretches the
     section past its content; with auto/no-stretch it's a no-op
     because the single in-flow child fills the available space. */
  min-height: var(--fe-hero-min-h, auto);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Named background styles override the default wash. "solid", "gradient",
   and "image" are applied via inline style on the hero <section>; "frosty"
   keeps the default wash and adds animated blobs on top. */
.fe-hero-bg-solid, .fe-hero-bg-gradient, .fe-hero-bg-image {
  background: inherit;  /* placeholder; inline style overrides */
}
.fe-hero-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
/* Particle canvas layered between the bg (frosty/sinewave/etc) and the
   centered content. Pointer-events disabled so it never steals clicks. */
.fe-hero-particles { position: absolute; inset: 0; width: 100%; height: 100%;
  z-index: 1; pointer-events: none; }
.fe-hero-bg-sinewave { background: linear-gradient(180deg, #16c2ba 0%, #5a1ce5 100%); }
/* Background video — fills the hero edge to edge with no letterboxing.
   Sits below content (z-index 0) and above the section's own background. */
.fe-hero-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 0;
  pointer-events: none;
}
/* Blobs are driven by two CSS custom properties (--fe-blob-hue-a/-b) so the
   admin can shift hues with sliders and optionally randomize them per page
   load via JS. --fe-blob-blur / --fe-blob-op control blur & opacity. */
.fe-hero-blob {
  position: absolute; border-radius: 50%;
  filter: blur(var(--fe-blob-blur, 80px));
  opacity: var(--fe-blob-op, 0.45);
  animation: fe-float 18s ease-in-out infinite;
}
.fe-hero-blob-a {
  width: 520px; height: 520px; top: -120px; left: -140px;
  background: radial-gradient(circle, hsl(var(--fe-blob-hue-a, 225), 100%, 85%) 0%, transparent 70%);
}
.fe-hero-blob-b {
  width: 480px; height: 480px; top: 60px; right: -160px;
  background: radial-gradient(circle, hsl(var(--fe-blob-hue-b, 170), 85%, 85%) 0%, transparent 70%);
  animation-delay: -6s;
}
.fe-hero-blob-c {
  width: 380px; height: 380px; bottom: -180px; left: 35%;
  background: radial-gradient(circle, hsl(calc(var(--fe-blob-hue-a, 225) + 80), 90%, 88%) 0%, transparent 70%);
  animation-delay: -12s;
}
@keyframes fe-float {
  0%,100% { transform: translate(0,0) scale(1); }
  50%     { transform: translate(30px,-20px) scale(1.08); }
}
.fe-hero-inner {
  position: relative; z-index: 1;
  text-align: center; max-width: 820px; margin: 0 auto;
}
.fe-hero-eyebrow {
  display: inline-block; margin: 0 0 18px; padding: 6px 14px;
  background: rgba(81, 100, 255, 0.10); color: var(--fe-accent);
  border-radius: 999px; font-weight: 600; font-size: 0.8125rem;
  letter-spacing: 0.04em; text-transform: uppercase;
}
/* The .fe-hero-eyebrow rule above sets `display: inline-block`, which has
   higher specificity than the UA `[hidden] { display: none }` rule, so the
   `hidden` attribute would otherwise be silently ignored. Re-enable it. */
.fe-hero-eyebrow[hidden] { display: none; }
.fe-hero-heading {
  /* Hero font intentionally NOT reading --fe-font-heading — the hero is
     theme-independent; the .fe-hero-heading-fraunces / -inter modifier
     applied by the template carries the admin's explicit pick. */
  font-family: "Fraunces", "Inter", Georgia, serif; font-weight: 800;
  /* --fe-hero-h-size is a unitless scale factor (1 = 100%). Firefox rejects
     <percentage> / <percentage> in calc(), so the template passes a decimal. */
  --fe-hero-h-size: 1;
  font-size: calc(clamp(2.4rem, 6vw, 4.25rem) * var(--fe-hero-h-size));
  /* line-height was 1.05 — too tight for Fraunces; descenders (y/g/p) were
     getting clipped by the background-clip:text gradient box. 1.15 + a small
     padding-bottom gives the descenders enough vertical room without losing
     the dense look. */
  line-height: 1.15;
  padding-bottom: 0.08em;
  letter-spacing: -0.025em; margin: 0 0 20px;
  background: linear-gradient(180deg,
      var(--fe-hero-h-grad-s, #0f172a) 0%,
      var(--fe-hero-h-grad-e, #374151) 100%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* The hero font is independent of the theme — it always honours the
   admin's explicit pick (Inter or Fraunces) and never reads the
   semantic --fe-font-* variables. */
.fe-hero-heading-inter { font-family: "Inter", "Fraunces", sans-serif; font-weight: 800; }
.fe-hero-heading-fraunces { font-family: "Fraunces", "Inter", Georgia, serif; font-weight: 800; }
/* Dynamic text colors: when the admin enabled "Dynamic text colors", the
   server tags the section with one of these classes based on the bg's
   lightness. We override only `background-image` so the base rule's
   `background-clip: text` and `text-fill-color: transparent` remain in
   effect — using the `background:` shorthand here would reset clip to
   border-box and the heading would render as a rectangle. */
.fe-hero-text-light .fe-hero-heading {
  background-image: linear-gradient(180deg, #ffffff 0%, #e2e8f0 100%) !important;
}
.fe-hero-text-light .fe-hero-sub { color: rgba(255, 255, 255, 0.92) !important; }
.fe-hero-text-light .fe-hero-eyebrow {
  background: rgba(255, 255, 255, 0.18); color: #ffffff;
}
.fe-hero-text-dark .fe-hero-heading {
  background-image: linear-gradient(180deg, #0f172a 0%, #374151 100%) !important;
}
.fe-hero-text-dark .fe-hero-sub { color: #475569 !important; }
.fe-hero-text-dark .fe-hero-eyebrow {
  background: rgba(15, 23, 42, 0.08); color: #0f172a;
}
.fe-hero-sub {
  /* --fe-hero-sub-size is a unitless scale factor (1 = 100%), parallel
     to --fe-hero-h-size on the heading. Wrapping the natural size in a
     clamp() means the admin's % rides a fluid baseline that already
     scales sensibly across phones / tablets / desktops — bumping
     "Size" to 150% on a phone never grows beyond clamp's max anchor. */
  --fe-hero-sub-size: 1;
  font-size: calc(clamp(1rem, 1.45vw + 0.6rem, 1.1875rem) * var(--fe-hero-sub-size));
  line-height: 1.55;
  color: var(--fe-hero-sub-color, var(--fe-ink-muted));
  max-width: 620px; margin: 0 auto 32px;
}
/* Independent font pick for the subheading. Same allowlist as the
   heading (Fraunces / Inter); other modifier classes are no-ops. */
.fe-hero-sub-fraunces { font-family: "Fraunces", "Inter", Georgia, serif; font-weight: 400; }
.fe-hero-sub-inter    { font-family: "Inter", "Fraunces", sans-serif; font-weight: 400; }
.fe-hero-cta {
  display: inline-flex; gap: 14px; flex-wrap: wrap; justify-content: center;
}
.fe-btn {
  display: inline-flex; align-items: center; justify-content: center;
  /* Padding reads from the design tokens (Site → Design → Buttons) so
     admins can dial vertical (top/bottom) and horizontal (left/right)
     independently. Fallbacks match the historic 14px / 28px shipped
     before the tokens were wired so unstyled environments stay on the
     same visual. */
  padding: var(--fe-btn-padding-y, 14px) var(--fe-btn-padding-x, 28px);
  border-radius: 999px; font-weight: 600; font-size: 1rem;
  text-decoration: none; border: 1px solid transparent;
  transition: transform 150ms ease, box-shadow 150ms ease, background 150ms ease;
  cursor: pointer;
}
.fe-btn-primary,
.fe-page .fe-btn-primary,
.frontend-body .fe-btn-primary {
  background: var(--fe-color-btn-primary-bg, var(--fe-ink));
  color: var(--fe-color-btn-primary-text, #fff);
  /* Border resting + hover are admin-tunable from the Design tokens
     page (Buttons → Primary). Defaults match the bg colour so the
     border is invisible until the admin sets a contrasting colour. */
  border-width: var(--fe-btn-primary-border-width, 1px);
  border-style: solid;
  border-color: var(--fe-color-btn-primary-border, var(--fe-color-btn-primary-bg, transparent));
  /* Drop-shadow + hover lift + hover glow are admin-toggleable from
     the Design tokens page (Buttons group). Each token resolves to
     `none` when its toggle is off so the rule still applies cleanly. */
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
}
.fe-btn-primary:hover,
.fe-page .fe-btn-primary:hover,
.frontend-body .fe-btn-primary:hover {
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  background: var(--fe-color-btn-primary-hover-bg,
                  color-mix(in srgb, var(--fe-color-btn-primary-bg, var(--fe-ink)) 88%, black));
  color: var(--fe-color-btn-primary-text, #fff);
  border-width: var(--fe-btn-primary-hover-border-width, var(--fe-btn-primary-border-width, 1px));
  border-color: var(--fe-color-btn-primary-hover-border,
                    var(--fe-color-btn-primary-border, var(--fe-color-btn-primary-bg, transparent)));
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}
.fe-btn-ghost,
.fe-page .fe-btn-ghost,
.frontend-body .fe-btn-ghost {
  background: var(--fe-color-btn-secondary-bg, transparent);
  color: var(--fe-color-btn-secondary-text, var(--fe-ink));
  border-width: var(--fe-btn-secondary-border-width, 1px);
  border-style: solid;
  border-color: var(--fe-color-btn-secondary-border, var(--fe-border));
}

/* Yellow CTA — high-contrast attention-grabbing variant. Hardcoded
   yellow + near-black text by default (no design-token override) so
   the style stays distinct from the brand-driven primary button.
   Dark-mode value drops the lightness on the yellow so it doesn't
   torch the eyes on a dark page bg — golden-amber instead of
   highlighter — while keeping a yellow hue identity. */
.fe-btn-yellow,
.fe-page .fe-btn-yellow,
.frontend-body .fe-btn-yellow {
  background: #facc15;
  color: #1c1917;
  border-color: transparent;
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(202, 138, 4, 0.30));
}
.fe-btn-yellow:hover,
.fe-page .fe-btn-yellow:hover,
.frontend-body .fe-btn-yellow:hover {
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  background: #eab308;
  color: #0c0a09;
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(202, 138, 4, 0.42));
}
html[data-theme="dark"] .fe-btn-yellow,
html[data-theme="dark"] .fe-page .fe-btn-yellow,
html[data-theme="dark"] .frontend-body .fe-btn-yellow {
  background: #eab308;
  color: #0c0a09;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-btn-yellow:hover,
html[data-theme="dark"] .fe-page .fe-btn-yellow:hover,
html[data-theme="dark"] .frontend-body .fe-btn-yellow:hover {
  background: #facc15;
  color: #0c0a09;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}
.fe-btn-ghost:hover,
.fe-page .fe-btn-ghost:hover,
.frontend-body .fe-btn-ghost:hover {
  background: var(--fe-color-btn-secondary-hover-bg,
                  color-mix(in srgb, var(--fe-color-btn-secondary-bg, var(--fe-panel-soft)) 92%, black));
  color: var(--fe-color-btn-secondary-text, var(--fe-ink));
  border-width: var(--fe-btn-secondary-hover-border-width, var(--fe-btn-secondary-border-width, 1px));
  border-color: var(--fe-color-btn-secondary-hover-border, var(--fe-color-btn-secondary-text, var(--fe-ink)));
}
/* Light-mode override: align the hover border with the feature card's
   hover border (var(--fe-accent)) so the two affordances visually rhyme.
   Scoped to light mode only — dark mode keeps the secondary-text border
   for adequate contrast against the dark surface. The Recovery Blue
   theme has its own bespoke hover recipe further down and stays
   untouched (its rule is more specific). */
html[data-theme="light"] .fe-btn-ghost:hover,
html[data-theme="light"] .fe-page .fe-btn-ghost:hover,
html[data-theme="light"] .frontend-body .fe-btn-ghost:hover {
  border-color: var(--fe-accent, var(--fe-border));
}
/* Recovery Blue theme — secondary (.fe-btn-ghost) hover treatment.
   The body carries data-fe-theme="recovery-blue" when this theme is
   active (set in templates/frontend/base.html), so scoping the rule
   on that attribute keeps every other theme on the default hover and
   leaves Recovery Blue with its bespoke pale-blue surface + border.
   The light-mode values were specified by the user; the dark-mode
   parallel uses the same recipe shape (a tinted surface + a readable
   border) tuned for a dark page background. */
body[data-fe-theme="recovery-blue"] .fe-btn-ghost:hover {
  background: color-mix(in srgb, #e0e9f4 92%, #f3ecec);
  border-color: #afc2e4;
}
html[data-theme="dark"] body[data-fe-theme="recovery-blue"] .fe-btn-ghost:hover {
  background: color-mix(in srgb, #1f2a40 92%, #2c2222);
  border-color: #5b78ad;
}
.fe-btn { gap: 10px; }
.fe-btn-icon {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; --icon-size: 18px;
}
.fe-btn-icon .icon { width: var(--icon-size); height: var(--icon-size); }
.fe-btn-icon .icon-custom { object-fit: contain; }

/* ===== Quick links ===== */
.fe-quick {
  padding: 72px 0 40px;
  background: #fff;
}
.fe-quick-grid {
  display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.fe-quick-card {
  display: block; padding: 28px 24px; border-radius: 18px;
  background: var(--fe-color-card-secondary-bg, var(--fe-panel-soft));
  color: var(--fe-ink);
  text-decoration: none; position: relative;
  transition: transform 200ms ease, box-shadow 200ms ease, background 200ms ease;
  border: var(--fe-card-secondary-border-width, 1px) solid var(--fe-color-card-secondary-border, transparent);
}
.fe-quick-card:hover {
  transform: translateY(-4px);
  background: #fff;
  border-color: var(--fe-border);
  box-shadow: var(--fe-shadow);
}
.fe-quick-icon {
  width: 48px; height: 48px; display: grid; place-items: center;
  font-size: 1.5rem; margin-bottom: 14px; border-radius: 14px;
  background: var(--fe-gradient); color: #fff;
}
.fe-quick-card h3 {
  margin: 0 0 6px; font-size: 1.125rem; font-weight: 700;
  letter-spacing: -0.01em;
}
.fe-quick-card p {
  margin: 0; color: var(--fe-ink-muted); font-size: 0.9375rem;
}

/* ===== Sections shared ===== */
.fe-section {
  padding: 80px 0;
}
.fe-section + .fe-section { border-top: 1px solid var(--fe-border); }
/* Page-builder pages compose sections explicitly via the admin; the
   auto-divider above adds an uncontrollable hairline between adjacent
   section blocks (e.g. Features + FAQ in the same `.fe-pp-section`)
   which the admin can't see in the editor. Suppress it inside `.fe-pp`
   so border styling stays the admin's call — wrap blocks in a
   container if you want a divider. Homepage (no `.fe-pp` wrapper)
   keeps the original behaviour. */
.fe-pp .fe-section + .fe-section { border-top: 0; }
.fe-section-head {
  text-align: center; max-width: 640px; margin: 0 auto 44px;
}
.fe-section-head h2 {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: clamp(1.75rem, 3.6vw, 2.5rem); margin: 0 0 10px;
  letter-spacing: -0.02em;
}
.fe-section-head p {
  margin: 0; color: var(--fe-ink-muted); font-size: 1rem;
}
.fe-section-foot {
  text-align: center; margin-top: 32px;
}

/* ===== Meetings ===== */
.fe-meeting-grid {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  grid-auto-rows: 1fr;
  align-items: stretch;
}
.fe-meeting-card {
  position: relative;
  display: flex; flex-direction: column;
  padding: 22px 22px 20px; border-radius: 16px;
  background: var(--fe-color-card-primary-bg, #fff);
  border: var(--fe-card-primary-border-width, 1px) solid var(--fe-color-card-primary-border, var(--fe-accent));
  transition: transform 200ms ease, box-shadow 200ms ease;
}
.fe-meeting-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.10);
}
.fe-meeting-card h3 { color: inherit; }
.fe-meeting-card-link,
.fe-page .fe-meeting-card-link,
.fe-page .fe-meeting-card-link:hover,
.frontend-body .fe-meeting-card-link,
.frontend-body .fe-meeting-card-link:hover {
  color: inherit; text-decoration: none;
}
/* Stretched-link overlay — makes the whole card clickable while still
   leaving inner buttons (zoom / directions) clickable separately via
   their own z-index above this layer. */
.fe-meeting-card-link::after {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
}
.fe-meeting-card-cta {
  display: inline-block;
  align-self: flex-start;
  margin-top: 14px;
  transition: color 160ms ease;
}
.fe-meeting-card:hover .fe-meeting-card-cta { color: var(--fe-accent); }
.fe-meeting-card-actions {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 8px;
  margin-top: auto; padding-top: 14px;
}
.fe-meeting-card-actions .fe-btn,
.fe-page .fe-meeting-card-actions .fe-btn,
.frontend-body .fe-meeting-card-actions .fe-btn {
  flex: 1 1 0; min-width: 0;
  padding: 8px 14px; font-size: 0.875rem;
  text-align: center;
  box-shadow: none;
}
/* Meeting-card buttons opt out of the global hover ``lift`` (1-px
   translateY + drop shadow) used elsewhere on .fe-btn-primary — in a
   tight card grid the lift reads as jitter rather than affordance,
   and the surrounding card already provides hover feedback. The
   border itself never toggles (the base .fe-btn keeps a 1-px
   transparent border at all times), so this rule and the no-shadow
   sibling are the only sources of geometric shift on hover. */
.fe-meeting-card-actions .fe-btn:hover { box-shadow: none; transform: none; }
.fe-meeting-type {
  display: inline-block; align-self: flex-start;
  padding: 3px 10px; border-radius: 999px;
  font-size: 0.6875rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
}
.fe-meeting-type-in_person { background: #dcfce7; color: #14532d; }
.fe-meeting-type-online    { background: #dbeafe; color: #1e3a8a; }
.fe-meeting-type-hybrid    { background: #fef3c7; color: #78350f; }
.fe-meeting-type-archived  { background: #e2e8f0; color: #334155; }
.fe-meeting-card h3 {
  font-family: var(--fe-font-heading); font-weight: 600;
  margin: 12px 0 10px; font-size: 1.25rem; line-height: 1.2;
  letter-spacing: -0.015em;
}
.fe-meeting-sched {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.9375rem; color: var(--fe-ink-muted);
}
.fe-meeting-day { font-weight: 600; color: var(--fe-ink); }

/* Per-meeting logo, top-right of each card. Card already has
   position: relative, so absolute positioning anchors here. The card
   reserves right padding on the chip/title rows when a logo is present
   so long names wrap rather than colliding with the artwork. */
.fe-meeting-card-logo {
  position: absolute;
  top: 16px; right: 16px;
  width: 80px; height: 80px;
  z-index: 1;
  pointer-events: none;
}
.fe-meeting-card-logo img {
  display: block;
  width: 100%; height: 100%;
  object-fit: contain;
  border-radius: 10px;
}
.fe-meeting-card.has-logo > h3 { padding-right: 92px; }
@media (max-width: 560px) {
  .fe-meeting-grid { gap: 14px; }
  .fe-meeting-card { padding: 18px 16px 16px; }
  .fe-meeting-card-logo { width: 64px; height: 64px; top: 12px; right: 12px; }
  .fe-meeting-card.has-logo > h3 { padding-right: 72px; }
}

/* ===== About ===== */
.fe-about {
  background: var(--fe-panel-soft);
}
.fe-about-inner {
  display: grid; gap: 56px; align-items: start;
  grid-template-columns: 1.1fr 0.9fr;
}
.fe-about-text h2 {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: clamp(1.75rem, 3.6vw, 2.5rem); margin: 0 0 18px;
  letter-spacing: -0.02em;
}
.fe-about-body {
  font-size: 1.0625rem; line-height: 1.65; color: var(--fe-ink);
}
.fe-about-body > :first-child { margin-top: 0; }
.fe-about-body > :last-child { margin-bottom: 0; }
.fe-about-body p { margin: 0 0 1em; }
.fe-about-body h3 {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: 1.4rem; margin: 1.6em 0 .4em;
}
.fe-about-side {
  display: flex; flex-direction: column; gap: 18px;
}
.fe-about-pillar {
  display: grid; grid-template-columns: auto 1fr; gap: 18px; align-items: start;
  padding: 22px 24px; background: #fff;
  border-radius: 14px; box-shadow: var(--fe-shadow);
}
.fe-about-pillar-num {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: 1.75rem; line-height: 1;
  background: var(--fe-gradient); -webkit-background-clip: text;
  background-clip: text; -webkit-text-fill-color: transparent;
}
.fe-about-pillar h4 {
  margin: 0 0 4px; font-size: 1rem; font-weight: 700;
  letter-spacing: -0.005em;
}
.fe-about-pillar p {
  margin: 0; color: var(--fe-ink-muted); font-size: 0.9375rem;
  line-height: 1.5;
}

/* ===== Contact ===== */
.fe-contact {
  background: var(--fe-ink); color: #fff; text-align: center;
  position: relative; overflow: hidden;
}
.fe-contact::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at 20% 20%, rgba(81,100,255,0.32) 0%, transparent 45%),
              radial-gradient(circle at 80% 70%, rgba(42,212,195,0.25) 0%, transparent 45%);
  pointer-events: none;
}
.fe-contact-inner { position: relative; z-index: 1; max-width: 640px; margin: 0 auto; }
.fe-contact h2 {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: clamp(1.75rem, 3.6vw, 2.5rem); margin: 0 0 16px;
  letter-spacing: -0.02em; color: #fff;
}
.fe-contact-body {
  font-size: 1.0625rem; line-height: 1.65; color: #cbd5e1;
  margin-bottom: 28px;
}
.fe-contact-body > :first-child { margin-top: 0; }
.fe-contact-body > :last-child { margin-bottom: 0; }
.fe-contact-body a { color: var(--fe-accent-2); }
.fe-contact-card {
  display: inline-block; text-align: left;
  padding: 22px 28px; border-radius: 16px;
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12);
  min-width: 280px; backdrop-filter: blur(4px);
}
.fe-contact-role {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--fe-accent-2);
}
.fe-contact-name {
  font-size: 1.375rem; font-weight: 700; margin: 4px 0 10px;
  letter-spacing: -0.015em; font-family: var(--fe-font-heading);
}
.fe-contact-lines { color: #cbd5e1; font-size: 0.9375rem; }
.fe-contact-lines a { color: #fff; text-decoration: none; }
.fe-contact-lines a:hover { color: var(--fe-accent-2); }

/* ===== Footer — shared base =====
   `position: relative; overflow: hidden` provides a positioning
   context for the optional bg layers (frosty blob wrapper, particle
   canvas, sinewave gradient img) inserted by the shared bg macro. */
.fe-footer {
  position: relative; overflow: hidden;
  padding: 56px 0 36px; background: #fff;
  border-top: 1px solid var(--fe-border);
  color: var(--fe-ink);
  /* Admin-tunable footer base font-size. The admin sets a percentage
     (50-200) on the Footer settings card; the macro emits the
     `--fe-footer-scale` CSS variable on this element. Descendant
     text elements declare their font-size in `em` so they cascade
     from this scaled base. Mobile media queries below cap how high
     the scale can climb so a 200% setting doesn't blow out a phone. */
  font-size: calc(1rem * var(--fe-footer-scale, 1));
}
@media (max-width: 980px) {
  .fe-footer { font-size: calc(1rem * min(var(--fe-footer-scale, 1), 1.15)); }
}
@media (max-width: 600px) {
  .fe-footer { font-size: calc(1rem * min(var(--fe-footer-scale, 1), 1)); }
}
/* Sinewave variant — fallback gradient before the JS paints the
   sinewave SVG. Mirrors `.fe-hero-bg-sinewave` so the footer reuses
   the hero's effect. */
.fe-footer-bg-sinewave { background: linear-gradient(180deg, #16c2ba 0%, #5a1ce5 100%); }
/* Footer inner content sits above the optional bg layers. */
.fe-footer-inner {
  display: block;
  margin-left: auto; margin-right: auto;
  position: relative; z-index: 2;
}
/* Effect overlays in the footer get clipped by the parent (overflow:
   hidden above). Particles and blobs already use absolute positioning
   from the shared hero CSS. */
.fe-footer-bg-effects { z-index: 0; }
.fe-footer-particles { z-index: 1; }
.fe-footer-brand {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
}
/* Stacked-layout variant centres the logo + tagline horizontally — the
   stacked footer's outer wrapper applies this modifier. */
.fe-footer-brand--center { align-items: center; text-align: center; }
.fe-footer-logo { width: 44px; height: 44px; object-fit: contain; }
.fe-footer-name { font-weight: 700; font-size: 1.0625em; letter-spacing: -0.01em; }
.fe-footer-tagline { color: var(--fe-ink-muted); font-size: 0.875em; line-height: 1.5; margin-top: 2px; }
.fe-footer-cols {
  display: grid; gap: 32px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.fe-footer-col-title {
  font-weight: 700; font-size: 0.78em; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--fe-ink-soft);
  margin: 0 0 12px;
}
.fe-footer-link-list { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 6px; }
.fe-footer-link-list a {
  color: var(--fe-ink-muted); text-decoration: none; font-size: 0.9375em;
  display: inline-block; padding: 2px 0;
  transition: color 120ms ease;
}
.fe-footer-link-list a:hover { color: var(--fe-ink); text-decoration: underline; }

.fe-footer-copy {
  color: var(--fe-ink-soft); font-size: 0.8125em; line-height: 1.55;
}
.fe-footer-copy a { color: inherit; text-decoration: underline; transition: color 120ms ease; }
/* Powered-by attribution link — rendered as a subtle pill chip whose
   surface tint comes from the surrounding text colour (so it stays
   legible in light, dark, and force-dark). Hover bumps the surface
   alpha and pulls the text colour from the link design tokens. */
.fe-footer-block-powered-by a {
  display: inline-block;
  padding: 2px 10px;
  margin-left: 4px;
  border-radius: 999px;
  border: 1px solid currentColor;
  background: color-mix(in srgb, currentColor 10%, transparent);
  text-decoration: none;
  font-weight: 600;
  font-size: inherit;
  line-height: 1.4;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}
.fe-footer-block-powered-by a:hover {
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-color-brand, var(--fe-accent))));
  background: color-mix(in srgb, currentColor 18%, transparent);
  border-color: currentColor;
}

.fe-footer-secondary-nav {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; gap: 6px 18px;
}
.fe-footer-secondary-nav a {
  color: var(--fe-ink-soft); text-decoration: none; font-size: 0.8125em;
  transition: color 120ms ease;
}
.fe-footer-secondary-nav a:hover { color: var(--fe-ink); text-decoration: underline; }

.fe-footer-social {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.fe-footer-social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 999px;
  background: var(--fe-panel-soft); color: var(--fe-ink-muted);
  border: 1px solid var(--fe-border);
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease, transform 140ms ease;
}
.fe-footer-social a:hover {
  background: var(--fe-accent, var(--fe-ink));
  color: #fff; border-color: transparent;
  transform: translateY(-1px);
}
.fe-footer-social .icon { width: 18px; height: 18px; }

.fe-footer-bottom {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  margin-top: 32px; padding-top: 24px;
  border-top: 1px solid var(--fe-border);
}

/* ----- Classic variant ----- */
.fe-footer-classic .fe-footer-classic-grid {
  display: grid; gap: 28px 36px; align-items: start;
  grid-template-columns: minmax(220px, 1.1fr) 2fr auto;
}
.fe-footer-classic .fe-footer-copy { text-align: right; }
@media (max-width: 880px) {
  .fe-footer-classic .fe-footer-classic-grid { grid-template-columns: 1fr; }
  .fe-footer-classic .fe-footer-copy { text-align: left; }
}

/* ----- Minimal variant — single line ----- */
.fe-footer-minimal { padding: 22px 0; }
.fe-footer-minimal .fe-footer-minimal-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}

/* ----- Stacked variant — centered ----- */
.fe-footer-stacked { text-align: center; }
.fe-footer-stacked .fe-footer-brand--center {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  margin: 0 auto 36px;
}
.fe-footer-stacked .fe-footer-cols--center {
  margin: 0 auto 32px; max-width: 880px;
  text-align: left;
}
.fe-footer-stacked .fe-footer-cols--center .fe-footer-col {
  text-align: center;
}
.fe-footer-stacked .fe-footer-cols--center .fe-footer-link-list { align-items: center; }
.fe-footer-stacked .fe-footer-social--center { justify-content: center; margin: 0 auto 24px; }
.fe-footer-stacked .fe-footer-bottom--center {
  flex-direction: column; gap: 8px; text-align: center;
}
.fe-footer-stacked .fe-footer-bottom--center .fe-footer-secondary-nav { justify-content: center; }

/* ----- Meeting locations block (used inside custom layouts) -----
   Cards use a frosted-glass surface (translucent bg + backdrop-blur)
   so they sit gracefully over any footer bg style — solid, gradient,
   sinewave, or frosty blobs all show through tinted by the card's
   surface alpha. The whole card transforms on hover regardless of
   whether a link is attached; the stretched-link <a> covers the card
   when one is set, while the inline "Get directions" button stays
   above it via a higher z-index. */
.fe-footer-locations-heading {
  font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 700; font-size: 1.0625em; letter-spacing: 0;
  /* Inherit from the surrounding footer so the heading colour tracks
     the dark / force-dark / light cascade — explicit colour here would
     leave it black on a forced-dark footer. */
  color: inherit;
  margin: 0 0 14px;
}
.fe-footer-locations-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.fe-footer-location-card {
  position: relative;
  display: flex; gap: 12px;
  padding: 16px 18px; border-radius: 12px;
  /* Default (light page, light footer): a soft dark tint on the white
     footer reads as a faintly raised card. Dark / force-dark below
     swap the surface to a darker, opaque-ish navy so the card stands
     out against the dark footer instead of being a near-transparent
     ghost. */
  background: rgba(15, 23, 42, 0.04);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  border: 1px solid rgba(15, 23, 42, 0.10);
  text-decoration: none;
  transition: background 140ms ease, border-color 140ms ease,
              transform 140ms ease, box-shadow 140ms ease;
}
.fe-footer-location-card:hover {
  background: rgba(15, 23, 42, 0.07);
  border-color: color-mix(in srgb, var(--fe-accent, #0f172a) 45%, transparent);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.12);
}
/* Stretched link — covers the card so the entire surface is clickable
   when a `url` is set. z-index 1 keeps it below the body content on
   the stacking layer so inline links (Get directions) remain
   interactive at z-index 2. */
.fe-footer-location-card-link {
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
}
.fe-footer-location-icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 10px;
  background: color-mix(in srgb, var(--fe-accent, var(--fe-ink)) 16%, transparent);
  color: var(--fe-accent, var(--fe-ink));
}
.fe-footer-location-icon .icon { width: 18px; height: 18px; }
.fe-footer-location-body {
  position: relative; z-index: 2;
  flex: 1; min-width: 0;
  /* Flex column lets the directions pill push to the bottom via
     `margin-top: auto`, so cards in the same row line up their CTA
     evenly even when their address / note copy is uneven heights. */
  display: flex; flex-direction: column;
}
/* Guarantee 1rem of breathing room between whatever sits directly
   above the directions pill (note / website / address) and the pill
   itself. `:has(+ ...)` matches any element followed by the pill, so
   the spacing rule rides whichever element happens to be the last
   non-pill child. Combined with `margin-top: auto` on the pill, the
   1rem is the minimum gap; tall cards still push the pill further
   down via the auto margin. */
.fe-footer-location-body > *:has(+ .fe-footer-location-directions) {
  margin-bottom: 1rem;
}
.fe-footer-location-name {
  font-weight: 700; font-size: 0.95em; letter-spacing: 0;
  color: var(--fe-ink); margin-bottom: 6px;
}
.fe-footer-location-addr {
  font-size: 0.9375em; color: var(--fe-ink-muted); line-height: 1.5;
}
.fe-footer-location-note {
  margin-top: 6px; font-size: 0.8125em; color: var(--fe-ink-soft);
  font-style: italic;
}
/* Website link — italicized URL with a trailing right-arrow that
   signals "this leaves the site". Sits above the directions pill,
   z-index above the card's stretched link so the click goes here.
   Inherits colour from the surrounding text cascade so it adapts to
   light/dark/force-dark. Hover tints to the design-token link colour. */
.fe-footer-location-website {
  position: relative; z-index: 2;
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: 6px; font-size: 0.8125em; font-style: italic;
  /* Inherit so the URL link tracks whatever the card's body text
     resolves to in light / dark / force-dark — visually it reads as
     a continuation of the address/note copy until hover. */
  color: inherit;
  text-decoration: none;
  align-self: flex-start;
  transition: color 120ms ease;
}
.fe-footer-location-website-arrow {
  font-style: normal; font-weight: 600;
  transition: transform 120ms ease;
}
.fe-footer-location-website:hover,
.fe-footer-location-website:focus-visible {
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-color-brand, var(--fe-accent))));
  text-decoration: underline;
}
.fe-footer-location-website:hover .fe-footer-location-website-arrow,
.fe-footer-location-website:focus-visible .fe-footer-location-website-arrow {
  transform: translateX(2px);
}
.fe-footer-location-note p { margin: 0 0 0.4em; }
.fe-footer-location-note p:last-child { margin: 0; }
.fe-footer-location-note a { color: inherit; text-decoration: underline; }
.fe-footer-location-note a:hover {
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-color-brand, var(--fe-accent))));
}
/* Get directions — pill button. Always shows its surface + border
   (so it reads as a tappable button at rest, not just on hover); the
   stacking layer keeps it above the card's stretched link
   (z-index 2 vs 1) so clicking it doesn't trigger the card-link
   navigation. Hover only nudges the colour — no underline ever.
   `.fe-footer-admin-login` shares the same pill recipe so the admin-
   login footer block reads identically — `margin-top:auto` is a no-op
   outside the meeting-location card flex column, so the pill simply
   sits inline in its footer column. */
.fe-footer-location-directions,
.fe-footer-admin-login {
  position: relative; z-index: 2;
  display: inline-flex; align-items: center; gap: 6px;
  /* margin-top: auto pushes this pill to the bottom of the flex
     column body, so every card's CTA aligns evenly across the row.
     The 1rem floor between this pill and the previous sibling is
     enforced by the `*:has(+ .fe-footer-location-directions)` rule
     on `.fe-footer-location-body` above. */
  margin-top: auto; padding: 6px 12px; border-radius: 999px;
  /* align-self keeps the pill from stretching the full body width. */
  align-self: flex-start;
  font-size: 0.8125em; font-weight: 600;
  color: inherit; text-decoration: none;
  background: color-mix(in srgb, var(--fe-accent, var(--fe-ink)) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent, var(--fe-ink)) 50%, transparent);
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.fe-footer-location-directions .icon,
.fe-footer-admin-login .icon { width: 13px; height: 13px; }
.fe-footer-location-directions:hover,
.fe-footer-location-directions:focus-visible,
.fe-footer-admin-login:hover,
.fe-footer-admin-login:focus-visible {
  background: color-mix(in srgb, var(--fe-accent, var(--fe-ink)) 28%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, var(--fe-ink)) 70%, transparent);
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-color-brand, var(--fe-accent))));
  text-decoration: none;
}
/* Dark / force-dark — surface darkens to a deeper navy (#0a0f24-ish)
   so each card reads as a recessed panel against the footer's #0b1026
   bg, with a visible white-tinted border to set it off. Hover lifts
   the surface a notch and brightens the border. */
html[data-theme="dark"] .fe-footer-location-card,
body.fe-footer-force-dark .fe-footer-location-card {
  background: rgba(0, 0, 0, 0.28);
  border-color: rgba(255, 255, 255, 0.14);
}
html[data-theme="dark"] .fe-footer-location-card:hover,
body.fe-footer-force-dark .fe-footer-location-card:hover {
  background: rgba(0, 0, 0, 0.36);
  border-color: rgba(122, 163, 255, 0.55);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}
html[data-theme="dark"] .fe-footer-location-name { color: #e2e8f0; }
html[data-theme="dark"] .fe-footer-location-addr { color: #cbd5e1; }
html[data-theme="dark"] .fe-footer-location-note { color: #94a3b8; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-location-name { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-location-addr { color: #f1f5f9; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-location-note { color: #cbd5e1; }

/* ----- Contact section block ----- */
.fe-footer-contact-grid {
  display: grid; gap: 24px 36px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.fe-footer-contact-pane h3.fe-footer-contact-heading {
  font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 700; font-size: 0.78em; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--fe-ink); margin: 0 0 8px;
}
.fe-footer-contact-body {
  font-size: 0.9375em; line-height: 1.55; color: var(--fe-ink-muted);
}
.fe-footer-contact-body p { margin: 0 0 0.5em; }
.fe-footer-contact-body p:last-child { margin: 0; }
.fe-footer-contact-body a { color: var(--fe-color-link, var(--fe-accent)); }
html[data-theme="dark"] .fe-footer-contact-pane h3.fe-footer-contact-heading { color: #e2e8f0; }
html[data-theme="dark"] .fe-footer-contact-body { color: #cbd5e1; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-contact-pane h3.fe-footer-contact-heading { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-contact-body { color: #f1f5f9; }

/* ----- Custom variant — rows + columns dispatcher ----- */
.fe-footer-custom .fe-footer-custom-stack {
  display: flex; flex-direction: column; gap: 28px;
}
.fe-footer-custom .fe-footer-custom-row {
  display: grid;
  gap: 24px 36px;
}
.fe-footer-custom .fe-footer-custom-row--cols-1 { grid-template-columns: 1fr; }
.fe-footer-custom .fe-footer-custom-row--cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fe-footer-custom .fe-footer-custom-row--cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fe-footer-custom .fe-footer-custom-row--cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 980px) {
  .fe-footer-custom .fe-footer-custom-row--cols-3,
  .fe-footer-custom .fe-footer-custom-row--cols-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 600px) {
  .fe-footer-custom .fe-footer-custom-row { grid-template-columns: 1fr !important; }
}
.fe-footer-custom .fe-footer-custom-col {
  display: flex; flex-direction: column; gap: 14px;
  min-width: 0;
}
.fe-footer-custom .fe-footer-block-divider {
  border: 0; border-top: 1px solid var(--fe-border); margin: 0;
}
.fe-footer-custom .fe-footer-block-spacer { height: 16px; }
.fe-footer-custom .fe-footer-cols { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
html[data-theme="dark"] .fe-footer-custom .fe-footer-block-divider,
body.fe-footer-force-dark .fe-footer-custom .fe-footer-block-divider { border-top-color: #1f2a44; }

/* ----- Mega variant — 4-col wall ----- */
.fe-footer-mega .fe-footer-mega-grid {
  display: grid; gap: 36px;
  grid-template-columns: minmax(220px, 1.3fr) repeat(3, minmax(140px, 1fr));
  margin-bottom: 16px;
}
.fe-footer-mega .fe-footer-mega-brand {
  display: flex; flex-direction: column; align-items: flex-start; gap: 14px;
}
.fe-footer-mega .fe-footer-mega-brand .fe-footer-social { margin-top: 4px; }
@media (max-width: 980px) {
  .fe-footer-mega .fe-footer-mega-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 560px) {
  .fe-footer-mega .fe-footer-mega-grid { grid-template-columns: 1fr; }
}

/* ----- Dark mode -----
   Each rule applies in TWO situations:
   1. The page is in dark mode (`html[data-theme="dark"]`).
   2. The admin set the footer's background mode to 'dark', which adds
      `body.fe-footer-force-dark` regardless of the page theme. This
      lets the footer always look dark even when the rest of the site
      is in light mode — useful for sites that want a moody footer
      band against an otherwise-bright page.
   `:where()` would lower specificity but isn't necessary here; both
   selectors carry the same weight. #}
/* Dark mode (page is dark) — text colors are DIMMED so the footer
   reads as a quiet, low-contrast band against the all-dark page. */
html[data-theme="dark"] .fe-footer {
  background: #0b1026; border-top-color: #1f2a44; color: #cbd5e1;
}
html[data-theme="dark"] .fe-footer-name { color: #e2e8f0; }
html[data-theme="dark"] .fe-footer-tagline,
html[data-theme="dark"] .fe-footer-link-list a,
html[data-theme="dark"] .fe-footer-secondary-nav a,
html[data-theme="dark"] .fe-footer-copy { color: #94a3b8; }
html[data-theme="dark"] .fe-footer-link-list a:hover,
html[data-theme="dark"] .fe-footer-secondary-nav a:hover { color: #e2e8f0; }
html[data-theme="dark"] .fe-footer-col-title { color: #cbd5e1; }
html[data-theme="dark"] .fe-footer-bottom { border-top-color: #1f2a44; }
html[data-theme="dark"] .fe-footer-social a {
  background: #131a33; border-color: transparent; color: #94a3b8;
}
html[data-theme="dark"] .fe-footer-social a:hover {
  background: #7aa3ff; color: #0b1026;
}

/* Forced-dark footer on a LIGHT page — bg is dark but text stays
   BRIGHT so it pops against the dark surface instead of reading as a
   "dim afterthought" (the dark-mode dimmed colors above would look
   washed-out when the rest of the page is bright). The `html:not(...)`
   guard keeps these rules from competing with the dark-mode block —
   when the page is dark, only the dark-mode rules apply. */
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer {
  background: #0b1026; border-top-color: #1f2a44; color: #f8fafc;
}
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-name { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-tagline,
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-link-list a,
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-secondary-nav a,
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-copy { color: #f1f5f9; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-link-list a:hover,
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-secondary-nav a:hover { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-col-title { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-bottom { border-top-color: #1f2a44; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-social a {
  background: #131a33; border-color: transparent; color: #f1f5f9;
}
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-social a:hover {
  background: #7aa3ff; color: #0b1026;
}

/* ===== Responsive ===== */
@media (max-width: 880px) {
  .fe-nav { display: none; position: absolute; left: 0; right: 0; top: 72px;
    flex-direction: column; gap: 0; background: #fff; border-bottom: 1px solid var(--fe-border);
    padding: 8px 20px 16px; box-shadow: 0 10px 24px rgba(15,23,42,0.10); }
  .fe-header.fe-nav-open .fe-nav { display: flex; }
  .fe-nav a { padding: 12px 14px; }
  .fe-nav-cta { margin: 8px 14px 0; text-align: center; }
  .fe-menu-btn { display: inline-flex; }
  .fe-hero {
    padding: 80px 0 72px;
    /* Mobile uses its own admin-controlled height when set, falling
       back to the desktop value (so a single tall-hero opt-in still
       reads as tall on phones), then to the natural padding. */
    min-height: var(--fe-hero-min-h-mobile, var(--fe-hero-min-h, auto));
  }
  .fe-about-inner { grid-template-columns: 1fr; gap: 32px; }
  .fe-footer-inner { grid-template-columns: 1fr; gap: 18px; }
  .fe-footer-copy { text-align: left; }
  .fe-section { padding: 56px 0; }
}
@media (max-width: 480px) {
  .fe-brand-name { font-size: 1rem; }
}

/* ========================================================================
   MEGA MENU
   Full-width dropdown that slides down under the header. Header stays
   crisp; page content blurs via a backdrop sibling with backdrop-filter.
   ======================================================================== */
.fe-megamenu {
  position: fixed; left: 0;
  /* ``--fe-mm-gutter`` is the inline scrollbar gutter width set on
     the body when the menu opens — pulls the panel's right edge in
     so it lines up with the body's content edge instead of extending
     into the freed scrollbar gutter. Falls back to 0 (the previous
     behaviour) when the var isn't set. */
  right: var(--fe-mm-gutter, 0);
  top: var(--fe-header-full-h, 108px);
  z-index: 35;
  padding: 40px 0 48px;
  box-shadow: 0 24px 48px rgba(15, 23, 42, 0.25),
              0 4px 12px rgba(15, 23, 42, 0.12);
  pointer-events: none;
  opacity: 0;
  transform: translateY(-12px);
  /* No default transition — opt in via the .fe-megamenu-panel-fade
     modifier (admin toggle in Frontend → Navigation → Mega menu
     appearance). When the modifier isn't present the panel snaps
     in / out without a fade. */
  /* Tall panels on small screens need internal scroll so the user can reach
     every link. The panel is capped to the remaining viewport below the header
     and scrolls its own overflow; a body-level lock below stops the page from
     tug-of-warring with it. */
  max-height: calc(100vh - var(--fe-header-full-h, 108px));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.fe-megamenu.open { pointer-events: auto; opacity: 1; transform: translateY(0); }
/* Panel fade-in modifier — added by the public template when
   `frontend_megamenu_panel_fade` is on. Duration drives off
   `--fe-mm-fade-ms` (admin-controlled in the Mega menu appearance
   panel; default 180 ms). Independent of the staggered link reveal
   below, so admins can mix-and-match (e.g. snappy panel + slow
   link stagger). */
.fe-megamenu-panel-fade {
  transition: opacity var(--fe-mm-fade-ms, 180ms) ease,
              transform var(--fe-mm-fade-ms, 180ms) cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.fe-megamenu-open { overflow: hidden; }
/* On mobile the mega menu covers the full viewport width edge-to-edge, so the
   inline bottom-radius from the admin appearance controls looks out of place.
   Flush the bottom corners; top corners are already 0 on DCCMA (flush to the
   header) and are set on Classic's inner card below. */
@media (max-width: 880px) {
  .fe-megamenu-recovery-blue {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
  }
  .fe-megamenu-classic-card {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
  }
}
.fe-megamenu-inner {
  max-width: 1400px; margin: 0 auto; padding: 0 40px;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 48px;
}
.fe-megamenu-heading {
  font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif; font-weight: 600;
  font-size: 1.25rem; margin: 0 0 16px; letter-spacing: -0.01em;
  color: inherit; opacity: 0.95;
}
.fe-megamenu-links { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 2px; }
.fe-megamenu-link {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 9px 14px 9px 10px; border-radius: 8px;
  color: inherit; text-decoration: none;
  font-size: calc(var(--fe-mm-link-base, 1.2rem) * var(--fe-mm-link-scale, 1));
  font-weight: 500;
  transition: background 140ms ease, transform 140ms ease, padding 140ms ease;
}
.fe-megamenu-link:hover {
  background: rgba(255, 255, 255, 0.14);
  padding-left: 18px;  /* slide right on hover */
  text-decoration: none;
}
/* Per-link size override: the rendered <a> sets --fe-mm-link-scale
   inline (when the admin's per-link "Override size" toggle is on),
   and the calc() in .fe-megamenu-link multiplies the theme base by
   that value. The cascaded value from the megamenu root is overridden
   only for the link that opted in. The legacy small/large modifier
   classes were retired in favour of this percent-based override. */
/* Per-link color override. The template emits --link-color-light (the chosen
   hex) and --link-color-dark (an auto-derived variant suitable for a dark
   background). The dark-mode selector switches to the dark value so the link
   stays readable in both themes. */
.fe-megamenu-link-colored { color: var(--link-color-light); }
html[data-theme="dark"] .fe-megamenu-link-colored { color: var(--link-color-dark); }
.fe-megamenu-icon { display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; opacity: 0.9; --icon-size: 16px; }
.fe-megamenu-icon .icon { width: var(--icon-size); height: var(--icon-size); }
.fe-megamenu-icon .icon-custom { object-fit: contain; }

/* DCCMA mega-menu search widget — pill input that spans the column.
   Sits on the blue panel, so a light fill + dark text gives contrast.
   Non-functional for now; just renders the widget. */
.fe-megamenu-search {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 16px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.95); color: #0f172a;
  border: 1px solid rgba(255, 255, 255, 0.35);
  margin: 6px 0;
}
.fe-megamenu-search-icon { display: inline-flex; flex: 0 0 auto; color: #64748b; }
.fe-megamenu-search-icon .icon { width: 18px; height: 18px; }
.fe-megamenu-search-input {
  flex: 1 1 auto; min-width: 0;
  border: 0; outline: 0; background: transparent; color: inherit;
  font: inherit; font-size: 1rem; padding: 0;
}
.fe-megamenu-search-input::placeholder { color: #94a3b8; }
.fe-megamenu-search-input::-webkit-search-cancel-button { cursor: pointer; }
html[data-theme="dark"] .fe-megamenu-search {
  background: #1e293b; color: #e2e8f0; border-color: #334155;
}
html[data-theme="dark"] .fe-megamenu-search-icon { color: #94a3b8; }
html[data-theme="dark"] .fe-megamenu-search-input::placeholder { color: #64748b; }
/* Label shrinks to its text width so icon-after flows right after it with the
   row gap, instead of being pushed to the far right edge by a stretched label. */
.fe-megamenu-label { flex: 0 1 auto; min-width: 0;
  white-space: normal; overflow-wrap: anywhere; }
.fe-megamenu-chevron { display: inline-flex; align-items: center; flex: 0 0 auto;
  margin-left: auto;       /* pin to the right edge */
  /* Inherit text colour via currentColor on the SVG stroke and run
     at full opacity so the chevron renders the same shade as the
     surrounding link label. */
  color: inherit;
  opacity: 1;
  transform: translateX(0);
  transition: transform 140ms ease; }
.fe-megamenu-chevron .icon { width: 20px; height: 20px; }
.fe-megamenu-link:hover .fe-megamenu-chevron { transform: translateX(3px); }

/* ---------- DCCMA mega menu blocks (title / button / section) ---------- */
.fe-megamenu-blocks { display: flex; flex-direction: column; gap: 2px; }
/* Recovery Blue theme bakes its baseline heading + link sizes as CSS
   vars on the megamenu root so the percent-scale slider can multiply
   them via calc(). When the slider rests at 100 % the scale var is
   absent and calc() reduces to the base value (no override). */
.fe-megamenu-recovery-blue { --fe-mm-heading-base: 2rem; --fe-mm-link-base: 1.2rem; }
.fe-megamenu-block-title {
  font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  font-weight: 600;
  font-size: calc(var(--fe-mm-heading-base, 2rem) * var(--fe-mm-heading-scale, 1));
  margin: 14px 10px 4px;
  letter-spacing: -0.5px; color: #fff;
}
.fe-megamenu-blocks > .fe-megamenu-block-title:first-child { margin-top: 0; }
.fe-megamenu-block-section { display: flex; align-items: center; gap: 10px;
  margin: 14px 10px 8px; color: inherit; opacity: 0.75; }
.fe-megamenu-block-section-line { flex: 1 1 auto; height: 1px;
  background: currentColor; opacity: 0.32; }
.fe-megamenu-block-section-label { font-size: 0.75rem; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
.fe-megamenu-block-btn { display: inline-flex; align-items: center; gap: 8px;
  margin: 6px 10px; padding: 9px 16px; font-weight: 600; font-size: 0.9375rem;
  text-decoration: none; color: #fff; background: rgba(255,255,255,0.18);
  border: 1px solid rgba(255,255,255,0.28); align-self: flex-start;
  transition: background 140ms ease, transform 140ms ease; }
.fe-megamenu-block-btn:hover { background: rgba(255,255,255,0.28); text-decoration: none;
  transform: translateY(-1px); }
.fe-megamenu-block-btn-pill { border-radius: 999px; }
.fe-megamenu-block-btn-rounded { border-radius: 1rem; }

/* DCCMA-only: buttons inside a mega-menu column stretch to the column's
   full width with the label + icons centered inside. */
.fe-megamenu-recovery-blue .fe-megamenu-block-btn {
  align-self: stretch;
  justify-content: center;
  text-align: center;
}

/* Staggered reveal — when the admin toggles "Staggered reveal animation" on,
   each direct child of a column's .fe-megamenu-blocks list animates in with
   a short fade + slide-down + rotate as the panel opens. Each element sets
   its own --i via inline style (row index within the column) so rows cascade
   while columns run in parallel. The animation is keyed to the .open class
   on the panel so it re-triggers every time the menu is hovered open. */
@keyframes fe-mm-reveal {
  from {
    opacity: 0;
    transform: translateY(-14px) rotate(-5deg);
  }
  to {
    opacity: 1;
    transform: translateY(0) rotate(0);
  }
}
.fe-megamenu-recovery-blue.fe-megamenu-animate .fe-megamenu-blocks > * {
  opacity: 0;
  transform-origin: top left;
  will-change: opacity, transform;
}
.fe-megamenu-recovery-blue.fe-megamenu-animate.open .fe-megamenu-blocks > * {
  animation: fe-mm-reveal var(--fe-mm-reveal-ms, 320ms) cubic-bezier(0.2, 0.8, 0.2, 1) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
@media (prefers-reduced-motion: reduce) {
  .fe-megamenu-recovery-blue.fe-megamenu-animate .fe-megamenu-blocks > *,
  .fe-megamenu-recovery-blue.fe-megamenu-animate.open .fe-megamenu-blocks > * {
    opacity: 1;
    transform: none;
    animation: none;
  }
}

/* ---------- Mega menu, Classic style ---------- */
.fe-megamenu-classic { padding: 12px 0 0;
  background: transparent; box-shadow: none; }
.fe-megamenu-classic-card {
  max-width: 960px; margin: 0 auto; padding: 28px 32px;
  background: #fff; color: #0f172a;
  border: 1px solid rgba(15, 23, 42, 0.08);
  box-shadow: 0 16px 40px rgba(15, 23, 42, 0.14),
              0 2px 8px rgba(15, 23, 42, 0.06);
}
.fe-megamenu-classic-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0; align-items: start;
}
.fe-megamenu-classic-col { padding: 0 22px; min-width: 0; }
.fe-megamenu-classic-col + .fe-megamenu-classic-col {
  border-left: 1px solid rgba(15, 23, 42, 0.08); }
.fe-megamenu-classic-heading {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: 0.9375rem; letter-spacing: 0.04em; text-transform: uppercase;
  margin: 0 0 14px; color: inherit; opacity: 0.72;
}
.fe-megamenu-classic-links { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 2px; }
.fe-megamenu-classic-link {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 7px 10px; border-radius: 6px;
  color: inherit; text-decoration: none;
  font-size: calc(var(--fe-mm-link-base, 0.9375rem) * var(--fe-mm-link-scale, 1));
  font-weight: 500;
  transition: background 140ms ease, color 140ms ease;
}
.fe-megamenu-classic-link:hover {
  background: rgba(81, 100, 255, 0.08); color: #0b5cff;
  text-decoration: none;
}
/* Per-link size override (Classic theme) — see fe-megamenu-link
   above. Inline --fe-mm-link-scale on the rendered <a> wins over the
   cascaded megamenu-root value. Legacy small/large modifiers retired. */
.fe-megamenu-classic-link-colored { color: var(--link-color-light); }
.fe-megamenu-classic-link-colored:hover { color: var(--link-color-light); }
html[data-theme="dark"] .fe-megamenu-classic-link-colored { color: var(--link-color-dark); }
html[data-theme="dark"] .fe-megamenu-classic-link-colored:hover { color: var(--link-color-dark); }
.fe-megamenu-classic-icon { display: inline-flex; flex: 0 0 auto; opacity: 0.7; --icon-size: 15px; }
.fe-megamenu-classic-icon .icon { width: var(--icon-size); height: var(--icon-size); }
.fe-megamenu-classic-icon .icon-custom { object-fit: contain; }

/* Classic mega-menu search widget — pill input that spans the column.
   Sits on the white card, so a subtle border + light fill reads cleanly. */
.fe-megamenu-classic-search {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 16px; border-radius: 999px;
  background: #f1f5f9; color: #0f172a;
  border: 1px solid #e2e8f0;
  margin: 6px 0;
}
.fe-megamenu-classic-search-icon { display: inline-flex; flex: 0 0 auto; color: #64748b; }
.fe-megamenu-classic-search-icon .icon { width: 18px; height: 18px; }
.fe-megamenu-classic-search-input {
  flex: 1 1 auto; min-width: 0;
  border: 0; outline: 0; background: transparent; color: inherit;
  font: inherit; font-size: 1rem; padding: 0;
}
.fe-megamenu-classic-search-input::placeholder { color: #94a3b8; }
html[data-theme="dark"] .fe-megamenu-classic-search {
  background: #1e293b; color: #e2e8f0; border-color: #334155;
}
html[data-theme="dark"] .fe-megamenu-classic-search-icon { color: #94a3b8; }
html[data-theme="dark"] .fe-megamenu-classic-search-input::placeholder { color: #64748b; }
.fe-megamenu-classic-label { flex: 1 1 auto; min-width: 0;
  white-space: normal; overflow-wrap: anywhere; }
.fe-megamenu-classic-arrow {
  flex: 0 0 auto;
  display: inline-flex; align-items: center;
  margin-left: auto;       /* pin to the right edge */
  /* Match the link label's colour exactly: inherit via currentColor
     and stay fully opaque. The hover slide is the only motion. */
  color: inherit;
  opacity: 1;
  transform: translateX(0);
  transition: transform 140ms ease;
}
.fe-megamenu-classic-arrow .icon { width: 20px; height: 20px; }
.fe-megamenu-classic-link:hover .fe-megamenu-classic-arrow {
  transform: translateX(3px);
}

/* ---------- Classic mega menu blocks (title / button / section) ---------- */
.fe-megamenu-classic-blocks { display: flex; flex-direction: column; gap: 2px; }
/* Classic theme baseline so the percent slider's scale multiplier
   has the right number to multiply against. Same calc() pattern as
   Recovery Blue. */
.fe-megamenu-classic { --fe-mm-heading-base: 0.875rem; --fe-mm-link-base: 0.9375rem; }
.fe-megamenu-classic-block-title {
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: calc(var(--fe-mm-heading-base, 0.875rem) * var(--fe-mm-heading-scale, 1));
  margin: 14px 10px 4px;
  letter-spacing: 0.06em; text-transform: uppercase; opacity: 0.62;
}
.fe-megamenu-classic-blocks > .fe-megamenu-classic-block-title:first-child { margin-top: 0; }
.fe-megamenu-classic-block-section { display: flex; align-items: center; gap: 10px;
  margin: 14px 10px 8px; color: inherit; opacity: 0.6; }
.fe-megamenu-classic-block-section-line { flex: 1 1 auto; height: 1px;
  background: currentColor; opacity: 0.32; }
.fe-megamenu-classic-block-section-label { font-size: 0.6875rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; white-space: nowrap; }
.fe-megamenu-classic-block-btn { display: inline-flex; align-items: center; gap: 8px;
  margin: 6px 10px; padding: 8px 16px; font-weight: 600; font-size: 0.875rem;
  text-decoration: none; color: #fff; background: var(--fe-recovery-blue, #0B5CFF);
  border: 1px solid transparent; align-self: flex-start;
  transition: background 140ms ease, transform 140ms ease; }
.fe-megamenu-classic-block-btn:hover { background: var(--fe-recovery-blue-2, #3d6dff);
  text-decoration: none; transform: translateY(-1px); }
.fe-megamenu-classic-block-btn-pill { border-radius: 999px; }
.fe-megamenu-classic-block-btn-rounded { border-radius: 1rem; }

@media (max-width: 780px) {
  .fe-megamenu-classic-grid { grid-template-columns: 1fr; }
  .fe-megamenu-classic-col + .fe-megamenu-classic-col { border-left: 0;
    border-top: 1px solid rgba(15,23,42,0.08); padding-top: 18px; margin-top: 18px; }
}
html[data-theme="dark"] .fe-megamenu-classic-card {
  background: #131a33; color: #e2e8f0;
  border-color: rgba(255,255,255,0.08);
  box-shadow: 0 20px 44px rgba(0,0,0,0.5), 0 2px 10px rgba(0,0,0,0.3);
}
html[data-theme="dark"] .fe-megamenu-classic-heading { opacity: 0.6; }
html[data-theme="dark"] .fe-megamenu-classic-col + .fe-megamenu-classic-col {
  border-left-color: rgba(255,255,255,0.08); }
html[data-theme="dark"] .fe-megamenu-classic-link:hover {
  background: rgba(81, 100, 255, 0.18); color: #7aa3ff;
}

/* DCCMA mega menu — dark mode. The admin's configured mega-menu colors
   are applied as inline styles on the panel, so we use !important to swap
   in a consistent deep-navy panel when the frontend is in dark mode. */
html[data-theme="dark"] .fe-megamenu-recovery-blue {
  background: #131a33 !important;
  color: #e2e8f0 !important;
  box-shadow: 0 24px 48px rgba(0,0,0,0.55), 0 4px 12px rgba(0,0,0,0.3);
}
html[data-theme="dark"] .fe-megamenu-recovery-blue .fe-megamenu-block-title { color: #f1f5f9; }
html[data-theme="dark"] .fe-megamenu-recovery-blue .fe-megamenu-link:hover {
  background: rgba(255,255,255,0.06);
}
html[data-theme="dark"] .fe-megamenu-recovery-blue .fe-megamenu-block-btn {
  background: rgba(255,255,255,0.08);
  border-color: rgba(255,255,255,0.16);
  color: #e2e8f0;
}
html[data-theme="dark"] .fe-megamenu-recovery-blue .fe-megamenu-block-btn:hover {
  background: rgba(255,255,255,0.14);
}
html[data-theme="dark"] .fe-megamenu-recovery-blue .fe-megamenu-block-section-line {
  background: rgba(255,255,255,0.12);
}

/* Backdrop — blurs everything BELOW the header (main + footer). */
.fe-megamenu-backdrop {
  position: fixed; left: 0; bottom: 0;
  right: var(--fe-mm-gutter, 0);
  top: var(--fe-header-full-h, 108px);
  background: rgba(10, 16, 38, 0.35);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  z-index: 30; opacity: 0; pointer-events: none;
  transition: opacity 180ms ease;
}
.fe-megamenu-backdrop.open { opacity: 1; pointer-events: auto; }

/* Let the active nav item know it's parent to an open panel. */
.fe-recovery-nav-link.mega-active,
.fe-megaparent.mega-active { color: var(--fe-recovery-blue); background: #eef3ff; }

@media (max-width: 960px) {
  .fe-megamenu-inner { grid-template-columns: 1fr; gap: 24px; padding: 0 20px; }
  .fe-megamenu { padding: 28px 0 36px; }
}

/* ========================================================================
   DARK MODE — applied via <html data-theme="dark">.
   Overrides hardcoded backgrounds/borders/text across every major region.
   Kept at the end of the stylesheet so it wins on cascade tie.
   ======================================================================== */
/* =====================================================================
   UNIVERSAL DARK-MODE PALETTE TOKENS
   Set once here and consumed by every dark-mode card / heading /
   surface / button rule throughout the site. New templates and new
   blocks just need to pull from these custom properties (or use the
   generic class hooks below) and they inherit dark mode for free —
   without revisiting this file every time a new component lands.

   `body.fe-frontend-force-dark` mirrors the data-theme override so
   admin-locked dark sections (e.g. force-dark footer on a light page)
   pick up the same palette as the visitor's explicit dark choice.
   ===================================================================== */
html[data-theme="dark"], body.fe-frontend-force-dark {
  /* Page-level surface — sits behind every section without its own bg.
     Reads the design token so admins can rebrand the dark-mode surface
     site-wide from Site → Design → Colors → Surface — Darkmode. */
  --fe-dm-page-bg:           var(--fe-color-surface-dark, #0b1026);
  /* Card / panel surfaces. -surface is the elevated card colour;
     -surface-soft is a translucent wash for chips, soft fills, etc. */
  --fe-dm-surface:           #131a33;
  --fe-dm-surface-soft:      rgba(255, 255, 255, 0.04);
  /* Borders — resting + hover indigo edge highlight that lights up on
     interactive surfaces. */
  --fe-dm-border:            #1f2a44;
  --fe-dm-border-hover:      rgba(122, 163, 255, 0.4);
  /* Type — strong is for headings, base for body, muted for sub-text. */
  --fe-dm-text-strong:       #f1f5f9;
  --fe-dm-text:              #e2e8f0;
  --fe-dm-text-muted:        #94a3b8;
  /* Link hover tint inside dark surfaces. */
  --fe-dm-link-hover:        #93c5fd;
  /* Inline code surface — used by anything wrapping `<code>` chips. */
  --fe-dm-code-bg:           rgba(255, 255, 255, 0.10);
  /* Primary-button recipe — colour-mix of the admin's chosen primary
     against black (40% rest, 60% hover) so the button tracks the
     site's primary token while staying legible on the dark surface.
     Mirrors the hero "Find a Meeting" treatment so every primary
     button site-wide reads as one button family. */
  --fe-dm-btn-primary-bg:    color-mix(in srgb,
                                       var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 40%,
                                       #000);
  --fe-dm-btn-primary-bg-hover: color-mix(in srgb,
                                          var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 60%,
                                          #000);
  --fe-dm-btn-primary-text:       #f8fafc;
  --fe-dm-btn-primary-text-hover: #ffffff;
  --fe-dm-btn-primary-shadow:     0 8px 24px rgba(0, 0, 0, 0.45);
}

html[data-theme="dark"] body.frontend-body { background: var(--fe-dm-page-bg); color: var(--fe-dm-text); }

/* Classic header */
html[data-theme="dark"] .fe-header { background: rgba(15, 23, 42, 0.72);
  border-bottom-color: transparent; }
html[data-theme="dark"] .fe-header.fe-header-scrolled {
  background: rgba(11, 16, 38, 0.92); border-bottom-color: #1f2a44;
  box-shadow: 0 1px 0 rgba(0,0,0,0.4); }
html[data-theme="dark"] .fe-brand { color: #e2e8f0; }
html[data-theme="dark"] .fe-nav a { color: #94a3b8; }
html[data-theme="dark"] .fe-nav a:hover { color: #fff; background: #1e293b; }
html[data-theme="dark"] .fe-nav-cta { background: var(--fe-recovery-blue, #0B5CFF); color: #fff !important; }
html[data-theme="dark"] .fe-nav-cta:hover { background: #3d6dff !important; }
html[data-theme="dark"] .fe-menu-btn span { background: #e2e8f0; }

/* DCCMA header (white → near-black) */
html[data-theme="dark"] .fe-header-recovery { background: #0f172a;
  border-bottom-color: #1f2a44; box-shadow: 0 1px 0 rgba(0,0,0,0.4); }
html[data-theme="dark"] .fe-recovery-main { background: #0f172a; }
/* Utility strip dims to the same muted navy as the meeting-card "Join
   Zoom" button (#1e3a8a — Tailwind blue-900) so the bright #0B5CFF that
   lives on top of the header in light mode doesn't read as a glow bar
   against the deep dark surface. White-on-navy still meets contrast for
   the link + phone copy; the white-alpha pill on the phone number works
   over any background tint. */
html[data-theme="dark"] .fe-recovery-util { background: #1e3a8a; }
html[data-theme="dark"] .fe-recovery-brand { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-nav-link { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-nav-link:hover { color: #fff; background: #1e293b; }
html[data-theme="dark"] .fe-recovery-nav-link.mega-active,
html[data-theme="dark"] .fe-megaparent.mega-active { color: #fff; background: #1e293b; }
html[data-theme="dark"] .fe-recovery-nav-newcomer-lead { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-nav-newcomer-cta { color: #7aa3ff; }
html[data-theme="dark"] .fe-recovery-nav-newcomer:hover,
html[data-theme="dark"] .fe-recovery-nav-newcomer.mega-active { background: #1e293b; }
html[data-theme="dark"] .fe-recovery-nav-portal { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-menu-btn span { background: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-nav { background: #0f172a; border-bottom-color: #1f2a44; }

/* Hero */
html[data-theme="dark"] .fe-hero { background: linear-gradient(180deg, #0b1026 0%, #0f172a 100%); }
/* Dark-mode hero heading + sub colours read from per-block dark-mode
   CSS variables when set (admin picks them from the hero edit modal's
   Dark-mode gradient + Text colour fields); fall back to the historic
   hardcoded gradient (`#fff → #94a3b8`) and muted slate subheading
   so the homepage hero and any unedited per-page hero render
   identically to before the controls existed.

   `!important` is required to beat the dynamic-text override
   (`.fe-hero-text-dark .fe-hero-heading { … !important }` further up
   the file): that rule hardcodes light-mode colours regardless of
   theme, so without the `!important` here the heading stays dark on
   a dark page when dynamic text is enabled. Dark-mode admin choice
   always wins. */
/* Use `background-image:` (NOT the `background:` shorthand) so we
   don't reset `background-clip` to its initial value. The base rule
   above sets `background-clip: text` + `text-fill-color: transparent`
   to draw the heading through the gradient — the shorthand reset
   would force the gradient to paint a solid rectangle behind the
   text instead of clipping to the glyph shapes. The dynamic-text
   override (`.fe-hero-text-light/dark .fe-hero-heading`) uses the
   same trick. */
html[data-theme="dark"] .fe-hero-heading,
body.fe-frontend-force-dark .fe-hero-heading {
  background-image: linear-gradient(180deg,
      var(--fe-hero-h-grad-s-dark, #fff) 0%,
      var(--fe-hero-h-grad-e-dark, #94a3b8) 100%) !important;
}
html[data-theme="dark"] .fe-hero-sub,
body.fe-frontend-force-dark .fe-hero-sub {
  color: var(--fe-hero-sub-color-dark, #94a3b8) !important;
}
html[data-theme="dark"] .fe-hero-blob { opacity: 0.25; }
html[data-theme="dark"] .fe-hero-eyebrow { background: rgba(81,100,255,0.18); color: #7aa3ff; }
html[data-theme="dark"] .fe-btn-primary { background: #052566; color: #e2e8f0;
  box-shadow: var(--fe-btn-shadow, 0 8px 24px rgba(0,0,0,0.4)); }
/* Hover: lighten the deep navy bg slightly so the press affordance
   reads on dark themes; keep the same #e2e8f0 label so contrast
   doesn't flip mid-hover. The explicit `color` here wins over the
   lower-specificity `.frontend-body .fe-btn-primary:hover` rule
   that would otherwise resolve to `#fff` via the design tokens. */
html[data-theme="dark"] .fe-btn-primary:hover {
  background: color-mix(in srgb, #052566 80%, #fff 20%);
  color: #e2e8f0;
}
html[data-theme="dark"] .fe-btn-ghost { background: transparent; color: #e2e8f0; border-color: #334155; }
/* Same fix on the ghost variant: the inherited hover color
   resolves to ``#0f172a`` (dark slate) via the secondary-text token,
   which would disappear into the dark hover background. */
html[data-theme="dark"] .fe-btn-ghost:hover { background: #1e293b; color: #e2e8f0; border-color: #94a3b8; }
/* Meeting-card "Join Zoom Meeting" primary button — dark mode uses a
   muted navy-blue instead of the global light-on-dark primary. The base
   dark `.fe-btn-primary` is bright (#e2e8f0 / off-white) for max
   readability everywhere else, but inside the meeting-card grid that
   reads as too punchy against the deep card backgrounds. Scoped to the
   card-actions wrapper so other primary buttons site-wide are
   unaffected. Hover lifts to a slightly more saturated blue. */
html[data-theme="dark"] .fe-meeting-card-actions .fe-btn-primary,
html[data-theme="dark"] .fe-page .fe-meeting-card-actions .fe-btn-primary,
html[data-theme="dark"] .frontend-body .fe-meeting-card-actions .fe-btn-primary {
  background: #1e3a8a; color: #e0e7ff; border-color: transparent;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}
html[data-theme="dark"] .fe-meeting-card-actions .fe-btn-primary:hover,
html[data-theme="dark"] .fe-page .fe-meeting-card-actions .fe-btn-primary:hover,
html[data-theme="dark"] .frontend-body .fe-meeting-card-actions .fe-btn-primary:hover {
  background: #2747a3; color: #ffffff;
}
/* Hero CTA buttons — admin's per-button colour pickers feed `--fe-btn-bg`
   / `--fe-btn-text` (and per-icon `--fe-btn-icon-color`) via inline CSS
   custom properties. The actual property declarations live here in the
   stylesheet so theme overrides win by specificity, not by !important.
   Light-mode default falls back to the theme's primary token if the
   admin didn't customise the button. */
.fe-hero-cta .fe-btn-primary,
.fe-page .fe-hero-cta .fe-btn-primary,
.frontend-body .fe-hero-cta .fe-btn-primary {
  background: var(--fe-btn-bg, var(--fe-color-btn-primary-bg, var(--fe-ink)));
  color: var(--fe-btn-text, var(--fe-color-btn-primary-text, #fff));
  border-color: var(--fe-btn-bg, transparent);
}
.fe-btn-icon { color: var(--fe-btn-icon-color, inherit); }
/* Dark mode derives a darkened version of whatever colour the admin
   picked (a yellow button becomes dark mustard, a blue button becomes
   dark navy, etc.) instead of forcing one fixed dim. `color-mix` blends
   40 % of the admin's `--fe-btn-bg` with 60 % black for the base, and
   60 % / 40 % on hover for a hint more saturation. Text always flips to
   off-white so any darkened bg has readable copy regardless of the
   admin's `--fe-btn-text` choice (which often defaults to black). The
   default-fallback inside the var() resolves to the theme primary token
   when the admin hasn't customised the button. */
html[data-theme="dark"] .fe-hero-cta .fe-btn-primary,
html[data-theme="dark"] .fe-page .fe-hero-cta .fe-btn-primary,
html[data-theme="dark"] .frontend-body .fe-hero-cta .fe-btn-primary {
  background: color-mix(in srgb,
                        var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 40%,
                        #000);
  color: #f8fafc;
  border-color: color-mix(in srgb,
                          var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 40%,
                          #000);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-hero-cta .fe-btn-primary:hover,
html[data-theme="dark"] .fe-page .fe-hero-cta .fe-btn-primary:hover,
html[data-theme="dark"] .frontend-body .fe-hero-cta .fe-btn-primary:hover {
  background: color-mix(in srgb,
                        var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 60%,
                        #000);
  color: #ffffff;
  border-color: color-mix(in srgb,
                          var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff)) 60%,
                          #000);
}
/* Hero ghost buttons in dark mode — the global dark ghost is transparent
   with a slate border + light text. Over the dark hero blobs / sinewave
   that's fine, but tinting the hover into the same muted-navy family
   keeps the secondary affordance visually paired with the primary. */
html[data-theme="dark"] .fe-hero-cta .fe-btn-ghost:hover,
html[data-theme="dark"] .fe-page .fe-hero-cta .fe-btn-ghost:hover,
html[data-theme="dark"] .frontend-body .fe-hero-cta .fe-btn-ghost:hover {
  background: rgba(30, 58, 138, 0.45);
  border-color: #2747a3;
  color: #e0e7ff;
}

/* Quick links / sections */
html[data-theme="dark"] .fe-quick { background: #0b1026; }
html[data-theme="dark"] .fe-quick-card {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, transparent);
  color: #e2e8f0;
}
html[data-theme="dark"] .fe-quick-card:hover { background: #17203f; border-color: #1f2a44;
  box-shadow: 0 8px 28px rgba(0,0,0,0.4); }
html[data-theme="dark"] .fe-quick-card p { color: #94a3b8; }
html[data-theme="dark"] .fe-section + .fe-section { border-top-color: #1f2a44; }
html[data-theme="dark"] .fe-section-head p { color: #94a3b8; }

/* Meetings */
html[data-theme="dark"] .fe-meeting-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, #1f2a44);
}
html[data-theme="dark"] .fe-meeting-card:hover { box-shadow: 0 10px 28px rgba(0,0,0,0.45); }
/* Meetings-list cards (`.fe-mlist-card`) — distinct from the
   homepage `.fe-meeting-card`. Without a dark-mode override these
   would inherit the default white bg and read as a stark light
   tile against the dark page. Track the Primary-card dark-mode
   tokens (`--fe-color-card-primary-bg-dark` /
   `--fe-color-card-primary-border-dark`) so every primary-card
   shape across the public site shares one dark surface that the
   admin can re-tint from Site → Design → Card styles → Primary
   card. Solid (not alpha) so a page-level dynbg can't paint
   through and make the cards read as transparent. */
html[data-theme="dark"] .fe-mlist-card,
body.fe-frontend-force-dark .fe-mlist-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, #1f2a44);
}
html[data-theme="dark"] .fe-mlist-card:hover,
body.fe-frontend-force-dark .fe-mlist-card:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}
/* Description inherits the `muted` utility class from the template,
   which renders dim grey on dark — making the desc copy fade out
   while the title stays bright. Force the desc back to the card's
   text colour so it reads at the same weight as the title. */
html[data-theme="dark"] .fe-mlist-card .fe-mlist-card-desc,
html[data-theme="dark"] .fe-mlist-card .fe-mlist-card-desc.muted,
body.fe-frontend-force-dark .fe-mlist-card .fe-mlist-card-desc,
body.fe-frontend-force-dark .fe-mlist-card .fe-mlist-card-desc.muted {
  color: inherit;
}

/* Unified dark-mode hover border for every card-shaped block on the
   public homepage (and detail pages reusing these classes). The
   indigo tint matches the FAQ accordion + meetings-list cards so the
   dark site has one shared "edge lights up on hover" language across
   features, quick links, events, meetings, testimonials, etc. Each
   card's own light-mode hover (transform / shadow / background) is
   left intact — this only retints the border. */
html[data-theme="dark"] .fe-meeting-card:hover,
html[data-theme="dark"] .fe-feature-card:hover,
html[data-theme="dark"] .fe-quick-card:hover,
html[data-theme="dark"] .fe-event-row:hover,
html[data-theme="dark"] .fe-recovery-threeup-card:hover,
html[data-theme="dark"] .fe-recovery-meeting-card:hover,
html[data-theme="dark"] .fe-contact-card:hover,
html[data-theme="dark"] .fe-inclusion-card:hover,
html[data-theme="dark"] .fe-testimonial:hover,
body.fe-frontend-force-dark .fe-meeting-card:hover,
body.fe-frontend-force-dark .fe-feature-card:hover,
body.fe-frontend-force-dark .fe-quick-card:hover,
body.fe-frontend-force-dark .fe-event-row:hover,
body.fe-frontend-force-dark .fe-recovery-threeup-card:hover,
body.fe-frontend-force-dark .fe-recovery-meeting-card:hover,
body.fe-frontend-force-dark .fe-contact-card:hover,
body.fe-frontend-force-dark .fe-inclusion-card:hover,
body.fe-frontend-force-dark .fe-testimonial:hover {
  border-color: rgba(122, 163, 255, 0.4);
}
html[data-theme="dark"] .fe-meeting-sched { color: #94a3b8; }
html[data-theme="dark"] .fe-meeting-day { color: #e2e8f0; }

/* About */
html[data-theme="dark"] .fe-about { background: #0b1026; }
html[data-theme="dark"] .fe-about-body { color: #e2e8f0; }
html[data-theme="dark"] .fe-about-pillar { background: #131a33; box-shadow: 0 4px 16px rgba(0,0,0,0.3); }
html[data-theme="dark"] .fe-about-pillar p { color: #94a3b8; }

/* Contact (already dark) — minor tweaks so it blends */
html[data-theme="dark"] .fe-contact { background: #05081a; }
html[data-theme="dark"] .fe-contact-card { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.08); }

/* Classic footer — text dimming applies on dark mode (page is dark).
   Force-dark footer on a light page keeps text BRIGHT so it pops
   against the dark surface; same split as the consolidated block
   higher in this file. */
html[data-theme="dark"] .fe-footer {
  background: #0b1026; border-top-color: #1f2a44;
}
html[data-theme="dark"] .fe-footer-name { color: #e2e8f0; }
html[data-theme="dark"] .fe-footer-tagline { color: #94a3b8; }
html[data-theme="dark"] .fe-footer-links a { color: #94a3b8; }
html[data-theme="dark"] .fe-footer-links a:hover { color: #fff; }
html[data-theme="dark"] .fe-footer-copy { color: #64748b; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-links a { color: #f1f5f9; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-links a:hover { color: #ffffff; }
html:not([data-theme="dark"]) body.fe-footer-force-dark .fe-footer-copy { color: #cbd5e1; }

/* DCCMA homepage */
html[data-theme="dark"] .fe-recovery-hero { background: #0b1026; border-bottom-color: #1f2a44; }
html[data-theme="dark"] .fe-recovery-hero-h1 { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-hero-lede { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-threeup { background: #0f172a; border-bottom-color: #1f2a44; }
html[data-theme="dark"] .fe-recovery-threeup-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, #1f2a44);
}
html[data-theme="dark"] .fe-recovery-threeup-card:hover { box-shadow: 0 10px 32px rgba(0,0,0,0.5); }
html[data-theme="dark"] .fe-recovery-threeup-card h2 { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-threeup-card p { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-meetings { background: #0b1026; }
html[data-theme="dark"] .fe-recovery-section-head h2 { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-section-head p.muted { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-meeting-card { background: #131a33; border-color: #1f2a44; }
html[data-theme="dark"] .fe-recovery-meeting-card h3 { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-meeting-sched { color: #94a3b8; }
html[data-theme="dark"] .fe-recovery-meeting-sched .day { color: #e2e8f0; }
html[data-theme="dark"] .fe-recovery-fellowship { background: #13286b; }
html[data-theme="dark"] .fe-recovery-helpline { background: #05081a; }

/* DCCMA footer (already dark) — lift contrast slightly so the darker page bg doesn't bleed in. */
html[data-theme="dark"] .fe-footer-recovery { background: #060a1a; }
html[data-theme="dark"] .fe-footer-recovery-locations { background: #080d22; }

/* Alert bars: preserve admin-chosen colors but tone borders down. */
html[data-theme="dark"] .fe-alertbar { border-bottom-color: rgba(255,255,255,0.08); }

/* Two-panel split row — placed by the layout builder. Modifiers set
   per-split by the homepage admin's split card:
     fe-split--w-{boxed|full}      width
     fe-split--p-{none|tight|default|loose}    inner padding (vertical)
     fe-split--g-{none|tight|default|loose}    inner gap between columns
     fe-split--gt-{...}            outer gap on top (margin-top)
     fe-split--gb-{...}            outer gap on bottom (margin-bottom)
   Defaults: gt = gb = g = none — sections butt flush against neighbors
   unless the admin opts into a gap. The previous single `margin` field
   has been split into top + bottom; legacy values still render through
   the template's fallback. */
/* The base .fe-section class adds 80px top + 80px bottom padding to
   every section. On a split, that padding sits between the section's
   edges and the column content — so even with gt/gb=none the user
   sees a fat vertical gap. Zero it out and let the per-column padding
   (.fe-split--p-*) own all vertical spacing inside a split. */
.fe-split { padding: 0; }
.fe-split-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 0; align-items: stretch;
}
.fe-split-col { min-width: 0; }
.fe-split-col > .fe-section { padding: 56px 0; }
@media (max-width: 900px) {
  .fe-split-grid {
    grid-template-columns: 1fr;
    /* Override the admin's per-block padding-% inline style on mobile,
       where the grid collapses to one column and side gutters would
       eat too much horizontal space. */
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
}

/* Top + bottom gap (margin around the section). Default: 0 = flush. */
.fe-split--gt-none    { margin-top: 0; }
.fe-split--gt-tight   { margin-top: 1rem; }
.fe-split--gt-default { margin-top: 2.5rem; }
.fe-split--gt-loose   { margin-top: 5rem; }
.fe-split--gb-none    { margin-bottom: 0; }
.fe-split--gb-tight   { margin-bottom: 1rem; }
.fe-split--gb-default { margin-bottom: 2.5rem; }
.fe-split--gb-loose   { margin-bottom: 5rem; }

/* Inner gap between the left and right columns. Default: 0 (columns abut). */
.fe-split--g-none    > .fe-split-grid { gap: 0; }
.fe-split--g-tight   > .fe-split-grid { gap: 1rem; }
.fe-split--g-default > .fe-split-grid { gap: 2rem; }
.fe-split--g-loose   > .fe-split-grid { gap: 4rem; }

/* Inner padding (vertical breathing room inside each column). */
.fe-split--p-none    .fe-split-col > .fe-section { padding-top: 0; padding-bottom: 0; }
.fe-split--p-tight   .fe-split-col > .fe-section { padding-top: 24px; padding-bottom: 24px; }
.fe-split--p-default .fe-split-col > .fe-section { padding-top: 56px; padding-bottom: 56px; }
.fe-split--p-loose   .fe-split-col > .fe-section { padding-top: 96px; padding-bottom: 96px; }

/* Full-width variant: the .fe-container wrapper is dropped on the markup
   side, so the .fe-split-grid directly spans the viewport edge-to-edge. */
.fe-split--w-full > .fe-split-grid { width: 100%; max-width: none; padding-left: 0; padding-right: 0; }

/* ===== Public 404 ===== */
.fe-404 { padding: 4rem 0; }
.fe-404-inner { text-align: center; max-width: 720px; margin: 0 auto; }
.fe-404-art, .fe-404-image {
  display: block; width: 100%; max-width: 480px; margin: 0 auto 1.25rem;
}
.fe-404-image { max-height: 280px; object-fit: contain; }
.fe-404-heading {
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 2.25rem; font-weight: 800;
  margin: 0.25rem 0 0.75rem; color: var(--fe-color-text, inherit);
  letter-spacing: -0.015em;
}
.fe-404-sub {
  max-width: 560px; margin: 0 auto 1.5rem;
  color: var(--fe-color-text-soft, inherit); line-height: 1.6;
}
.fe-404-actions {
  display: inline-flex; flex-wrap: wrap; gap: 10px; justify-content: center;
}
@media (max-width: 720px) {
  .fe-404 { padding: 2.5rem 0; }
  .fe-404-heading { font-size: 1.6rem; }
}

/* Dark-mode background: the admin's chosen split colour is for light
   mode only. In dark mode, force the section + columns transparent so
   the body's dark surface (#0b1026) shows through and the split
   blends with the rest of the page — instead of carrying a derived
   variant of the admin's light pick that visually clashes with the
   rest of the dark homepage. `!important` is required to beat the
   inline `background: …` style emitted from the partial. */
html[data-theme="dark"] .fe-split,
body.fe-frontend-force-dark .fe-split,
html[data-theme="dark"] .fe-split .fe-split-col,
body.fe-frontend-force-dark .fe-split .fe-split-col {
  background: transparent !important;
}

/* Day heading separator inside the meetings list (used when the
   group_by_day setting is on). */
.fe-meetings-day-heading {
  font-family: var(--fe-font-heading);
  font-weight: 700; font-size: 1.5rem;
  margin: 32px 0 16px; color: var(--fe-ink);
  letter-spacing: -0.02em;
}
.fe-meetings-day-heading:first-of-type { margin-top: 0; }
html[data-theme="dark"] .fe-meetings-day-heading { color: #e2e8f0; }
.fe-meetings-empty {
  padding: 48px 0; text-align: center; font-size: 1.0625rem;
}

/* Section foot — "See all meetings"/"See all events" CTA below the grid.
   Features block uses the same pattern when its admin-set bottom button
   is filled in (cta_label + cta_url). */
.fe-meetings-foot,
.fe-events-foot,
.fe-features-foot {
  margin-top: 28px; text-align: center;
}
/* Features block gets a deeper desktop gutter — the cards above are
   already chunky, so 28px reads as cramped between the grid and the
   single section CTA. Mobile keeps the tighter shared default. */
@media (min-width: 768px) {
  .fe-features-foot { margin-top: 4rem; }
}

/* Animation classes — each card gets its own --i index from the template
   so cards stagger in. Using opacity+transform keeps the layout stable
   from frame zero (no shift after animation). */
.fe-meetings-anim-fade .fe-meeting-card {
  opacity: 0;
  animation: fe-mt-fade 380ms ease-out both;
  animation-delay: calc(var(--i, 0) * var(--fe-meetings-stagger, 60ms));
}
.fe-meetings-anim-slide .fe-meeting-card {
  opacity: 0; transform: translateY(14px);
  animation: fe-mt-slide 460ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
  animation-delay: calc(var(--i, 0) * var(--fe-meetings-stagger, 60ms));
}
.fe-meetings-anim-none .fe-meeting-card { opacity: 1; }
@keyframes fe-mt-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes fe-mt-slide {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .fe-meetings-anim-fade .fe-meeting-card,
  .fe-meetings-anim-slide .fe-meeting-card { animation: none; opacity: 1; transform: none; }
}


/* ============================================================
   UPCOMING EVENTS BLOCK
   Stacked rows (one event per row, top → bottom). Each row has a
   left-anchored date stamp, optional thumbnail, title + meta + CTA
   flowing horizontally. Reads more like a schedule than a card grid.
   ============================================================ */
.fe-events-empty { text-align: center; padding: 32px 0; }

.fe-events-list {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 18px;
}
.fe-event-row {
  /* Surface + border read from the Primary card design tokens (same
     as `.fe-meeting-card` and the standalone event/announcement
     detail cards) so the homepage Upcoming Events tiles share the
     elevated meeting-card visual family. The shared primary
     aggregator near the bottom of this file wires shadow at rest +
     hover shadow + hover transform + hover border colour through
     the matching Primary tokens, so no per-row `:hover` rule is
     needed here. */
  position: relative;
  display: flex; align-items: center; gap: 24px;
  padding: 22px 22px 20px;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 16px;
  cursor: pointer;
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
/* Whole-card click target — the title's <a> is "stretched" via
   ::after to cover the entire row. The title stays the single
   semantic link per card (one focus stop, one screen-reader
   announcement) and the existing thumbnail anchor is still in the
   DOM but its click is consumed by the overlay (same target URL).
   z-index keeps the overlay below interactive children that opt in
   with their own ``z-index: 2`` if any are added in the future. */
.fe-event-title a::after {
  content: ""; position: absolute; inset: 0;
  border-radius: inherit;
  z-index: 1;
}
/* Title link inherits the row colour and stays put on hover — the
   row-lift + box-shadow are the sole hover affordances. */
html[data-theme="dark"] .fe-event-row { background: #131a33; border-color: #1f2a44; }
html[data-theme="dark"] .fe-event-row:hover { box-shadow: 0 10px 28px rgba(0,0,0,0.45); }
/* Dark-mode title colour — the base rule pins ``color: var(--fe-color-text)``
   which the design tokens fix at a light-mode dark value, so without this
   override the title would render dark-on-dark. The summary ``muted`` class
   keeps its theme-aware colour via the existing dark-mode rule. */
html[data-theme="dark"] .fe-event-title,
html[data-theme="dark"] .fe-event-title a { color: #f1f5f9; }
/* Date stamp + thumbnail tile — both default to `--fe-color-surface-alt`
   (a light gray) which renders as near-white on the dark page. Override
   to a deep navy so the calendar element + thumb fit the night-sky
   palette. Day/year text colour follows the row's content tokens; only
   the *month* tag explicitly re-asserts a brand-tinted dark colour so
   it doesn't bleed into the dark surface. */
html[data-theme="dark"] .fe-event-date,
html[data-theme="dark"] .fe-event-thumb {
  background: #0f1a35; border-color: #243056;
}
html[data-theme="dark"] .fe-event-date-month { color: #7aa3ff; }
html[data-theme="dark"] .fe-event-date-day { color: #f1f5f9; }
html[data-theme="dark"] .fe-event-date-year { color: #94a3b8; }

/* Date stamp on the far left — month/day/year stacked */
.fe-event-date {
  flex: 0 0 auto;
  display: flex; flex-direction: column; align-items: center;
  padding: 8px 14px; border-radius: 12px;
  background: var(--fe-color-surface-alt, #f8fafc);
  border: 1px solid var(--fe-color-border, #e2e8f0);
  min-width: 76px; text-align: center;
}
.fe-event-date-month {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--fe-color-brand, #0b5cff);
  line-height: 1;
}
.fe-event-date-day {
  font-size: 1.85rem; font-weight: 800; line-height: 1.05;
  font-family: var(--fe-font-heading, sans-serif);
  color: var(--fe-color-text, inherit);
  margin-top: 2px;
}
.fe-event-date-year {
  font-size: 0.7rem; color: var(--fe-color-text-soft, var(--fe-color-text, inherit));
  margin-top: 2px;
}

/* Optional thumbnail — small square, sits between date and content */
.fe-event-thumb {
  flex: 0 0 auto;
  display: block; width: 96px; height: 96px;
  border-radius: 12px; overflow: hidden;
  background: var(--fe-color-surface-alt, #f8fafc);
  border: 1px solid var(--fe-color-border, #e2e8f0);
}
.fe-event-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 280ms ease;
}
.fe-event-row:hover .fe-event-thumb img { transform: scale(1.05); }

/* Body — title, attrs row, summary */
.fe-event-body { flex: 1 1 auto; min-width: 0; }
.fe-event-title {
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.2rem; font-weight: 700; line-height: 1.3;
  margin: 0 0 6px; color: var(--fe-color-text, inherit);
  letter-spacing: -0.005em;
  overflow-wrap: anywhere;
}
/* Both states pinned: the global ``.fe-page a:hover`` rule sets
   ``color: var(--fe-color-link-hover)``, which would otherwise tint
   the title on hover. Including the ``:hover`` selector here keeps the
   colour locked to the parent. Same source-order tie-break as
   ``.fe-page a:hover`` (equal specificity, this rule comes later). */
.fe-event-title a,
.fe-event-title a:hover { color: inherit; text-decoration: none; }
/* Suppress the default ``.fe-page a:hover { text-decoration: underline }``
   inside event cards — the whole card is the click target now and
   the title's brand-tint hover already telegraphs interactivity. */
.fe-page .fe-event-row a,
.frontend-body .fe-event-row a,
.fe-page .fe-event-row a:hover,
.frontend-body .fe-event-row a:hover { text-decoration: none; }

.fe-event-attrs {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  font-size: 0.875rem;
  color: var(--fe-color-text-soft, var(--fe-color-text, inherit));
}
.fe-event-attr {
  display: inline-flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.fe-event-attr-icon { display: inline-flex; align-items: center; opacity: 0.85; }
.fe-event-attr-icon .icon { width: 14px; height: 14px; }
.fe-event-attr-online { color: var(--fe-color-brand, #0b5cff); font-weight: 600; }

.fe-event-summary {
  margin: 6px 0 0; line-height: 1.5; font-size: 0.95rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Tablet / narrow desktop — keep the row horizontal but tighten gaps
   and shrink the thumbnail. The View Event pill that used to drop to
   its own row at this breakpoint is gone; the whole card is now the
   click target. */
@media (max-width: 1023px) {
  .fe-event-row {
    flex-wrap: wrap; gap: 14px 18px; padding: 20px;
  }
  .fe-event-thumb { width: 80px; height: 80px; }
  .fe-event-body { flex: 1 1 200px; min-width: 0; }
}

/* Mobile — stack everything vertically. Image on top (full-width
   banner), then a compact horizontal date pill, then title + meta,
   then the CTA spans the card width. */
@media (max-width: 640px) {
  .fe-event-row {
    flex-direction: column; align-items: stretch;
    flex-wrap: nowrap; gap: 14px; padding: 16px;
  }
  .fe-event-thumb {
    order: -1;
    width: 100%; height: 180px;
    border-radius: 12px;
  }
  .fe-event-date {
    flex-direction: row; align-items: baseline; gap: 8px;
    min-width: 0; padding: 6px 12px;
    align-self: flex-start;
  }
  .fe-event-date-month { font-size: 0.7rem; }
  .fe-event-date-day { font-size: 1.1rem; margin-top: 0; }
  .fe-event-date-year { font-size: 0.7rem; margin-top: 0; }
  .fe-event-title { font-size: 1.1rem; }
  .fe-event-attrs { gap: 4px 14px; font-size: 0.8125rem; }
  .fe-event-summary { font-size: 0.9rem; }
}

/* Stagger animation — fades or slides each row in sequence. */
.fe-events-anim-fade .fe-event-row {
  opacity: 0;
  animation: fe-ev-fade 380ms ease-out both;
  animation-delay: calc(var(--fe-event-i, 0) * var(--fe-events-stagger, 60ms));
}
/* Slide entrance uses ``top`` (rows are position: relative) instead
   of ``transform``. animation-fill-mode: both keeps the animation's
   end state applied, which would otherwise pin ``transform:
   translateY(0)`` and block the row's :hover transform — animations
   override the normal cascade for whatever properties they touch.
   By animating ``top`` we keep the hover translateY available for
   :hover { transform: translateY(-2px) } to actually apply. */
.fe-events-anim-slide .fe-event-row {
  opacity: 0; top: 14px;
  animation: fe-ev-slide 460ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
  animation-delay: calc(var(--fe-event-i, 0) * var(--fe-events-stagger, 60ms));
}
.fe-events-anim-none .fe-event-row { opacity: 1; }
@keyframes fe-ev-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fe-ev-slide {
  from { opacity: 0; top: 14px; }
  to   { opacity: 1; top: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .fe-events-anim-fade .fe-event-row,
  .fe-events-anim-slide .fe-event-row { animation: none; opacity: 1; top: 0; }
}

/* ============================================================
   PUBLIC MEETING DETAIL PAGE
   ============================================================ */
.fe-meeting-detail {
  padding: 56px 0 80px;
  background: var(--tpl-bg, var(--fe-color-surface, #fff));
}
.fe-meeting-detail-inner { max-width: 880px; margin: 0 auto; }
.fe-meeting-detail-back {
  display: inline-block; margin-bottom: 24px;
  color: var(--fe-ink-muted); font-size: 0.9rem; text-decoration: none;
}
.fe-meeting-detail-back:hover { color: var(--fe-accent); text-decoration: none; }
/* Header is a flex row: logo (when present) on the left, then a content
   column with the type pill, h1, and description. When no logo is uploaded,
   the logo column is omitted and the content column flows flush left
   without any reserved gutter. */
.fe-meeting-detail-head {
  display: flex; align-items: flex-start; gap: 28px;
  margin-bottom: 32px;
}
.fe-meeting-detail-head-logo { flex: 0 0 auto; }
.fe-meeting-detail-head-logo .fe-meeting-logo {
  display: block; width: 180px; height: auto;
  border-radius: var(--fe-card-radius, 12px);
}
.fe-meeting-detail-head-content { flex: 1 1 auto; min-width: 0; }
.fe-meeting-detail-head .fe-meeting-type { margin-bottom: 16px; }
.fe-meeting-detail-head h1 {
  font-family: var(--tpl-heading-font, var(--fe-font-heading));
  font-weight: 800;
  font-size: var(--tpl-heading-size, clamp(2rem, 5vw, 3rem));
  line-height: 1.1; margin: 0 0 16px;
  letter-spacing: -0.02em;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-meeting-detail-head-content .fe-meeting-detail-desc { margin-bottom: 0; }
@media (max-width: 600px) {
  .fe-meeting-detail-head { flex-direction: column; gap: 18px; }
  .fe-meeting-detail-head-logo .fe-meeting-logo { width: 140px; }
}
.fe-meeting-detail-alert {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 16px 20px; margin: 0 0 28px; border-radius: 10px;
  background: rgba(245, 158, 11, 0.10); color: #78350f;
  border: 1px solid rgba(245, 158, 11, 0.35);
  font-size: 0.95rem; line-height: 1.5;
}
.fe-meeting-detail-alert .icon { width: 20px; height: 20px; flex: 0 0 auto; margin-top: 2px; }
.fe-meeting-detail-desc {
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, 1.0625rem);
  line-height: 1.65; color: var(--fe-color-text, var(--fe-ink));
  margin-bottom: 32px;
}
.fe-meeting-detail-desc p { margin: 0 0 16px; }

/* Description text on the meeting-detail page caps at three-quarters
   of the container width on wide desktop viewports so the prose
   reads as a column rather than stretching the full page width
   (uncomfortable line length at typical desktop sizes). Below
   1024 px (tablet and smaller — including landscape phones /
   split-screen), the cap drops so the text uses the full available
   width. Applied across all four meeting-detail templates (Classic,
   Minimal, Card Stack, Magazine) so the layout reads consistently
   regardless of which template the admin selected. */
.fe-meeting-detail-desc,
.fe-meeting-min-desc,
.fe-meeting-stack-prose,
.fe-meeting-mag-prose {
  max-width: 75%;
}
@media (max-width: 1024px) {
  .fe-meeting-detail-desc,
  .fe-meeting-min-desc,
  .fe-meeting-stack-prose,
  .fe-meeting-mag-prose {
    max-width: 100%;
  }
}
.fe-meeting-detail-grid {
  display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.fe-meeting-detail-card {
  /* Surface + border match `.fe-meeting-extended-card`; hover lift
     and timing match the homepage `.fe-meeting-card` so every meeting-
     shaped card across the public site shares one hover language.
     Dark-mode override further down keeps the deep-navy surface so
     this only flips the light-mode appearance. */
  padding: 24px; border-radius: 14px;
  background: var(--fe-color-card-primary-bg, var(--fe-color-surface, #ffffff));
  border: var(--fe-card-primary-border-width, 1px) solid var(--fe-color-card-primary-border, var(--fe-accent));
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
/* Hover-lift mirrors the homepage `.fe-meeting-card:hover`: 2 px rise
   with a softer indigo drop-shadow at the same `200ms ease` tempo.
   Applies to every detail-card in the Classic grid (Schedule /
   Location / Zoom) AND the Files & Readings panel via the chained
   selector. */
.fe-meeting-detail-card:hover,
.fe-meeting-detail-grid > .fe-meeting-resources:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.10);
}
.fe-meeting-detail-card h3 {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--fe-ink-muted);
  margin: 0 0 16px;
}
.fe-meeting-detail-card h3 .icon { width: 18px; height: 18px; color: var(--fe-accent); }
.fe-meeting-detail-sched { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 14px; }
.fe-meeting-detail-sched li { display: flex; flex-direction: column; gap: 2px; }
.fe-meeting-detail-day { font-weight: 700; color: var(--fe-ink); }
.fe-meeting-detail-time { color: var(--fe-ink); font-variant-numeric: tabular-nums; }
.fe-meeting-detail-location { font-size: 1rem; line-height: 1.55; margin: 0 0 16px; }

/* Add-to-Calendar button — sits below the schedule list on the
   meeting detail card. Inherits the .fe-btn-ghost neutral styling
   and adds top spacing so it reads as a follow-on action rather
   than visually merging with the last schedule row. */
.fe-meeting-detail-ics {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 14px;
}
.fe-meeting-detail-ics .icon { width: 16px; height: 16px; }
.fe-meeting-detail-ics-link {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 8px;
}
.fe-meeting-detail-ics-link .icon { width: 14px; height: 14px; }

/* Add-to-Calendar pill on the meeting card — adopts the same
   ghost-button chrome as Get Directions. The icon stays sized to
   align with the existing pill icons (Join Zoom, Get Directions). */
.fe-mlist-card-ics .icon { width: 14px; height: 14px; }

/* Add-to-Calendar inline link inside the timeline meta-item. Sits on
   a new line under the start/end time so it reads as an action on
   the When row rather than a literal time fragment. */
.fe-event-time-link-ics {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 4px;
}
.fe-event-time-link-ics .icon { width: 14px; height: 14px; }
.fe-event-min-ics { margin-top: -4px; }

/* Zoom credentials list — definition list with copy buttons. */
.fe-meeting-detail-zoom .fe-btn { margin-bottom: 18px; }
.fe-meeting-detail-creds { margin: 0; display: grid;
  grid-template-columns: max-content 1fr; gap: 8px 16px; align-items: center; }
.fe-meeting-detail-creds dt {
  font-size: 0.8125rem; font-weight: 600; color: var(--fe-ink-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.fe-meeting-detail-creds dd { margin: 0; display: flex; align-items: center; gap: 8px; }
.fe-meeting-detail-creds code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.95rem; padding: 4px 8px;
  background: #fff; border: 1px solid var(--fe-border); border-radius: 6px;
  color: var(--fe-ink);
}
.fe-copy-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  background: transparent; border: 1px solid var(--fe-border);
  border-radius: 6px; color: var(--fe-ink-muted); cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.fe-copy-btn:hover { color: var(--fe-accent); border-color: var(--fe-accent); }
.fe-copy-btn .icon { width: 14px; height: 14px; }
.fe-copy-btn.is-copied { background: #16a34a; color: #fff; border-color: #16a34a; }

html[data-theme="dark"] .fe-meeting-detail { background: #0b1026; }
html[data-theme="dark"] .fe-meeting-detail-head h1 { color: #f1f5f9; }
html[data-theme="dark"] .fe-meeting-detail-desc { color: #e2e8f0; }
html[data-theme="dark"] .fe-meeting-detail-card,
body.fe-frontend-force-dark .fe-meeting-detail-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, transparent);
}
/* Dark-mode hover border for the Schedule / Location / Zoom cards
   AND the Files & Readings panel — same indigo tint the Pro Tips
   accordion + meetings-list cards use, so every detail surface
   shares one hover language in dark mode. */
html[data-theme="dark"] .fe-meeting-detail-card:hover,
body.fe-frontend-force-dark .fe-meeting-detail-card:hover,
html[data-theme="dark"] .fe-meeting-detail-grid > .fe-meeting-resources:hover,
body.fe-frontend-force-dark .fe-meeting-detail-grid > .fe-meeting-resources:hover {
  border-color: rgba(122, 163, 255, 0.4);
}
html[data-theme="dark"] .fe-meeting-detail-day,
html[data-theme="dark"] .fe-meeting-detail-time,
html[data-theme="dark"] .fe-meeting-detail-location { color: #e2e8f0; }
html[data-theme="dark"] .fe-meeting-detail-creds code {
  background: #0f172a; color: #e2e8f0; border-color: #334155;
}
html[data-theme="dark"] .fe-copy-btn { border-color: #334155; }
html[data-theme="dark"] .fe-meeting-detail-alert {
  background: rgba(245, 158, 11, 0.15); color: #fbbf24;
  border-color: rgba(245, 158, 11, 0.30);
}

/* ============================================================
   PUBLIC EVENT DETAIL PAGE
   Mirrors the meeting-detail layout: max-width column, optional
   cover image, header, summary, body, then a grid of cards (when,
   location, zoom, website, contact).
   ============================================================ */
.fe-event-detail {
  padding: 56px 0 80px;
  background: var(--tpl-bg, var(--fe-color-surface, #fff));
}
.fe-event-detail-inner { max-width: 880px; margin: 0 auto; }
.fe-event-detail-back {
  display: inline-block;
  color: var(--fe-ink-muted); font-size: 0.9rem; text-decoration: none;
}
.fe-event-detail-back:hover { color: var(--fe-accent); text-decoration: none; }

/* Hero row: featured image on the left, chips/heading/summary on the
   right. Below 860 px (≈ tablet portrait) the row collapses to a
   single column so the cover stacks above the heading on phones. The
   `has-cover` modifier is what enables the 2-col grid — when there's
   no featured image the head spans the row by itself. */
.fe-event-detail-hero {
  display: grid; grid-template-columns: 1fr; gap: 24px;
  margin: 0 0 28px;
  align-items: start;
}
@media (min-width: 860px) {
  .fe-event-detail-hero.has-cover {
    grid-template-columns: minmax(0, 427px) minmax(0, 1fr);
    gap: 32px;
  }
}

/* Cover button — 4:3 frame at a fixed 320 px height on desktop so the
   image always reads as a clean rectangle next to the heading. The
   <img> covers the frame regardless of its native aspect ratio. The
   button itself is styled to look like a card; on hover it lifts and
   the inset zoom glyph fades in to advertise the lightbox. */
.fe-event-detail-cover {
  position: relative;
  display: block; padding: 0; margin: 0;
  width: 100%; aspect-ratio: 4 / 3;
  border-radius: 16px; overflow: hidden;
  background: var(--fe-color-surface-alt, #f8fafc);
  border: 1px solid var(--fe-border);
  cursor: zoom-in;
  font: inherit; color: inherit;
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
@media (min-width: 860px) {
  .fe-event-detail-cover { height: 320px; aspect-ratio: 4 / 3; }
}
.fe-event-detail-cover:hover {
  transform: translateY(-3px);
  border-color: var(--fe-accent, var(--fe-border));
  box-shadow: 0 10px 32px rgba(15,23,42,0.08);
}
.fe-event-detail-cover:focus-visible {
  outline: 2px solid var(--fe-accent, currentColor);
  outline-offset: 3px;
}
.fe-event-detail-cover img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
.fe-event-detail-cover-zoom {
  position: absolute; top: 12px; right: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 999px;
  background: rgba(15, 23, 42, 0.55); color: #fff;
  opacity: 0; transition: opacity 180ms ease;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.fe-event-detail-cover-zoom .icon { width: 18px; height: 18px; }
.fe-event-detail-cover:hover .fe-event-detail-cover-zoom,
.fe-event-detail-cover:focus-visible .fe-event-detail-cover-zoom { opacity: 1; }

.fe-event-detail-chips {
  display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px;
}
.fe-event-detail-head h1 {
  font-family: var(--tpl-heading-font, var(--fe-font-heading));
  font-weight: 700;
  font-size: var(--tpl-heading-size, clamp(2rem, 4vw, 2.75rem));
  line-height: 1.15; letter-spacing: -0.02em;
  margin: 0 0 12px; color: var(--fe-color-text, var(--fe-ink));
}
.fe-event-detail-summary {
  font-size: 1.125rem; line-height: 1.55;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  margin: 0 0 24px;
}
.fe-event-detail-desc {
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, 1.0625rem);
  line-height: 1.7; color: var(--fe-color-text, var(--fe-ink));
  margin: 0 0 32px;
}
.fe-event-detail-desc p { margin: 0 0 16px; }
.fe-event-detail-desc > :first-child { margin-top: 0; }
.fe-event-detail-desc > :last-child { margin-bottom: 0; }
.fe-event-detail-grid {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.fe-event-detail-card {
  /* Surface + border read from the Primary card design tokens (same
     as `.fe-meeting-detail-card`) so the announcement / event /
     archive detail panels share the elevated meeting-card look. The
     aggregator rules near the bottom of this file wire the hover
     lift + shadow + transition through the matching Primary tokens
     too — see the `.fe-event-detail-card` entry in the
     `.fe-meeting-card,*-detail-card,*-extended-card,…` selectors. */
  padding: 24px; border-radius: 14px;
  background: var(--fe-color-card-primary-bg, var(--fe-color-surface, #ffffff));
  border: var(--fe-card-primary-border-width, 1px) solid var(--fe-color-card-primary-border, var(--fe-accent));
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
/* The shared `.fe-meeting-card:hover, .fe-meeting-detail-card:hover,
   …, .fe-card-primary:hover` aggregator below provides the lift +
   accent-tinted shadow on hover via the Primary card tokens, so no
   per-card hover rule is needed here. */
.fe-event-detail-card h3 {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--fe-font-heading); font-weight: 700;
  font-size: 1rem; margin: 0 0 12px; color: var(--fe-ink);
}
.fe-event-detail-card h3 .icon { width: 18px; height: 18px; color: var(--fe-accent); }
.fe-event-detail-when {
  margin: 0; display: flex; flex-direction: column; gap: 4px;
}
.fe-event-detail-day {
  font-weight: 700; color: var(--fe-ink); font-size: 1rem;
}
.fe-event-detail-time {
  color: var(--fe-ink); font-variant-numeric: tabular-nums;
}
.fe-event-detail-location-name {
  font-weight: 700; color: var(--fe-ink); margin: 0 0 4px;
}
.fe-event-detail-location {
  font-size: 1rem; line-height: 1.55; color: var(--fe-ink);
  margin: 0 0 14px; white-space: pre-line;
}
.fe-event-detail-zoom .fe-btn { margin-bottom: 18px; }
.fe-event-detail-creds { margin: 0; display: grid;
  grid-template-columns: max-content 1fr; column-gap: 14px; row-gap: 8px;
}
.fe-event-detail-creds dt {
  font-size: 0.8125rem; font-weight: 600; color: var(--fe-ink-muted);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.fe-event-detail-creds dd { margin: 0; display: flex; align-items: center; gap: 8px; }
.fe-event-detail-creds code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.95rem; padding: 4px 8px;
  background: #fff; border: 1px solid var(--fe-border); border-radius: 6px;
  color: var(--fe-ink);
}
.fe-event-detail-creds a { color: var(--fe-accent); text-decoration: none; }
.fe-event-detail-creds a:hover { text-decoration: underline; }

html[data-theme="dark"] .fe-event-detail,
body.fe-frontend-force-dark .fe-event-detail { background: var(--fe-dm-page-bg); }
html[data-theme="dark"] .fe-event-detail-head h1,
body.fe-frontend-force-dark .fe-event-detail-head h1 { color: var(--fe-dm-text-strong); }
html[data-theme="dark"] .fe-event-detail-summary,
body.fe-frontend-force-dark .fe-event-detail-summary { color: var(--fe-dm-text); }
html[data-theme="dark"] .fe-event-detail-desc,
body.fe-frontend-force-dark .fe-event-detail-desc { color: var(--fe-dm-text); }
html[data-theme="dark"] .fe-event-detail-card,
body.fe-frontend-force-dark .fe-event-detail-card {
  background: var(--fe-color-card-secondary-bg-dark, var(--fe-dm-surface));
  border-color: var(--fe-color-card-secondary-border-dark, var(--fe-dm-border));
  color: var(--fe-dm-text);
}
/* Indigo edge-highlight on hover — same treatment as every other
   detail card on the site (meetings list, meeting detail, FAQ, etc.). */
html[data-theme="dark"] .fe-event-detail-card:hover,
body.fe-frontend-force-dark .fe-event-detail-card:hover {
  border-color: var(--fe-dm-border-hover);
}
/* Headings on the event detail cards — the light-mode rule sits at
   `.fe-event-detail-card h3 { color: ... }`; in dark mode bump to the
   strong text token so block titles ("When", "Location", "Zoom") read
   bright against the elevated surface. */
html[data-theme="dark"] .fe-event-detail-card h3,
body.fe-frontend-force-dark .fe-event-detail-card h3 {
  color: var(--fe-dm-text-strong);
}
html[data-theme="dark"] .fe-event-detail-day,
html[data-theme="dark"] .fe-event-detail-time,
html[data-theme="dark"] .fe-event-detail-location,
html[data-theme="dark"] .fe-event-detail-location-name,
body.fe-frontend-force-dark .fe-event-detail-day,
body.fe-frontend-force-dark .fe-event-detail-time,
body.fe-frontend-force-dark .fe-event-detail-location,
body.fe-frontend-force-dark .fe-event-detail-location-name { color: var(--fe-dm-text); }
html[data-theme="dark"] .fe-event-detail-creds code,
body.fe-frontend-force-dark .fe-event-detail-creds code {
  background: var(--fe-dm-code-bg);
  color: var(--fe-dm-text);
  border-color: transparent;
}
html[data-theme="dark"] .fe-event-detail-cover,
body.fe-frontend-force-dark .fe-event-detail-cover {
  background: var(--fe-dm-surface); border-color: var(--fe-dm-border);
}
/* Primary action button inside event detail cards (Join Zoom Meeting,
   Visit Event Website) — adopts the same dark-mode recipe as the hero
   CTA + nav primary buttons via the shared tokens, so every primary
   button on the dark site tracks the admin's chosen primary colour
   uniformly. */
html[data-theme="dark"] .fe-event-detail-card .fe-btn-primary,
body.fe-frontend-force-dark .fe-event-detail-card .fe-btn-primary {
  background: var(--fe-dm-btn-primary-bg) !important;
  color: var(--fe-dm-btn-primary-text) !important;
  border-color: var(--fe-dm-btn-primary-bg) !important;
  box-shadow: var(--fe-dm-btn-primary-shadow);
}
html[data-theme="dark"] .fe-event-detail-card .fe-btn-primary:hover,
body.fe-frontend-force-dark .fe-event-detail-card .fe-btn-primary:hover {
  background: var(--fe-dm-btn-primary-bg-hover) !important;
  color: var(--fe-dm-btn-primary-text-hover) !important;
  border-color: var(--fe-dm-btn-primary-bg-hover) !important;
}

/* ============================================================
   PUBLIC IMAGE LIGHTBOX
   Full-screen overlay used by the event-detail cover (and any other
   public-page image opted in via [data-img-lightbox-src]). Hidden
   by default; .is-open animates the backdrop + image in. The body
   gets overflow:hidden while open so the page beneath doesn't scroll.
   ============================================================ */
.fe-img-lightbox {
  position: fixed; inset: 0; z-index: 9999;
  display: none;
  align-items: center; justify-content: center;
  padding: 24px;
}
.fe-img-lightbox.is-open { display: flex; }
.fe-img-lightbox-backdrop {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.85);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  cursor: zoom-out;
  animation: fe-img-lightbox-fade 180ms ease;
}
.fe-img-lightbox-img {
  position: relative; z-index: 1;
  max-width: min(100%, 92vw); max-height: 92vh;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0,0,0,.4);
  animation: fe-img-lightbox-zoom 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.fe-img-lightbox-close {
  position: absolute; top: 16px; right: 20px; z-index: 2;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, 0.12); color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px; cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease;
}
.fe-img-lightbox-close:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.32);
}
.fe-img-lightbox-close .icon { width: 18px; height: 18px; }
@keyframes fe-img-lightbox-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes fe-img-lightbox-zoom {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}

/* ============================================================
   MARKETING BLOCK SECTIONS
   Used by the block-driven custom-layout renderer.
   ============================================================ */
.fe-features { background: #fff; }
.fe-features-grid {
  display: grid; gap: 28px; margin-top: 24px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.fe-feature-card {
  display: flex; flex-direction: column;
  padding: 28px 24px; border-radius: 18px;
  background: var(--fe-color-card-secondary-bg, var(--fe-panel-soft));
  border: var(--fe-card-secondary-border-width, 1px) solid var(--fe-color-card-secondary-border, var(--fe-border));
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
.fe-feature-card:hover {
  transform: translateY(-3px);
  border-color: var(--fe-color-card-secondary-hover-border,
                    var(--fe-accent, var(--fe-border)));
  box-shadow: 0 10px 32px rgba(15,23,42,0.08);
}
/* Per-card CTA. Cards are always <article> now (the public template
   doesn't wrap them in <a> any more) so the title + body never pick up
   the global ``.fe-page a:hover`` underline. The link, when set, sits
   inside the card as a real button using one of the two design-system
   styles (Primary = ``.fe-btn-primary``, Secondary = ``.fe-btn-ghost``).
   ``margin-top: auto`` on the actions row keeps the CTA aligned across
   cards even when bodies differ in length. */
.fe-feature-card-actions {
  margin-top: auto; padding-top: 16px;
  display: flex; flex-wrap: wrap; gap: 10px;
}
.fe-feature-card-cta { align-self: flex-start; }
.fe-feature-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 10px;
  background: rgba(81, 100, 255, 0.12); color: var(--fe-accent);
  margin-bottom: 14px;
}
.fe-feature-icon .icon { width: 22px; height: 22px; }
.fe-feature-card h3 { font-family: var(--fe-font-heading); font-weight: 700;
  font-size: 1.375rem; margin: 0 0 8px; }
.fe-feature-card p { color: var(--fe-ink-muted); line-height: 1.55; }
.fe-feature-body { color: var(--fe-ink-muted); line-height: 1.55; }
.fe-feature-body > :first-child { margin-top: 0; }
.fe-feature-body > :last-child { margin-bottom: 0; }
.fe-feature-body p { margin: 0 0 0.6em; }
.fe-feature-body ul, .fe-feature-body ol { margin: 0 0 0.6em 1.2em; padding: 0; }
.fe-feature-body li { margin: 0.15em 0; }
.fe-feature-body strong { color: var(--fe-ink); }
html[data-theme="dark"] .fe-feature-body { color: #94a3b8; }
html[data-theme="dark"] .fe-feature-body strong { color: #e2e8f0; }
html[data-theme="dark"] .fe-features { background: #0b1026; }
html[data-theme="dark"] .fe-feature-card {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, transparent);
  color: #e2e8f0;
}
html[data-theme="dark"] .fe-feature-card p { color: #94a3b8; }
html[data-theme="dark"] .fe-feature-icon { background: rgba(81,100,255,0.18); color: #7aa3ff; }

.fe-cta-block {
  background: linear-gradient(135deg, #0a3eb5 0%, #1153e7 60%, #2e6bff 100%);
  color: #fff; text-align: center;
}
.fe-cta-inner { max-width: 720px; margin: 0 auto; padding: 64px 0; }
.fe-cta-block h2 {
  font-family: var(--fe-font-heading); font-weight: 800;
  font-size: clamp(2rem, 5vw, 3.25rem); margin: 0 0 16px; color: #fff;
}
.fe-cta-block p { font-size: 1.125rem; line-height: 1.55; margin: 0 0 28px;
  color: rgba(255,255,255,0.86); }
.fe-cta-actions { display: inline-flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.fe-cta-block .fe-btn-primary { background: #fff; color: #0a3eb5; }
.fe-cta-block .fe-btn-primary:hover { background: #f1f5f9; color: #0a3eb5; }
.fe-cta-block .fe-btn-ghost { color: #fff; border-color: rgba(255,255,255,0.6); }
.fe-cta-block .fe-btn-ghost:hover { background: rgba(255,255,255,0.12); border-color: #fff; }

.fe-stats-block { background: var(--fe-panel-soft); padding: 56px 0; }
.fe-stats-grid {
  display: grid; gap: 24px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  text-align: center;
}
.fe-stat-num {
  font-family: var(--fe-font-heading); font-weight: 800;
  font-size: clamp(2.4rem, 5vw, 3.5rem); line-height: 1; color: var(--fe-ink);
  letter-spacing: -0.02em;
}
.fe-stat-lbl { margin-top: 8px; color: var(--fe-ink-muted);
  font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.06em; }
html[data-theme="dark"] .fe-stats-block { background: #0b1026; }
html[data-theme="dark"] .fe-stat-num { color: #e2e8f0; }
html[data-theme="dark"] .fe-stat-lbl { color: #94a3b8; }

.fe-testimonial-grid {
  display: grid; gap: 20px; margin-top: 24px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.fe-testimonial {
  background: var(--fe-panel-soft); border-radius: 14px;
  padding: 28px 26px; margin: 0; border: 1px solid var(--fe-border);
}
.fe-testimonial p {
  font-family: var(--fe-font-heading); font-style: italic;
  font-size: 1.125rem; line-height: 1.55; color: var(--fe-ink);
  margin: 0 0 14px;
}
.fe-testimonial cite { font-style: normal; font-size: 0.85rem;
  color: var(--fe-ink-muted); }
html[data-theme="dark"] .fe-testimonial { background: #131a33; border-color: transparent; }
html[data-theme="dark"] .fe-testimonial p { color: #e2e8f0; }
html[data-theme="dark"] .fe-testimonial cite { color: #94a3b8; }

.fe-faq-list { display: flex; flex-direction: column; gap: 8px;
  max-width: 760px; margin: 24px auto 0; }

/* Full-width variant — page-builder knob `width_mode=full`. Drops the
   760px column cap so the accordion bleeds to the parent container's
   edge. Pairs naturally with the 2-column layout below; on a 1-column
   FAQ inside a wide page it produces a single very-wide column. */
.fe-faq-w-full .fe-faq-list { max-width: none; }

/* Bare FAQ — page-builder rendering inside a container drops the
   `.fe-section` + `.fe-container` chrome. The accordion fills the
   parent container edge-to-edge and lets the container own width +
   horizontal padding. Section-head margin still reads as a top
   separator without inheriting the 80-px section padding. */
.fe-faq--bare { padding: 0; }
/* Keep the default top/bottom margins on the inner blocks so vertical
   rhythm survives; only zero out horizontal margins (auto-centering
   was previously clamping width). The parent container's padding
   handles any left/right gutter. */
.fe-faq--bare .fe-faq-list { max-width: none; margin-left: 0; margin-right: 0; }
.fe-faq--bare .fe-section-head { margin-left: 0; margin-right: 0; max-width: none; }
/* Self-padding floor — when the parent container has no horizontal
   padding (common on full-bleed page-builder layouts), the bare FAQ
   would otherwise sit flush against the viewport. Apply the site-wide
   container padding tokens (Site → Design → Layout) at every width so
   the accordion always has a visible gutter, separate from whatever
   the parent container provides. Doesn't affect any parent background
   — only the FAQ's own inner content is inset. */
.fe-faq--bare {
  padding-left: var(--fe-container-pad-desktop, 5vw);
  padding-right: var(--fe-container-pad-desktop, 5vw);
}
@media (max-width: 768px) {
  .fe-faq--bare {
    padding-left: var(--fe-container-pad-mobile, 5vw);
    padding-right: var(--fe-container-pad-mobile, 5vw);
  }
  /* Same mobile floor for the homepage Meetings + Events blocks.
     Their inner `.fe-container` wrapper was dropped so the page-
     builder container could control width + gutter — fine on
     desktop where the admin sets a sensible padding, but the
     homepage's full-bleed containers explicitly carry `0 0 0 0`
     mobile padding so the blocks render flush. Self-padding on
     mobile keeps the cards inset without forcing the parent's
     edge-to-edge desktop layout to also gain a gutter. */
  .fe-meetings,
  .fe-events {
    padding-left: var(--fe-container-pad-mobile, 5vw);
    padding-right: var(--fe-container-pad-mobile, 5vw);
  }
}

/* 2-column variant — page-builder knob `columns=2`. The template
   renders two `.fe-faq-col` flex stacks (left + right) so each
   column is its own independent stacking context. This is critical:
   if both columns shared a CSS Grid row, items in the same row would
   share that row's height — and the 56px stagger on the right
   column's first item would inflate row 1, leaving a phantom gap
   below the first left-column item. Separate stacks mean the right
   column's heights cannot influence the left's.

   The OUTER container is a flex row (not a grid) so we can use
   `align-items: flex-start` to keep each column sized to its own
   content rather than stretched to match its peer. Grid's default
   `align-items: normal` resolves to `stretch` for auto-sized
   tracks, which would stretch the shorter (right) column to the
   row height and leave empty space below its last card — making
   the cards visually appear to sit in the middle of the column.
   Flex's `align-items: flex-start` keeps each column hugging its
   content so cards always sit at the top of their column. */
.fe-faq-cols-2 .fe-faq-list {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 16px;
  max-width: 1080px;
}
.fe-faq-w-full.fe-faq-cols-2 .fe-faq-list { max-width: none; }
.fe-faq-cols-2 .fe-faq-col {
  flex: 1 1 0;
  display: flex; flex-direction: column;
  justify-content: flex-start;
  gap: 8px; min-width: 0;
}

/* Both columns sit flush against the top — the right column's
   first card aligns with the left column's first card. The
   `.fe-faq-col2-start` class is still emitted by the template
   (in case a future stagger variant wants to hook onto it) but
   carries no positioning rule by default. */
.fe-faq-cols-2 .fe-faq-col2-start { margin-top: 0; }

@media (max-width: 720px) {
  /* Drop back to a single-column stack on phones. The flex row
     wraps so the two columns flow vertically. */
  .fe-faq-cols-2 .fe-faq-list {
    flex-direction: column;
    max-width: 760px;
  }
  .fe-faq-cols-2 .fe-faq-col { flex: 1 1 auto; }
}
.fe-faq-item {
  background: var(--fe-color-card-secondary-bg, var(--fe-panel-soft));
  border-radius: 12px;
  padding: 0;
  border: var(--fe-card-secondary-border-width, 1px) solid var(--fe-color-card-secondary-border, var(--fe-border));
  overflow: hidden;
  transition: border-color 200ms ease, box-shadow 200ms ease;
}
.fe-faq-item:hover { border-color: var(--fe-accent, var(--fe-border)); }
.fe-faq-item.is-open {
  border-color: var(--fe-accent, var(--fe-border));
  box-shadow: 0 6px 22px rgba(15,23,42,0.06);
}
.fe-faq-summary {
  width: 100%; display: flex; align-items: center; gap: 14px;
  padding: 18px 22px; background: transparent; border: 0;
  font: inherit; font-weight: 600; font-size: 1rem; color: var(--fe-ink);
  text-align: left; cursor: pointer;
}
.fe-faq-summary:focus-visible {
  outline: 2px solid var(--fe-accent, currentColor); outline-offset: -3px;
  border-radius: 12px;
}
.fe-faq-summary-icon {
  display: inline-flex; align-items: center; justify-content: center;
  --icon-size: 20px;
  width: calc(var(--icon-size) + 16px); height: calc(var(--icon-size) + 16px);
  flex: 0 0 auto;
  border-radius: 8px;
  background: rgba(81, 100, 255, 0.12); color: var(--fe-accent);
}
.fe-faq-summary-icon .icon { width: var(--icon-size); height: var(--icon-size); }
.fe-faq-summary-q { flex: 1 1 auto; min-width: 0; }
.fe-faq-summary-chevron {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; color: var(--fe-ink-muted);
  transition: transform 220ms ease, color 200ms ease;
}
.fe-faq-summary-chevron .icon { width: 18px; height: 18px; }
.fe-faq-item.is-open .fe-faq-summary-chevron {
  transform: rotate(180deg); color: var(--fe-accent);
}
/* Animated body — `display: grid` + `grid-template-rows: 0fr → 1fr` is
   the modern accordion trick: the inner element's natural height drives
   the row, and animating the track between zero-fraction and one-fraction
   gives a smooth height transition without measuring scrollHeight in JS. */
.fe-faq-body {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.fe-faq-item.is-open .fe-faq-body { grid-template-rows: 1fr; }
.fe-faq-body-inner {
  min-height: 0; overflow: hidden;
  padding: 0 22px;
  color: var(--fe-ink-muted); line-height: 1.6;
}
.fe-faq-item.is-open .fe-faq-body-inner { padding: 4px 22px 18px; }
.fe-faq-body-inner > :first-child { margin-top: 0; }
.fe-faq-body-inner > :last-child { margin-bottom: 0; }
.fe-faq-body-inner p { margin: 0 0 0.6em; }
.fe-faq-body-inner ul, .fe-faq-body-inner ol { margin: 0 0 0.6em 1.2em; padding: 0; }
.fe-faq-body-inner li { margin: 0.15em 0; }
.fe-faq-body-inner a { color: var(--fe-color-link, var(--fe-accent)); }
.fe-faq-body-inner strong { color: var(--fe-ink); }

html[data-theme="dark"] .fe-faq-item {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, transparent);
}
/* Pro Tips cards adopt the Primary-card recipe via the
   `fe-card-primary` opt-in class stamped on each `.fe-faq-item` in
   the Pro Tips templates, so the canonical `.fe-card-primary` +
   `[data-theme="dark"] .fe-card-primary` rules near the bottom of
   this file (plus the shared primary-card hover aggregator) drive
   bg / border / hover lift / shadow / dark-mode swap for them too.
   No Pro-Tips-specific overrides needed. */
html[data-theme="dark"] .fe-faq-item:hover,
html[data-theme="dark"] .fe-faq-item.is-open {
  border-color: rgba(122, 163, 255, 0.4);
}
html[data-theme="dark"] .fe-faq-summary { color: #e2e8f0; }
html[data-theme="dark"] .fe-faq-summary-icon { background: rgba(81,100,255,0.18); color: #7aa3ff; }
html[data-theme="dark"] .fe-faq-body-inner { color: #94a3b8; }
html[data-theme="dark"] .fe-faq-body-inner strong { color: #e2e8f0; }

/* ---------- Inclusion block ---------- */
.fe-inclusion { padding: 56px 0; }
.fe-inclusion-card {
  max-width: 760px; margin: 0 auto;
  background: var(--fe-color-card-secondary-bg, var(--fe-panel-soft));
  border: var(--fe-card-secondary-border-width, 1px) solid var(--fe-color-card-secondary-border, var(--fe-border));
  border-radius: 18px; padding: 40px 32px;
  display: flex; flex-direction: column; gap: 18px;
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
.fe-inclusion-card:hover {
  transform: translateY(-3px);
  border-color: var(--fe-accent, var(--fe-border));
  box-shadow: 0 10px 32px rgba(15,23,42,0.08);
}
.fe-inclusion--center .fe-inclusion-card { align-items: center; text-align: center; }
.fe-inclusion--left   .fe-inclusion-card { align-items: flex-start; text-align: left;  }
.fe-inclusion--right  .fe-inclusion-card { align-items: flex-end;   text-align: right; }
.fe-inclusion-icon {
  display: inline-flex; align-items: center; justify-content: center;
  /* Badge auto-grows around the icon glyph: glyph + 28px of total padding.
     Base size of 28px keeps the legacy 56×56 footprint for content without
     a custom size set. The admin can override --icon-size inline to scale. */
  --icon-size: 28px;
  width: calc(var(--icon-size) + 28px);
  height: calc(var(--icon-size) + 28px);
  border-radius: 14px;
  background: rgba(81, 100, 255, 0.12); color: var(--fe-accent);
  transition: color 200ms ease, background 200ms ease;
}
.fe-inclusion-icon .icon { width: var(--icon-size); height: var(--icon-size); }
.fe-inclusion-heading {
  font-family: var(--fe-font-heading); font-weight: 800;
  font-size: clamp(1.6rem, 3.5vw, 2.25rem); margin: 0; color: var(--fe-ink);
  letter-spacing: -0.01em;
}
.fe-inclusion-body {
  font-size: 1.05rem; line-height: 1.65; color: var(--fe-ink-muted); margin: 0;
}
.fe-inclusion-body p { margin: 0 0 0.8em; }
.fe-inclusion-body p:last-child { margin-bottom: 0; }
.fe-inclusion-body strong { color: var(--fe-ink); }
.fe-inclusion-tags {
  list-style: none; padding: 0; margin: 4px 0 0;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.fe-inclusion--center .fe-inclusion-tags { justify-content: center; }
.fe-inclusion--right  .fe-inclusion-tags { justify-content: flex-end; }
.fe-inclusion-tag {
  display: inline-flex; align-items: center;
  padding: 6px 12px; border-radius: 999px;
  background: rgba(81, 100, 255, 0.10); color: var(--fe-accent);
  font-size: 0.875rem; font-weight: 600; letter-spacing: 0.01em;
  border: 1px solid rgba(81, 100, 255, 0.18);
}
.fe-inclusion-cta { margin-top: 8px; }
html[data-theme="dark"] .fe-inclusion-card {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, transparent);
}
html[data-theme="dark"] .fe-inclusion-heading { color: #e2e8f0; }
html[data-theme="dark"] .fe-inclusion-body { color: #94a3b8; }
html[data-theme="dark"] .fe-inclusion-body strong { color: #e2e8f0; }
html[data-theme="dark"] .fe-inclusion-icon { background: rgba(81,100,255,0.18); color: #7aa3ff; }
html[data-theme="dark"] .fe-inclusion-tag {
  background: rgba(122, 163, 255, 0.14); color: #cbd9ff;
  border-color: rgba(122, 163, 255, 0.26);
}

/* ===========================================================================
   Meeting logo — shared across every public meeting detail template. Pulled
   from Meeting.logo_filename via /pub/meeting-logo/<id>.
   =========================================================================== */
.fe-meeting-logo-wrap { margin: 0 0 24px; }
.fe-meeting-logo-wrap-center { text-align: center; }
.fe-meeting-logo {
  display: block; width: 180px; max-width: 100%; height: auto;
  border-radius: var(--fe-card-radius, 12px);
}
.fe-meeting-logo-wrap-center .fe-meeting-logo { margin: 0 auto; }

/* ===========================================================================
   Files & Readings block — admin-curated public-visible files/readings
   shown after Zoom/Location on every meeting detail template. Inside the
   Classic grid it acts as a sibling card; on the other templates it stands
   on its own and inherits surrounding spacing from the parent layout.
   =========================================================================== */
.fe-meeting-resources {
  /* Files & Readings panel reads from the Primary card design tokens
     so it shares the elevated meeting-card visual family with the
     Schedule / Location / Zoom sibling cards in the Classic detail
     grid. Hover lift / shadow / hover-border are wired through the
     shared primary aggregator near the bottom of this file. Radius
     is pinned to 14 px (the same hardcoded value the
     `.fe-meeting-detail-card` siblings use) so the two surfaces
     share corner rounding regardless of what the admin's
     `--fe-card-radius` design token resolves to. */
  background: var(--fe-color-card-primary-bg, var(--fe-color-surface, #ffffff));
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 14px;
  padding: 22px 24px;
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
.fe-meeting-detail-grid > .fe-meeting-resources {
  /* Inside the Classic detail grid the panel sits next to the
     `.fe-meeting-detail-card` siblings. Pad to 24px so the inner
     content lines up exactly; everything else (surface, border,
     hover) inherits from the base rule above. */
  padding: 24px;
}
.fe-meeting-resources h3 {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  margin: 0 0 14px;
}
.fe-meeting-resources h3 .icon {
  width: 18px; height: 18px;
  color: var(--fe-color-brand, var(--fe-accent));
}
.fe-meeting-resources-group + .fe-meeting-resources-group { margin-top: 16px; }
.fe-meeting-resources-group h4 {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  margin: 0 0 8px;
}
.fe-meeting-resources-group ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.fe-meeting-resources-group li {
  display: flex; flex-direction: column; gap: 2px;
}
.fe-page .fe-meeting-resources-group li > a,
.frontend-body .fe-meeting-resources-group li > a {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--fe-color-link, var(--fe-color-brand, var(--fe-accent)));
  font-weight: 600;
}
html[data-theme="dark"] .fe-meeting-resources { background: #131a33; border-color: transparent; }
/* Dark-mode parity: Files & Readings inside the Classic grid was
   previously hand-tinted #1a2247 (noticeably brighter blue) which made
   it stand out from its #131a33 sibling cards. Land them on the same
   shade so the row reads cohesively in dark mode too. */
html[data-theme="dark"] .fe-meeting-detail-grid > .fe-meeting-resources {
  background: #131a33; border-color: transparent;
}

/* ===========================================================================
   Reusable detail-page templates (Meeting Detail / Event Detail variants).

   All colors, surfaces, borders, radii, and shadows source from the design
   tokens emitted by app/design.py (--fe-color-*, --fe-card-radius,
   --fe-card-shadow, etc.) so that whatever an admin chooses on the
   Web Frontend → Design page applies uniformly across every variant.
   Raw :root vars are kept as fallbacks for safety.

   Buttons reuse the global .fe-btn/.fe-btn-primary/.fe-btn-ghost system —
   not a separate per-template button — so primary/secondary colors and
   text-decoration always match the design admin's button settings. The
   per-template `.fe-*-cta` classes are *modifiers only* (they tune icon
   spacing inside the button, not its colors).
   =========================================================================== */

/* ----- Token aliases used below ----------------------------------------- */
/* Each rule below substitutes:
   var(--fe-color-text,        var(--fe-ink))         — body/headline color
   var(--fe-color-text-soft,   var(--fe-ink-muted))   — muted text
   var(--fe-color-surface,     var(--fe-panel))       — page bg
   var(--fe-color-surface-alt, var(--fe-panel-soft))  — soft surfaces / chips
   var(--fe-color-border,      var(--fe-border))      — hairlines / outlines
   var(--fe-color-brand,       var(--fe-accent))      — accent / link color
   var(--fe-color-link,        var(--fe-color-brand, var(--fe-accent))) — inline links
*/

/* ===========================================================================
   Meeting detail — Card Stack template
   Gradient hero banner + big primary action card up top. Mobile-first.
   =========================================================================== */
.fe-meeting-stack {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel))); padding: 0 0 80px;
}
.fe-meeting-stack-hero {
  position: relative; padding: 56px 0 64px;
  background:
    radial-gradient(120% 80% at 20% 0%,
      color-mix(in srgb, var(--fe-color-brand, var(--fe-accent)) 22%, transparent) 0%, transparent 60%),
    radial-gradient(120% 80% at 100% 100%,
      color-mix(in srgb, var(--fe-color-accent, var(--fe-accent-2, var(--fe-accent))) 22%, transparent) 0%, transparent 60%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--fe-color-brand, var(--fe-accent)) 8%, var(--fe-color-surface, var(--fe-panel))) 0%,
      var(--fe-color-surface, var(--fe-panel)) 100%);
  overflow: hidden;
}
.fe-meeting-stack-hero-glow {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 60% at 50% 100%, rgba(255,255,255,0.6) 0%, transparent 70%);
}
.fe-meeting-stack-hero-inner {
  position: relative; max-width: 760px; margin: 0 auto; padding: 0 24px;
  text-align: center;
}
.fe-page .fe-meeting-stack-back,
.frontend-body .fe-meeting-stack-back {
  display: inline-block; margin-bottom: 24px;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-decoration: none; font-size: 0.875rem;
}
.fe-page .fe-meeting-stack-back:hover,
.frontend-body .fe-meeting-stack-back:hover {
  color: var(--fe-color-brand, var(--fe-accent)); text-decoration: none;
}
.fe-meeting-stack-chip {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 18px;
  padding: 6px 14px; border-radius: 999px;
  background: rgba(255,255,255,0.85); backdrop-filter: blur(6px);
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fe-color-text, var(--fe-ink));
  border: 1px solid var(--fe-color-border, var(--fe-border));
}
.fe-meeting-stack-chip .icon { width: 14px; height: 14px; }
.fe-meeting-stack-hero-inner h1 {
  margin: 0 0 14px;
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-size: var(--tpl-heading-size, clamp(2rem, 5vw, 3.25rem));
  line-height: 1.1; font-weight: 700;
  color: var(--fe-color-text, var(--fe-ink));
  letter-spacing: -0.02em;
}
.fe-meeting-stack-subline {
  margin: 0; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  font-size: 0.95rem;
  display: inline-flex; flex-wrap: wrap; gap: 8px; justify-content: center;
}
.fe-meeting-stack-subline .dot { color: var(--fe-color-border, var(--fe-border)); }
.fe-meeting-stack-body {
  max-width: 760px; margin: -32px auto 0; padding: 0 24px;
  display: flex; flex-direction: column; gap: 22px;
  position: relative; z-index: 2;
}
.fe-meeting-stack-alert {
  display: flex; gap: 10px; padding: 14px 16px;
  border-radius: var(--fe-card-radius, 12px);
  background: rgba(245, 158, 11, 0.10); color: #78350f;
  border: 1px solid rgba(245, 158, 11, 0.35);
  font-size: 0.9375rem; line-height: 1.5;
}
.fe-meeting-stack-alert .icon { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; }
.fe-meeting-stack-action {
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-color-border, var(--fe-border));
  border-radius: var(--fe-card-radius, 18px);
  padding: 26px 24px;
  box-shadow: var(--fe-card-shadow, 0 6px 30px rgba(15, 23, 42, 0.06));
}
.fe-meeting-stack-action-soft {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
}
.fe-meeting-stack-action-head { margin-bottom: 16px; }
.fe-meeting-stack-action-eyebrow {
  display: block; font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); margin-bottom: 4px;
}
.fe-meeting-stack-action h2 {
  margin: 0; font-size: 1.25rem;
  color: var(--fe-color-text, var(--fe-ink));
  display: inline-flex; align-items: center; gap: 8px;
}
.fe-meeting-stack-action h2 .icon {
  width: 18px; height: 18px; color: var(--fe-color-brand, var(--fe-accent));
}
/* CTA modifier — adds icon arrow spacing on top of .fe-btn. Color, padding,
   radius, weight, and decoration all come from .fe-btn / .fe-btn-primary /
   .fe-btn-ghost which are wired to the design tokens. */
.fe-meeting-stack-cta { gap: 10px; }
.fe-meeting-stack-cta .icon { width: 18px; height: 18px; }
.fe-meeting-stack-cta em { font-style: normal; opacity: 0.85; margin-left: 4px; }
.fe-meeting-stack-creds {
  margin: 18px 0 0; display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px;
}
.fe-meeting-stack-creds > div { display: flex; flex-direction: column; gap: 4px; }
.fe-meeting-stack-creds dt {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-meeting-stack-creds dd { margin: 0; display: flex; align-items: center; gap: 8px; }
.fe-meeting-stack-creds code {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  padding: 6px 10px; border-radius: 8px;
  font-size: 0.95rem; color: var(--fe-color-text, var(--fe-ink));
  font-family: ui-monospace, monospace;
}
.fe-meeting-stack-prose {
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-color-border, var(--fe-border));
  border-radius: var(--fe-card-radius, 18px);
  padding: 28px; color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  line-height: var(--fe-text-line-height, 1.7);
  font-size: var(--tpl-body-size, var(--fe-text-size-base, 1rem));
}
.fe-meeting-stack-prose p { margin: 0 0 14px; }
.fe-meeting-stack-prose p:last-child { margin-bottom: 0; }
.fe-meeting-stack-schedule {
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-color-border, var(--fe-border));
  border-radius: var(--fe-card-radius, 18px);
  padding: 22px 24px;
}
.fe-meeting-stack-schedule h3 {
  margin: 0 0 14px; font-size: 1rem;
  color: var(--fe-color-text, var(--fe-ink));
  display: inline-flex; align-items: center; gap: 8px;
}
.fe-meeting-stack-schedule h3 .icon {
  width: 16px; height: 16px; color: var(--fe-color-brand, var(--fe-accent));
}
.fe-meeting-stack-schedule ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column;
}
.fe-meeting-stack-schedule li {
  display: grid; grid-template-columns: 110px 1fr auto; gap: 12px; align-items: baseline;
  padding: 12px 0; border-top: 1px solid var(--fe-color-border, var(--fe-border));
}
.fe-meeting-stack-schedule li:first-child { border-top: none; }
.fe-meeting-stack-schedule-day { font-weight: 700; color: var(--fe-color-text, var(--fe-ink)); }
.fe-meeting-stack-schedule-time {
  color: var(--fe-color-text, var(--fe-ink)); font-variant-numeric: tabular-nums;
}
.fe-meeting-stack-schedule-time em {
  font-style: normal;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); font-size: 0.875rem;
}
.fe-meeting-stack-schedule-meta {
  font-size: 0.8125rem; color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
@media (max-width: 600px) {
  .fe-meeting-stack-schedule li { grid-template-columns: 1fr; gap: 2px; }
}
html[data-theme="dark"] .fe-meeting-stack { background: #0b1026; }
html[data-theme="dark"] .fe-meeting-stack-action,
html[data-theme="dark"] .fe-meeting-stack-prose,
html[data-theme="dark"] .fe-meeting-stack-schedule { background: #131a33; border-color: transparent; }
html[data-theme="dark"] .fe-meeting-stack-action-soft { background: #1a2247; }
html[data-theme="dark"] .fe-meeting-stack-hero-inner h1,
html[data-theme="dark"] .fe-meeting-stack-action h2,
html[data-theme="dark"] .fe-meeting-stack-prose,
html[data-theme="dark"] .fe-meeting-stack-schedule h3,
html[data-theme="dark"] .fe-meeting-stack-schedule-day,
html[data-theme="dark"] .fe-meeting-stack-schedule-time { color: #f1f5f9; }
html[data-theme="dark"] .fe-meeting-stack-creds code { background: #0b1026; color: #e2e8f0; }
html[data-theme="dark"] .fe-meeting-stack-chip {
  background: rgba(19, 26, 51, 0.85); color: #e2e8f0;
  border-color: rgba(255,255,255,0.1);
}

/* ===========================================================================
   Meeting detail — Magazine template
   Editorial: serif headline, hairline rule, two-column grid + sticky sidebar.
   =========================================================================== */
.fe-meeting-mag {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel)));
  padding: 56px 0 80px;
}
.fe-meeting-mag-inner { max-width: 1080px; margin: 0 auto; padding: 0 24px; }
.fe-page .fe-meeting-mag-back,
.frontend-body .fe-meeting-mag-back {
  display: inline-block; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-decoration: none; font-size: 0.875rem; margin-bottom: 36px;
}
.fe-page .fe-meeting-mag-back:hover,
.frontend-body .fe-meeting-mag-back:hover {
  color: var(--fe-color-brand, var(--fe-accent)); text-decoration: none;
}
.fe-meeting-mag-head { margin-bottom: 40px; }
.fe-meeting-mag-kicker {
  display: inline-block; font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fe-color-brand, var(--fe-accent)); margin-bottom: 14px;
}
.fe-meeting-mag-head h1 {
  margin: 0;
  font-family: var(--tpl-heading-font, var(--fe-font-display, 'Fraunces'), 'Fraunces', Georgia, serif);
  font-size: var(--tpl-heading-size, clamp(2.25rem, 5.5vw, 4rem));
  line-height: 1.05; font-weight: 600;
  color: var(--fe-color-text, var(--fe-ink)); letter-spacing: -0.02em;
}
.fe-meeting-mag-rule {
  margin-top: 28px; height: 1px;
  background: var(--fe-color-text, var(--fe-ink)); opacity: 0.85;
}
.fe-meeting-mag-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 56px;
}
@media (max-width: 800px) { .fe-meeting-mag-grid { grid-template-columns: 1fr; gap: 32px; } }
.fe-meeting-mag-main { min-width: 0; }
.fe-meeting-mag-alert {
  display: flex; gap: 10px; padding: 14px 16px; border-radius: 8px;
  background: rgba(245, 158, 11, 0.10); color: #78350f;
  border-left: 3px solid #f97316;
  margin-bottom: 24px; font-size: 0.9375rem;
}
.fe-meeting-mag-alert .icon { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 2px; }
.fe-meeting-mag-prose {
  color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, 1.0625rem);
  line-height: var(--fe-text-line-height, 1.75);
}
.fe-meeting-mag-prose p { margin: 0 0 18px; }
.fe-meeting-mag-prose p:first-child::first-letter {
  font-family: var(--fe-font-display, 'Fraunces'), Georgia, serif;
  font-size: 3.6rem; line-height: 0.85; float: left; padding: 6px 10px 0 0;
  color: var(--fe-color-brand, var(--fe-accent)); font-weight: 600;
}
.fe-meeting-mag-empty { font-style: italic; }
.fe-meeting-mag-side {
  display: flex; flex-direction: column; gap: 18px;
  position: sticky; top: calc(var(--fe-header-full-h, 80px) + 24px); align-self: start;
}
@media (max-width: 800px) { .fe-meeting-mag-side { position: static; } }
.fe-meeting-mag-side-card {
  padding: 18px 20px;
  border: var(--fe-card-secondary-border-width, 1px) solid var(--fe-color-card-secondary-border, var(--fe-color-border, var(--fe-border)));
  border-radius: var(--fe-card-radius, 4px);
  background: var(--fe-color-card-secondary-bg, var(--fe-color-surface-alt, var(--fe-panel-soft)));
}
.fe-meeting-mag-side-label {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); margin-bottom: 10px;
}
.fe-meeting-mag-side-label .icon { width: 14px; height: 14px; }
.fe-meeting-mag-side-sched {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 12px;
}
.fe-meeting-mag-side-sched li { display: flex; flex-direction: column; gap: 1px; }
.fe-meeting-mag-side-day {
  font-weight: 700; color: var(--fe-color-text, var(--fe-ink)); font-size: 0.95rem;
}
.fe-meeting-mag-side-time {
  color: var(--fe-color-text, var(--fe-ink)); font-variant-numeric: tabular-nums;
}
.fe-meeting-mag-side-meta {
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); font-size: 0.8125rem;
}
.fe-meeting-mag-side-addr { margin: 0 0 10px; color: var(--fe-color-text, var(--fe-ink)); }
/* Inline (non-button) link inside the sidebar — uses link tokens. */
.fe-page .fe-meeting-mag-side-link,
.frontend-body .fe-meeting-mag-side-link {
  display: inline-flex; align-items: center; gap: 4px;
  color: var(--fe-color-link, var(--fe-color-brand, var(--fe-accent)));
  font-weight: 600; font-size: 0.9rem;
}
/* CTA modifier on top of .fe-btn (compact size suited to the side card). */
.fe-meeting-mag-side-cta { margin-bottom: 12px; padding: 10px 14px; font-size: 0.9rem; }
.fe-meeting-mag-side-creds {
  margin: 0; display: grid; grid-template-columns: 70px 1fr; gap: 6px 10px; align-items: center;
}
.fe-meeting-mag-side-creds dt {
  font-size: 0.75rem; font-weight: 700;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-transform: uppercase; letter-spacing: 0.04em;
}
.fe-meeting-mag-side-creds dd { margin: 0; display: flex; align-items: center; gap: 4px;
  font-size: 0.875rem; }
.fe-meeting-mag-side-creds code {
  background: var(--fe-color-surface, var(--fe-panel));
  padding: 3px 7px; border-radius: 4px;
  font-size: 0.8125rem; color: var(--fe-color-text, var(--fe-ink));
  border: 1px solid var(--fe-color-border, var(--fe-border));
}
html[data-theme="dark"] .fe-meeting-mag { background: #0b1026; }
html[data-theme="dark"] .fe-meeting-mag-head h1,
html[data-theme="dark"] .fe-meeting-mag-prose,
html[data-theme="dark"] .fe-meeting-mag-side-day,
html[data-theme="dark"] .fe-meeting-mag-side-time,
html[data-theme="dark"] .fe-meeting-mag-side-addr { color: #f1f5f9; }
html[data-theme="dark"] .fe-meeting-mag-rule { background: #f1f5f9; opacity: 0.6; }
html[data-theme="dark"] .fe-meeting-mag-side-card {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, transparent);
}
html[data-theme="dark"] .fe-meeting-mag-side-creds code {
  background: #0b1026; color: #e2e8f0; border-color: rgba(255,255,255,0.08);
}

/* ===========================================================================
   Meeting detail — Minimal template
   Typography-driven, lots of whitespace, no cards.
   =========================================================================== */
.fe-meeting-min {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel)));
  padding: 80px 0 120px;
}
.fe-meeting-min-inner { max-width: 640px; margin: 0 auto; padding: 0 24px; }
.fe-page .fe-meeting-min-back,
.frontend-body .fe-meeting-min-back {
  display: inline-block; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-decoration: none; font-size: 0.8125rem; letter-spacing: 0.04em;
  margin-bottom: 60px;
}
.fe-page .fe-meeting-min-back:hover,
.frontend-body .fe-meeting-min-back:hover {
  color: var(--fe-color-brand, var(--fe-accent)); text-decoration: none;
}
.fe-meeting-min-head { margin-bottom: 36px; }
.fe-meeting-min-eyebrow {
  display: inline-block; font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.32em; text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); margin-bottom: 18px;
}
.fe-meeting-min-head h1 {
  margin: 0;
  font-family: var(--tpl-heading-font, var(--fe-font-display, 'Fraunces'), Georgia, serif);
  font-size: var(--tpl-heading-size, clamp(2.25rem, 6vw, 3.75rem));
  line-height: 1.05; font-weight: 400;
  color: var(--fe-color-text, var(--fe-ink)); letter-spacing: -0.025em;
}
.fe-meeting-min-alert {
  margin: 24px 0 0; padding: 12px 0;
  border-top: 1px solid var(--fe-color-border, var(--fe-border));
  border-bottom: 1px solid var(--fe-color-border, var(--fe-border));
  color: #9a3412; font-size: 0.95rem;
}
.fe-meeting-min-desc {
  margin: 36px 0 60px;
  color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, 1.0625rem);
  line-height: var(--fe-text-line-height, 1.75);
}
.fe-meeting-min-desc p { margin: 0 0 16px; }
.fe-meeting-min-info {
  margin: 0; display: grid; grid-template-columns: 90px 1fr;
  gap: 24px 28px; align-items: baseline;
}
.fe-meeting-min-info dt {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); padding-top: 4px;
}
.fe-meeting-min-info dd {
  margin: 0; color: var(--fe-color-text, var(--fe-ink));
  font-size: 1rem; line-height: 1.6;
}
.fe-meeting-min-info dd ul {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.fe-meeting-min-info dd ul li {
  display: flex; gap: 14px; flex-wrap: wrap; align-items: baseline;
}
.fe-meeting-min-info dd ul li > span:first-child { font-weight: 600; min-width: 90px; }
.fe-meeting-min-info dd ul li .muted { font-size: 0.875rem; }
.fe-page .fe-meeting-min-link,
.frontend-body .fe-meeting-min-link {
  color: var(--fe-color-link, var(--fe-color-brand, var(--fe-accent)));
  font-weight: 500; margin-left: 8px;
}
.fe-meeting-min-link-strong { font-weight: 700; }
.fe-meeting-min-cred {
  margin-top: 8px; display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem;
}
.fe-meeting-min-cred code {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  padding: 4px 8px; border-radius: 4px;
  font-size: 0.875rem; color: var(--fe-color-text, var(--fe-ink));
}
@media (max-width: 600px) {
  .fe-meeting-min-info { grid-template-columns: 1fr; gap: 8px 0; }
  .fe-meeting-min-info dt { padding-top: 18px; }
  .fe-meeting-min-info dt:first-child { padding-top: 0; }
}
html[data-theme="dark"] .fe-meeting-min { background: #0b1026; }
html[data-theme="dark"] .fe-meeting-min-head h1,
html[data-theme="dark"] .fe-meeting-min-desc,
html[data-theme="dark"] .fe-meeting-min-info dd { color: #f1f5f9; }
html[data-theme="dark"] .fe-meeting-min-cred code { background: #131a33; color: #e2e8f0; }

/* ===========================================================================
   Event detail — Poster template
   Full-bleed featured image hero with darkened overlay + ticket-style card.
   =========================================================================== */
.fe-event-poster {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel)));
  padding: 0 0 80px;
}
.fe-event-poster-hero {
  position: relative; min-height: 360px; padding: 80px 0;
  background-color: color-mix(in srgb,
    var(--fe-color-brand, var(--fe-accent)) 20%, var(--fe-color-text, var(--fe-ink)));
  background-size: cover; background-position: center;
  color: #fff;
}
.fe-event-poster.has-cover .fe-event-poster-hero { min-height: 460px; }
.fe-event-poster-hero-shade {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(15, 23, 42, 0.35) 0%, rgba(15, 23, 42, 0.78) 100%);
}
.fe-event-poster-hero-inner {
  position: relative; max-width: 880px; margin: 0 auto; padding: 0 24px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 18px;
}
.fe-page .fe-event-poster-back,
.frontend-body .fe-event-poster-back {
  display: inline-block; color: rgba(255,255,255,0.85);
  text-decoration: none; font-size: 0.875rem;
}
.fe-page .fe-event-poster-back:hover,
.frontend-body .fe-event-poster-back:hover { color: #fff; text-decoration: none; }
.fe-event-poster-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.fe-event-poster-chips span {
  padding: 5px 12px; border-radius: 999px;
  background: rgba(255,255,255,0.18); backdrop-filter: blur(6px);
  font-size: 0.75rem; font-weight: 600; letter-spacing: 0.04em;
  border: 1px solid rgba(255,255,255,0.22);
}
.fe-event-poster-hero-inner h1 {
  margin: 0;
  font-family: var(--tpl-heading-font, var(--fe-font-display, 'Fraunces'), Georgia, serif);
  font-size: var(--tpl-heading-size, clamp(2.25rem, 5.5vw, 4rem));
  line-height: 1.05; font-weight: 600;
  letter-spacing: -0.02em; max-width: 800px;
}
.fe-event-poster-summary {
  margin: 0; font-size: 1.125rem; max-width: 700px;
  color: rgba(255,255,255,0.92); line-height: 1.5;
}
.fe-event-poster-body {
  max-width: 880px; margin: -50px auto 0; padding: 0 24px;
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: 32px;
}
.fe-event-poster-ticket {
  position: relative;
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-color-border, var(--fe-border));
  border-radius: var(--fe-card-radius, 18px);
  padding: 28px; display: grid; gap: 24px;
  grid-template-columns: 120px 1fr; align-items: start;
  box-shadow: var(--fe-card-shadow, 0 12px 50px rgba(15, 23, 42, 0.12));
}
.fe-event-poster-ticket-notch {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-color-border, var(--fe-border));
  box-shadow: inset 0 0 0 6px var(--fe-color-surface-alt, var(--fe-panel-soft));
}
.fe-event-poster-ticket-notch.left { left: -12px; }
.fe-event-poster-ticket-notch.right { right: -12px; }
.fe-event-poster-ticket-date {
  border-right: 2px dashed var(--fe-color-border, var(--fe-border));
  padding-right: 24px;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.fe-event-poster-ticket-mon {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--fe-color-brand, var(--fe-accent));
}
.fe-event-poster-ticket-day {
  font-family: var(--fe-font-display, 'Fraunces'), Georgia, serif;
  font-size: 3.5rem; line-height: 1; font-weight: 600;
  color: var(--fe-color-text, var(--fe-ink)); margin: 4px 0;
}
.fe-event-poster-ticket-yr {
  font-size: 0.875rem; color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-event-poster-ticket-info { display: flex; flex-direction: column; gap: 14px; }
.fe-event-poster-ticket-info > div { display: flex; flex-direction: column; gap: 2px; }
.fe-event-poster-ticket-label {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-event-poster-ticket-val {
  color: var(--fe-color-text, var(--fe-ink)); font-size: 1rem; line-height: 1.5;
}
.fe-event-poster-ticket-actions {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px;
  padding-top: 18px;
  border-top: 1px solid var(--fe-color-border, var(--fe-border));
}
/* CTA modifier for the poster ticket — leans on .fe-btn for color/decoration. */
.fe-event-poster-cta { gap: 8px; padding: 12px 20px; font-size: 0.9375rem; }
.fe-event-poster-cta .icon { width: 16px; height: 16px; }
.fe-event-poster-cta em { font-style: normal; opacity: 0.85; }
.fe-event-poster-ticket-creds {
  grid-column: 1 / -1; margin: 0; display: grid;
  grid-template-columns: 110px 1fr; gap: 8px 14px; align-items: center;
}
.fe-event-poster-ticket-creds dt {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-event-poster-ticket-creds dd { margin: 0; display: flex; align-items: center; gap: 6px; }
.fe-event-poster-ticket-creds code {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  padding: 4px 9px; border-radius: 6px;
  font-size: 0.875rem; color: var(--fe-color-text, var(--fe-ink));
}
.fe-event-poster-prose {
  color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, 1.0625rem);
  line-height: var(--fe-text-line-height, 1.75);
}
.fe-event-poster-prose p { margin: 0 0 16px; }
.fe-event-poster-contact {
  border-top: 1px solid var(--fe-color-border, var(--fe-border)); padding-top: 24px;
}
.fe-event-poster-contact h3 {
  margin: 0 0 14px; font-size: 0.9rem;
  color: var(--fe-color-text, var(--fe-ink));
  display: inline-flex; align-items: center; gap: 6px;
  text-transform: uppercase; letter-spacing: 0.1em;
}
.fe-event-poster-contact h3 .icon {
  width: 16px; height: 16px; color: var(--fe-color-brand, var(--fe-accent));
}
.fe-event-poster-contact ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.fe-event-poster-contact ul li {
  display: grid; grid-template-columns: 80px 1fr; gap: 14px; align-items: baseline;
}
.fe-event-poster-contact ul li > span:first-child {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
@media (max-width: 600px) {
  .fe-event-poster-ticket { grid-template-columns: 1fr; padding: 22px; }
  .fe-event-poster-ticket-date {
    border-right: none;
    border-bottom: 2px dashed var(--fe-color-border, var(--fe-border));
    padding: 0 0 18px; flex-direction: row; gap: 10px; align-items: baseline;
  }
  .fe-event-poster-ticket-day { font-size: 2.5rem; margin: 0; }
}
html[data-theme="dark"] .fe-event-poster { background: #0b1026; }
html[data-theme="dark"] .fe-event-poster-ticket { background: #131a33; border-color: transparent; }
html[data-theme="dark"] .fe-event-poster-ticket-notch {
  background: #0b1026; border-color: rgba(255,255,255,0.08);
  box-shadow: inset 0 0 0 6px #131a33;
}
html[data-theme="dark"] .fe-event-poster-ticket-day,
html[data-theme="dark"] .fe-event-poster-ticket-val,
html[data-theme="dark"] .fe-event-poster-prose,
html[data-theme="dark"] .fe-event-poster-contact h3 { color: #f1f5f9; }
html[data-theme="dark"] .fe-event-poster-ticket-creds code { background: #0b1026; color: #e2e8f0; }

/* ===========================================================================
   Event detail — Timeline template
   Big calendar-style date block on the left, content beside it.
   =========================================================================== */
.fe-event-time {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel)));
  padding: 56px 0 100px;
}
.fe-event-time-inner { max-width: 1000px; margin: 0 auto; padding: 0 24px; }
.fe-page .fe-event-time-back,
.frontend-body .fe-event-time-back {
  display: inline-block; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-decoration: none; font-size: 0.875rem; margin-bottom: 32px;
}
.fe-page .fe-event-time-back:hover,
.frontend-body .fe-event-time-back:hover {
  color: var(--fe-color-brand, var(--fe-accent)); text-decoration: none;
}
.fe-event-time-row { display: grid; grid-template-columns: 180px 1fr; gap: 56px; align-items: start; }
@media (max-width: 800px) { .fe-event-time-row { grid-template-columns: 1fr; gap: 24px; } }
.fe-event-time-date {
  position: sticky; top: calc(var(--fe-header-full-h, 80px) + 24px);
  display: flex; flex-direction: column;
  border-left: 4px solid var(--fe-color-brand, var(--fe-accent));
  padding: 8px 0 8px 22px;
}
@media (max-width: 800px) {
  .fe-event-time-date { position: static; flex-direction: row; gap: 12px; align-items: baseline; padding-left: 16px; }
}
.fe-event-time-mon {
  font-size: 0.875rem; font-weight: 700; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--fe-color-brand, var(--fe-accent));
  margin-bottom: 4px;
}
.fe-event-time-day {
  font-family: var(--fe-font-display, 'Fraunces'), Georgia, serif;
  font-size: clamp(4rem, 9vw, 6.5rem); line-height: 0.95; font-weight: 600;
  color: var(--fe-color-text, var(--fe-ink)); letter-spacing: -0.04em;
}
.fe-event-time-dow {
  font-size: 1rem; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  margin-top: 6px; font-weight: 500;
}
.fe-event-time-yr {
  font-size: 0.875rem; color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-event-time-main { min-width: 0; }
.fe-event-time-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.fe-event-time-chips span {
  padding: 4px 10px; border-radius: 6px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  border: 1px solid var(--fe-color-border, var(--fe-border));
}
.fe-event-time-chips span.online {
  background: color-mix(in srgb, var(--fe-color-brand, var(--fe-accent)) 12%, transparent);
  color: var(--fe-color-brand, var(--fe-accent)); border-color: transparent;
}
.fe-event-time-chips span.archived {
  background: #e2e8f0; color: #334155; border-color: transparent;
}
.fe-event-time-main h1 {
  margin: 0 0 14px;
  font-family: var(--tpl-heading-font, var(--fe-font-display, 'Fraunces'), Georgia, serif);
  font-size: var(--tpl-heading-size, clamp(2rem, 4.5vw, 3rem));
  line-height: 1.1; font-weight: 600;
  color: var(--fe-color-text, var(--fe-ink)); letter-spacing: -0.02em;
}
.fe-event-time-summary {
  margin: 0 0 20px; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  font-size: 1.0625rem; line-height: 1.55;
}
.fe-event-time-cover {
  margin: 0 0 28px; border-radius: var(--fe-card-radius, 14px); overflow: hidden;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
}
.fe-event-time-cover img { display: block; width: 100%; height: auto; }
.fe-event-time-meta {
  list-style: none; padding: 18px 0; margin: 0 0 24px;
  display: flex; flex-direction: column; gap: 10px;
  border-top: 1px solid var(--fe-color-border, var(--fe-border));
  border-bottom: 1px solid var(--fe-color-border, var(--fe-border));
}
.fe-event-time-meta li {
  display: flex; gap: 14px; align-items: baseline;
  color: var(--fe-color-text, var(--fe-ink));
  font-size: 0.9375rem; line-height: 1.5;
}
.fe-event-time-meta-icon { color: var(--fe-color-brand, var(--fe-accent)); flex: 0 0 auto; }
.fe-event-time-meta-icon .icon { width: 18px; height: 18px; }
.fe-page .fe-event-time-link,
.frontend-body .fe-event-time-link {
  color: var(--fe-color-link, var(--fe-color-brand, var(--fe-accent)));
  font-weight: 500;
}
.fe-event-time-link-strong { font-weight: 700; }
.fe-event-time-creds {
  margin: 0 0 28px; display: grid; grid-template-columns: 110px 1fr;
  gap: 6px 14px; align-items: center;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  padding: 14px 18px; border-radius: var(--fe-card-radius, 10px);
}
.fe-event-time-creds dt {
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
}
.fe-event-time-creds dd { margin: 0; display: flex; align-items: center; gap: 6px; }
.fe-event-time-creds code {
  background: var(--fe-color-surface, var(--fe-panel));
  padding: 3px 8px; border-radius: 4px;
  font-size: 0.875rem; color: var(--fe-color-text, var(--fe-ink));
  border: 1px solid var(--fe-color-border, var(--fe-border));
}
.fe-event-time-prose {
  color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, var(--fe-text-size-base, 1rem));
  line-height: var(--fe-text-line-height, 1.75);
}
.fe-event-time-prose p { margin: 0 0 16px; }
.fe-event-time-contact {
  margin-top: 28px;
  border-top: 1px solid var(--fe-color-border, var(--fe-border));
  padding-top: 18px;
}
.fe-event-time-contact h3 {
  margin: 0 0 10px; font-size: 0.85rem;
  color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-transform: uppercase; letter-spacing: 0.14em;
}
.fe-event-time-contact ul {
  list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 4px;
  color: var(--fe-color-text, var(--fe-ink));
}
html[data-theme="dark"] .fe-event-time { background: #0b1026; }
html[data-theme="dark"] .fe-event-time-day,
html[data-theme="dark"] .fe-event-time-main h1,
html[data-theme="dark"] .fe-event-time-meta li,
html[data-theme="dark"] .fe-event-time-prose,
html[data-theme="dark"] .fe-event-time-contact ul { color: #f1f5f9; }
html[data-theme="dark"] .fe-event-time-creds { background: #131a33; }
html[data-theme="dark"] .fe-event-time-creds code {
  background: #0b1026; color: #e2e8f0; border-color: rgba(255,255,255,0.08);
}

/* ===========================================================================
   Event detail — Minimal template
   Centered, no images, no chips. Typography-driven.
   =========================================================================== */
.fe-event-min {
  background: var(--tpl-bg, var(--fe-color-surface, var(--fe-panel)));
  padding: 80px 0 120px;
}
.fe-event-min-inner {
  max-width: 640px; margin: 0 auto; padding: 0 24px; text-align: center;
}
.fe-page .fe-event-min-back,
.frontend-body .fe-event-min-back {
  display: inline-block; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  text-decoration: none; font-size: 0.8125rem; letter-spacing: 0.04em;
  margin-bottom: 60px;
}
.fe-page .fe-event-min-back:hover,
.frontend-body .fe-event-min-back:hover {
  color: var(--fe-color-brand, var(--fe-accent)); text-decoration: none;
}
.fe-event-min-date {
  margin: 0 0 22px; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  font-size: 0.875rem; letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
}
.fe-event-min-date .dot { color: var(--fe-color-border, var(--fe-border)); margin: 0 6px; }
.fe-event-min-head { margin-bottom: 36px; }
.fe-event-min-head h1 {
  margin: 0;
  font-family: var(--tpl-heading-font, var(--fe-font-display, 'Fraunces'), Georgia, serif);
  font-size: var(--tpl-heading-size, clamp(2.25rem, 6vw, 3.75rem));
  line-height: 1.05; font-weight: 400;
  color: var(--fe-color-text, var(--fe-ink)); letter-spacing: -0.025em;
}
.fe-event-min-summary {
  margin: 22px 0 0; color: var(--fe-color-text-soft, var(--fe-ink-muted));
  font-size: 1.0625rem; line-height: 1.55;
}
.fe-event-min-prose {
  color: var(--fe-color-text, var(--fe-ink));
  font-family: var(--tpl-body-font, var(--fe-font-body, inherit));
  font-size: var(--tpl-body-size, var(--fe-text-size-base, 1rem));
  line-height: var(--fe-text-line-height, 1.75);
  text-align: left; margin: 0 0 60px;
}
.fe-event-min-prose p { margin: 0 0 16px; }
.fe-event-min-info {
  margin: 0; display: grid; grid-template-columns: 90px 1fr;
  gap: 22px 28px; align-items: baseline; text-align: left;
}
.fe-event-min-info dt {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fe-color-text-soft, var(--fe-ink-muted)); padding-top: 4px;
}
.fe-event-min-info dd {
  margin: 0; color: var(--fe-color-text, var(--fe-ink));
  font-size: 1rem; line-height: 1.6;
}
.fe-page .fe-event-min-link,
.frontend-body .fe-event-min-link {
  color: var(--fe-color-link, var(--fe-color-brand, var(--fe-accent)));
  font-weight: 500; margin-left: 8px;
}
.fe-event-min-link-strong {
  font-weight: 700; margin-left: 0; display: inline-block; margin-bottom: 8px;
}
.fe-event-min-cred {
  margin-top: 6px; display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem;
}
.fe-event-min-cred code {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft));
  padding: 4px 8px; border-radius: 4px;
  font-size: 0.875rem; color: var(--fe-color-text, var(--fe-ink));
}
@media (max-width: 600px) {
  .fe-event-min-info { grid-template-columns: 1fr; gap: 6px 0; }
  .fe-event-min-info dt { padding-top: 16px; }
  .fe-event-min-info dt:first-child { padding-top: 0; }
}
html[data-theme="dark"] .fe-event-min { background: #0b1026; }
html[data-theme="dark"] .fe-event-min-head h1,
html[data-theme="dark"] .fe-event-min-prose,
html[data-theme="dark"] .fe-event-min-info dd { color: #f1f5f9; }
html[data-theme="dark"] .fe-event-min-cred code { background: #131a33; color: #e2e8f0; }

/* Per-meeting extended-content blocks — admin-tunable Markdown panes
   that render below the schedule / zoom / files cards on every
   meeting detail template. Each block fills the meeting-detail
   container's full width and stacks vertically; cards share the same
   padding + radius + border treatment as the meeting-detail cards
   above so they read as peer surfaces. */
.fe-meeting-extended {
  margin: 32px 0 0;
  display: flex; flex-direction: column;
  gap: 16px;
}
.fe-meeting-extended-card {
  /* `box-sizing: border-box` so padding + border are counted inside the
     card's stretched width — without it the card overflows by ~54px
     and the flex parent constrains the content into a narrow column.
     The default `align-items: stretch` on the flex parent already
     fills cross-axis width, so no explicit `width: 100%` needed.
     Border + hover treatment matches the homepage `.fe-meeting-card`:
     solid accent border at rest, 2 px lift with a soft indigo drop
     shadow on hover, 200 ms ease — so every meeting-shaped card
     across the public site shares one hover language. */
  box-sizing: border-box;
  display: block;
  padding: 2rem;
  background: var(--fe-color-card-primary-bg, var(--fe-color-surface, #ffffff));
  border: var(--fe-card-primary-border-width, 1px) solid var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease;
}
.fe-meeting-extended-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.10);
}
@media (max-width: 600px) {
  /* Tighten the inner gutter on phones so the card doesn't lose half
     the readable width to padding. */
  .fe-meeting-extended-card { padding: 1.25rem; }
}
.fe-meeting-extended-title {
  margin: 0 0 12px;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.25rem; line-height: 1.25;
  font-weight: 600;
}
.fe-meeting-extended-body { line-height: 1.6; }
.fe-meeting-extended-body > *:first-child { margin-top: 0; }
.fe-meeting-extended-body > *:last-child { margin-bottom: 0; }
.fe-meeting-extended-body p { margin: 0.6em 0; }
.fe-meeting-extended-body ul,
.fe-meeting-extended-body ol { margin: 0.6em 0; padding-left: 1.4em; }
.fe-meeting-extended-body h2,
.fe-meeting-extended-body h3,
.fe-meeting-extended-body h4 { margin: 1.2em 0 0.4em; line-height: 1.3; }
.fe-meeting-extended-body code {
  padding: 1px 6px; border-radius: 4px;
  background: rgba(15, 23, 42, 0.06); font-size: 0.92em;
}
.fe-meeting-extended-body pre {
  padding: 12px 14px; border-radius: 6px;
  background: rgba(15, 23, 42, 0.06); overflow: auto;
}
html[data-theme="dark"] .fe-meeting-extended-card,
body.fe-frontend-force-dark .fe-meeting-extended-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, transparent);
}
/* Dark-mode hover border matches the meetings-list cards + Pro Tips
   accordion — the indigo tint lights up the edge on hover so the
   lift + shadow have a colour cue to land against. */
html[data-theme="dark"] .fe-meeting-extended-card:hover,
body.fe-frontend-force-dark .fe-meeting-extended-card:hover {
  border-color: rgba(122, 163, 255, 0.4);
}
html[data-theme="dark"] .fe-meeting-extended-body code,
html[data-theme="dark"] .fe-meeting-extended-body pre {
  background: rgba(255, 255, 255, 0.06);
}

/* ===== Edit shortcut bar — frontend meeting + event detail pages =====
   Wrapper that places the existing "Back to …" anchor on the left and
   the (gated) "Edit" button on the right. Only logged-in users with
   edit privileges see the button; everyone else just sees the back
   anchor in its original spot. The bar is unstyled padding-wise so
   each template's existing back-anchor positioning still applies. */
.fe-edit-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  /* Inherit the back-link's natural margin so the bar takes the spot
     the lone link used to occupy in each template. */
}
/* Wrapper around the adaptive back link(s). When the post is both an
   event and an announcement, two links render — stack them so they
   read as a small navigation block instead of a row of links. */
.fe-event-detail-back-stack {
  display: flex; flex-direction: column; gap: 4px;
  align-items: flex-start;
  margin-bottom: 24px;
}
.fe-edit-shortcut {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 14px; border-radius: 999px;
  font-size: 0.85rem; font-weight: 600; line-height: 1;
  text-decoration: none; color: inherit;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent, #3b82f6) 38%, transparent);
  transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.fe-edit-shortcut .icon { width: 14px; height: 14px; }
.fe-edit-shortcut:hover,
.fe-edit-shortcut:focus-visible {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 22%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 60%, transparent);
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-accent)));
  text-decoration: none;
}
/* On the Poster event template the bar sits on a darkened hero image,
   so colours are inverted to keep the chip legible against varying
   photo backgrounds. */
.fe-edit-bar-on-dark .fe-edit-shortcut {
  color: #fff;
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.32);
}
.fe-edit-bar-on-dark .fe-edit-shortcut:hover,
.fe-edit-bar-on-dark .fe-edit-shortcut:focus-visible {
  background: rgba(255, 255, 255, 0.26);
  border-color: rgba(255, 255, 255, 0.5);
  color: #fff;
}
html[data-theme="dark"] .fe-edit-shortcut:hover,
html[data-theme="dark"] .fe-edit-shortcut:focus-visible {
  color: var(--fe-color-link-hover, #93c5fd);
}

/* ===== Meetings list (/meetings) — shared card =====
   Three layouts (sidebar, directory, weekboard) all wrap the same
   _meeting_card.html partial and tweak its envelope. The base card
   defines the grid that places logo + chips + name + schedules + rows
   + actions in a balanced column. .is-dense compresses for the dense
   directory rows + week-board tiles. */
.fe-mlist {
  padding: 32px 0 64px;
  /* Page background sits in the soft panel tone; the cards and rail
     pop forward in pure white so the directory reads as a stack of
     "papers" on a light desk. Dark theme inverts via the rules at
     the bottom of this section. */
  background: var(--fe-panel-soft, #f1f5f9);
}

/* Floor list-page sections at 50vh so a sparsely-populated list still
   leaves visitors a page-shaped surface — without this, an empty
   archive or one-day meeting list collapses to a thin strip beneath
   the header. Applies to every public list/section template. */
.fe-mlist,
.fe-events-list-omni,
.fe-events-list-cards,
.fe-events-list-calendar,
.fe-events-list-timeline,
.fe-events-list-magazine,
.fe-announcements-list {
  min-height: 50vh;
}

/* "Pro Tips" accordion below the meetings list — reuses the FAQ block
   chrome (.fe-faq-*) but adds a section-header icon, a centered
   heading, and an opt-in custom background colour set via inline
   style. The .has-custom-bg modifier only adjusts the icon-chip
   contrast — it doesn't touch the FAQ rows themselves so admin-chosen
   backgrounds stay unaltered behind the items. */
.fe-protips {
  margin: 0;
  padding: 56px 0;
}
.fe-protips-head {
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.fe-protips-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px;
  margin-bottom: 14px;
  border-radius: 16px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 12%, transparent);
  color: var(--fe-accent, #3b82f6);
}
.fe-protips-icon .icon { width: 28px; height: 28px; }
.fe-protips-head h2 { margin: 0; }
.fe-protips-head p { margin: 8px 0 0; max-width: 60ch; }
/* Desktop — split the accordion into two even columns so 8 tips read
   as a 2×4 grid instead of a tall single stack. The base FAQ list is
   `display: flex; flex-direction: column`, which we override here
   only inside .fe-protips. CSS Grid auto-flow keeps the items in
   document order across the columns (left-to-right, top-to-bottom). */
@media (min-width: 720px) {
  .fe-protips .fe-faq-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px 20px;
    align-items: start;
  }
}

/* Sidebar layout's inline Pro Tips section mirrors the filter grid
   above it (240 px rail | fluid main) so the header column lines up
   with the day-filter rail, and the accordion column lines up with
   the meeting cards. The accordion list stays single-column so each
   item spans the full main-column width — matching the meeting
   cards in the column above for visual continuity. */
.fe-mlist-protips-inline {
  margin-top: 48px;
  padding: 24px 0;
}
.fe-mlist-grid-protips { align-items: start; }
.fe-mlist-protips-head {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 6px;
  position: sticky; top: 96px;
}
.fe-mlist-protips-head .fe-protips-icon {
  margin-bottom: 8px;
}
.fe-mlist-protips-head h2 {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: clamp(1.4rem, 1.2rem + 0.4vw, 1.7rem);
  line-height: 1.2;
  word-break: break-word;
}
.fe-mlist-protips-head p {
  margin: 0;
  font-size: 0.875rem; line-height: 1.4;
}
/* Sidebar-layout Pro Tips list: 2-up grid that fills the main column.
   Drop the `max-width: 760px` cap and centring from the base
   `.fe-faq-list` rule so the grid spans the full main track and
   aligns flush with the meeting cards above. Below 720 px the
   columns collapse to one so each tip stays readable on phones. */
.fe-mlist-protips-inline .fe-faq-list {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 20px;
  max-width: none;
  margin: 0;
  align-items: start;
}
@media (max-width: 720px) {
  .fe-mlist-protips-inline .fe-faq-list {
    grid-template-columns: 1fr;
  }
}
/* Accordion-item face matches the meeting cards above — white surface
   + 1 px accent border + same border-radius — so the Pro Tips block
   reads as a peer surface to the directory cards. The base FAQ item
   uses panel-soft + border-token, both overridden here. */
/* Pro Tips cards opt into the Primary-card recipe via the
   `fe-card-primary` class stamped on each `.fe-faq-item` in the
   Pro Tips templates, so background / border / border-radius /
   hover lift / shadow / dark-mode swap all come from the shared
   Primary-card tokens + hover aggregator at the bottom of this
   file. The only Pro-Tips-specific tweak left is the "open" state
   border colour, which the aggregator doesn't address. */
.fe-mlist-protips-inline .fe-faq-item.is-open { border-color: var(--fe-accent); }
/* Dark-mode polish for the inline Pro Tips section header — make sure
   the heading reads bright against the dark page surface, the
   subheading uses the muted-on-dark token, and the icon chip's
   surface is visible against #0b1026. */
html[data-theme="dark"] .fe-mlist-protips-head h2,
body.fe-frontend-force-dark .fe-mlist-protips-head h2 {
  color: #f1f5f9;
}
html[data-theme="dark"] .fe-mlist-protips-head p,
body.fe-frontend-force-dark .fe-mlist-protips-head p {
  color: #94a3b8;
}
html[data-theme="dark"] .fe-mlist-protips-inline .fe-protips-icon,
body.fe-frontend-force-dark .fe-mlist-protips-inline .fe-protips-icon {
  background: rgba(122, 163, 255, 0.18);
  color: #7aa3ff;
}
/* Body text inside each accordion's expanded panel — the base dark
   FAQ rule sits at the same specificity as my Pro Tips overrides
   above, so re-state explicitly so the muted body colour wins
   inside the inline section. */
html[data-theme="dark"] .fe-mlist-protips-inline .fe-faq-body-inner,
body.fe-frontend-force-dark .fe-mlist-protips-inline .fe-faq-body-inner {
  color: #cbd5e1;
}
html[data-theme="dark"] .fe-mlist-protips-inline .fe-faq-summary,
body.fe-frontend-force-dark .fe-mlist-protips-inline .fe-faq-summary {
  color: #f1f5f9;
}

@media (max-width: 900px) {
  .fe-mlist-protips-inline { margin-top: 32px; padding: 12px 0; }
  .fe-mlist-protips-head {
    position: static;
    text-align: left;
  }
}

/* When the heading lives inside the sidebar rail column (Sidebar
   layout), scale the typography down so it fits the 240px track
   without clipping or awkward wrapping. The default .fe-mlist-head
   sizes are tuned for a full-width banner above the grid — in the
   narrower rail context they need to be smaller and tighter. */
.fe-mlist-sidebar .fe-mlist-rail > .fe-mlist-head {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  margin: 0 0 18px;
  padding: 0;
}
.fe-mlist-sidebar .fe-mlist-rail > .fe-mlist-head h1 {
  font-size: clamp(1.4rem, 1.2rem + 0.4vw, 1.7rem);
  line-height: 1.2;
  margin: 0 0 6px;
  word-break: break-word;
}
.fe-mlist-sidebar .fe-mlist-rail > .fe-mlist-head p {
  font-size: 0.875rem; line-height: 1.4;
  margin: 0;
}

/* Full-width wrapper — used in place of .fe-container when the admin
   picks the 'full' width mode on the templates page. The N-vw side
   padding is emitted as an inline style by the template; this class
   just zeroes out fe-container's max-width / horizontal padding so
   the wrapper actually spans the viewport. Below 600px the inline
   padding gets clamped to 4vw min so cards don't crash into the
   screen edges on narrow phones. */
.fe-mlist-fullwrap {
  width: 100%; max-width: none;
  margin: 0 auto; padding-left: 0; padding-right: 0;
  box-sizing: border-box;
}
@media (max-width: 600px) {
  .fe-mlist-fullwrap { padding-left: max(4vw, 16px) !important; padding-right: max(4vw, 16px) !important; }
}
.fe-mlist-head { margin-bottom: 28px; }
.fe-mlist-head h1 {
  font-family: var(--fe-font-heading, sans-serif);
  font-size: clamp(1.6rem, 1.2rem + 1.5vw, 2.4rem);
  margin: 0 0 8px;
}
.fe-mlist-head p { margin: 0; max-width: 65ch; }
.fe-mlist-empty,
.fe-mlist-no-match { padding: 40px 0; text-align: center; }

/* JS filter sets [hidden] on cards. .fe-mlist-card's display:flex has
   the same specificity as the UA's [hidden]{display:none} but comes
   later in the cascade, so without this rule hidden cards stay
   visible. Same fix applied to the directory rows + week-board tiles
   since they all share .fe-mlist-card. */
.fe-mlist-card[hidden] { display: none !important; }
.fe-mlist-card {
  /* Surface + border read from the Primary card design tokens. The
     shared primary aggregator near the bottom of this file wires
     shadow at rest + hover shadow + hover transform + hover border
     through the matching Primary tokens, so every meeting-shaped
     surface across the public site (homepage, /meetings list,
     meeting detail grid, Files & Readings panel, extended card)
     shares one rest + motion language driven by Site → Design →
     Card styles → Primary card. */
  position: relative;
  display: flex; flex-direction: column; gap: 12px;
  padding: 20px 22px;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 16px;
}
/* The body wrapper holds head + schedules + rows + actions; in column
   layouts (directory, weekboard) it's a transparent passthrough so
   each child stacks with the card's own 12px gap. The sidebar layout
   re-assigns it to a separate flex column beside the logo. */
.fe-mlist-card-body {
  display: flex; flex-direction: column; gap: 12px;
  flex: 1 1 auto; min-width: 0;
}
.fe-mlist-card:hover {
  /* hover lift / shadow / border come from the shared primary
     aggregator below; this rule is intentionally empty so the
     selector still exists for any future consumers but doesn't
     override the aggregator's token-driven recipe. */
  /* border-color intentionally not overridden — the resting accent
     border stays put on hover so the card feels stable instead of
     visibly recoloring under the cursor. */
}
.fe-mlist-card.has-logo { padding-right: 96px; }
.fe-mlist-card-logo {
  position: absolute; top: 16px; right: 16px;
  width: 64px; height: 64px;
  display: flex; align-items: center; justify-content: center;
}
.fe-mlist-card-logo img {
  max-width: 100%; max-height: 100%;
  object-fit: contain; border-radius: 10px;
}
.fe-mlist-card-head {
  display: flex; flex-direction: column; gap: 6px;
}
.fe-mlist-card-name {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.15rem; line-height: 1.25;
  font-weight: 600;
}
.fe-mlist-card-name a {
  color: inherit; text-decoration: none;
  font-weight: inherit;
}
.fe-mlist-card-name a:hover { text-decoration: none; }

/* Schedule list — pills, one per occurrence (day + time). */
.fe-mlist-card-schedules {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px;
}
.fe-mlist-card-sched {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent, #3b82f6) 22%, transparent);
  font-size: 0.8125rem; line-height: 1.2;
}
.fe-mlist-card-sched-day { font-weight: 600; }
.fe-mlist-card-sched-time { color: color-mix(in srgb, currentColor 75%, transparent); }

.fe-mlist-card-row {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 0.9rem; color: color-mix(in srgb, currentColor 80%, transparent);
}
.fe-mlist-card-row-icon {
  display: inline-flex; flex: 0 0 auto;
  width: 16px; height: 16px;
  margin-top: 2px;
  color: color-mix(in srgb, var(--fe-accent, #3b82f6) 70%, currentColor);
}
.fe-mlist-card-row-icon .icon { width: 16px; height: 16px; }
.fe-mlist-card-row-text { word-break: break-word; }
.fe-mlist-card-row-text code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.8125rem; padding: 0 4px;
  background: rgba(15, 23, 42, 0.06); border-radius: 4px;
}

.fe-mlist-card-desc {
  margin: 0; font-size: 0.92rem; line-height: 1.5;
}
.fe-mlist-card-actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; margin-top: auto;
}
.fe-mlist-card-actions .fe-btn {
  padding: 7px 14px; font-size: 0.85rem; border-radius: 999px;
}
.fe-mlist-card-permalink {
  margin-left: auto;
  font-size: 0.825rem; font-weight: 500;
  color: color-mix(in srgb, currentColor 65%, transparent);
  text-decoration: none;
}
.fe-mlist-card-permalink:hover {
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-accent)));
}

/* Dense card variant — narrower padding, tighter typography for
   directory rows + weekboard column tiles. */
.fe-mlist-card.is-dense { padding: 14px 16px; gap: 8px; }
.fe-mlist-card.is-dense.has-logo { padding-right: 76px; }
.fe-mlist-card.is-dense .fe-mlist-card-logo { width: 48px; height: 48px; top: 12px; right: 12px; }
.fe-mlist-card.is-dense .fe-mlist-card-name { font-size: 1.02rem; }
.fe-mlist-card.is-dense .fe-mlist-card-schedules { gap: 4px; }
.fe-mlist-card.is-dense .fe-mlist-card-sched { padding: 2px 8px; font-size: 0.78rem; }
.fe-mlist-card.is-dense .fe-mlist-card-row { font-size: 0.825rem; }
.fe-mlist-card.is-dense .fe-mlist-card-actions .fe-btn {
  padding: 5px 11px; font-size: 0.78rem;
}

/* ===== Sidebar layout =====
   Two-column shell: 240px sticky rail on the left, fluid card grid on
   the right. Below 900px the rail collapses into a horizontal scroll
   strip pinned to the top of the section. */
.fe-mlist-sidebar .fe-mlist-shell { display: block; }
.fe-mlist-sidebar .fe-mlist-grid {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 32px;
  align-items: start;
}
.fe-mlist-rail-inner {
  /* Sticky position only — no card chrome. The rail sits flush against
     the page background (the .fe-mlist surface) so the day pills read
     as inline navigation, not a contained widget. Padding is dropped
     too so the pills align with the page's left edge. */
  position: sticky; top: 96px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
}
/* Live search input above the day pills in the sidebar rail.
   Reuses the directory layout's `.fe-mlist-search` chrome (icon +
   border + focus tint) but adds a small `kbd` chip on the right
   showing the keyboard shortcut, and tightens the padding so the
   input fits cleanly above the day list. */
.fe-mlist-rail-search {
  margin-bottom: 12px;
  padding: 0;
  background: transparent;
  border: 0;
  position: relative;
}
.fe-mlist-rail-search input {
  /* Reset the directory toolbar variant's full-width look + add
     space on the right for the kbd chip. */
  width: 100%;
  padding: 9px 44px 9px 38px;
  background: rgba(15, 23, 42, 0.04);
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  color: inherit;
  box-sizing: border-box;
}
.fe-mlist-rail-search input::placeholder {
  color: color-mix(in srgb, currentColor 50%, transparent);
}
.fe-mlist-rail-search input:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.92);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 50%, transparent);
}
.fe-mlist-rail-search .fe-mlist-search-icon {
  position: absolute;
  left: 12px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px;
  color: color-mix(in srgb, currentColor 50%, transparent);
  pointer-events: none;
}
.fe-mlist-rail-search .fe-mlist-search-icon .icon { width: 18px; height: 18px; }
.fe-mlist-search-kbd {
  position: absolute;
  right: 10px; top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px;
  padding: 0 6px; border-radius: 6px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.78rem; font-weight: 600;
  background: rgba(15, 23, 42, 0.08);
  color: color-mix(in srgb, currentColor 60%, transparent);
  pointer-events: none;
}
.fe-mlist-rail-search input:focus ~ .fe-mlist-search-kbd {
  opacity: 0;
}
html[data-theme="dark"] .fe-mlist-rail-search input,
body.fe-frontend-force-dark .fe-mlist-rail-search input {
  background: rgba(255, 255, 255, 0.06);
  color: #f1f5f9;
}
html[data-theme="dark"] .fe-mlist-rail-search input:focus,
body.fe-frontend-force-dark .fe-mlist-rail-search input:focus {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(122, 163, 255, 0.55);
}
html[data-theme="dark"] .fe-mlist-search-kbd,
body.fe-frontend-force-dark .fe-mlist-search-kbd {
  background: rgba(255, 255, 255, 0.10);
}
@media (max-width: 900px) {
  .fe-mlist-rail-search { margin-bottom: 10px; }
}

.fe-mlist-day-list { list-style: none; margin: 0; padding: 0; }
.fe-mlist-day-list > li + li { margin-top: 4px; }
/* "Filter by day" section label — sits as the 2nd item in the list,
   styled like an eyebrow heading rather than a button. The divider
   line is its top border so "All meetings" anchors the top of the
   rail and the day pills group beneath the label. */
.fe-mlist-rail-section {
  margin-top: 14px;
  padding: 12px 12px 4px;
  border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 55%, transparent);
  pointer-events: none;
}
.fe-mlist-day-pill {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 9px 12px;
  background: transparent; border: 1px solid transparent; border-radius: 10px;
  color: inherit; font-size: 0.92rem; font-weight: 500;
  cursor: pointer; transition: background 120ms ease, border-color 120ms ease;
}
.fe-mlist-day-pill:hover {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 7%, transparent);
}
.fe-mlist-day-pill.is-active {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 14%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 32%, transparent);
}
.fe-mlist-day-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem; font-weight: 600;
  padding: 1px 8px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 8%, transparent);
}
.fe-mlist-day-pill.is-active .fe-mlist-day-count {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 28%, transparent);
}

/* Admin-curated rail links — sit BELOW the day-pill list under a
   divider line. Each row is a flush left → right layout with the
   trailing icon (chevron-right for internal, external-link for
   external) sitting against the right edge. Same hover language as
   the day pills so the rail reads as one coherent column. */
.fe-mlist-link-list {
  list-style: none; margin: 14px 0 0; padding: 12px 0 0;
  border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}
.fe-mlist-link-list > li + li { margin-top: 4px; }
/* Selectors prefixed with `.fe-page` / `.frontend-body` so the rest
   AND hover rules win over the global `.fe-page a` + `.fe-page a:hover`
   block (specificity 0,2,1) — without the prefix our rules are 0,2,0
   and lose, so the link picks up the body link colour at rest and a
   colour-shift + underline on hover. */
.fe-page .fe-mlist-rail-link,
.frontend-body .fe-mlist-rail-link {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 9px 12px;
  border: 1px solid transparent; border-radius: 10px;
  color: inherit; text-decoration: none;
  font-size: 0.92rem; font-weight: 500;
  transition: background 120ms ease, border-color 120ms ease;
}
.fe-page .fe-mlist-rail-link:hover,
.fe-page .fe-mlist-rail-link:focus,
.fe-page .fe-mlist-rail-link:focus-visible,
.frontend-body .fe-mlist-rail-link:hover,
.frontend-body .fe-mlist-rail-link:focus,
.frontend-body .fe-mlist-rail-link:focus-visible {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 7%, transparent);
  color: inherit;
  text-decoration: none;
}
.fe-mlist-rail-link-label {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fe-mlist-rail-link-icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  color: color-mix(in srgb, currentColor 55%, transparent);
  transition: color 120ms ease;
}
.fe-mlist-rail-link-icon .icon { width: 16px; height: 16px; }
.fe-mlist-rail-link:hover .fe-mlist-rail-link-icon {
  color: inherit;
}

.fe-mlist-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 16px;
}

/* Sidebar layout — meetings group under day-of-week headings, sorted
   chronologically within each day. Each card is rendered in 3-column
   mode: logo / info / actions. Picking a single day from the rail
   hides the other day sections. Below 900px the 3 columns stack
   vertically so each panel still gets full width on phones. */
.fe-mlist-day-section + .fe-mlist-day-section { margin-top: 32px; }
.fe-mlist-day-section[hidden] { display: none !important; }

/* Single-day mode — when the rail is filtered to one day, the JS adds
   .is-single-day on the results column. Drop the day heading + its
   trailing margin so cards line up flush with the top of the rail.
   The active day pill in the sidebar already tells the visitor what
   they're looking at. */
.fe-mlist-main.is-single-day .fe-mlist-day-section-heading { display: none; }
.fe-mlist-main.is-single-day .fe-mlist-day-section { margin-top: 0; }

/* Entrance animation — when the rail filter changes, the JS adds
   .is-entering on every now-visible card with a per-card stagger
   exposed via --fe-card-stagger. Cards slide in from the right, one
   after another, settling into place. The class is stripped on
   animationend so the card sits at its rest state (no permanent
   transform, no layout shift) and the next click can replay it. */
@keyframes fe-mlist-card-in {
  from { opacity: 0; transform: translate3d(60px, 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
.fe-mlist-card.is-entering,
.fe-library-item.is-entering,
.fe-events-archive-card-wrap.is-entering,
.fe-events-card.is-entering,
.fe-announcements-card.is-entering {
  animation: fe-mlist-card-in 720ms cubic-bezier(.18,.74,.3,1) both;
  animation-delay: var(--fe-card-stagger, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .fe-mlist-card.is-entering,
  .fe-library-item.is-entering,
  .fe-events-archive-card-wrap.is-entering,
  .fe-events-card.is-entering,
  .fe-announcements-card.is-entering { animation: none; }
}
.fe-mlist-day-section-heading {
  display: flex; align-items: baseline; gap: 14px;
  margin: 0 0 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.4rem; font-weight: 600;
}
.fe-mlist-day-section-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem; font-weight: 600;
  padding: 2px 10px; border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 16%, transparent);
  color: color-mix(in srgb, var(--fe-accent, #3b82f6) 95%, currentColor);
}
.fe-mlist-day-section-empty { margin: 0; padding: 12px 0; }

/* 3-column card (.fe-mlist-card.is-3col) — only used by the day-grouped
   sidebar layout. Logo column is fixed at 180px (wraps to full-width
   stack on mobile); info column grows to fill; actions column has a
   minimum width so credentials + buttons stay readable. */
.fe-mlist-card.is-3col {
  display: grid;
  grid-template-columns: 180px minmax(0, 1fr) minmax(220px, 290px);
  gap: 28px;
  align-items: start;
  padding: 24px 28px;
  flex-direction: initial; /* override the base flex/column declaration */
  /* Cap reading width — past ~1180px the info column starts to feel
     too wide and the actions column drifts far from the eye. */
  max-width: 1180px;
}
.fe-mlist-card.is-3col.has-logo,
.fe-mlist-card.is-3col:not(.has-logo) { padding-right: 28px; }

.fe-mlist-card-col {
  min-width: 0;
  display: flex; flex-direction: column;
  gap: 12px;
}

/* ── Column 1 — Logo */
.fe-mlist-card-col-logo {
  width: 180px; height: 180px;
  align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 6%, transparent);
  border-radius: 14px;
  padding: 12px;
}
.fe-mlist-card-col-logo img {
  width: 100%; height: 100%;
  object-fit: contain;
  border-radius: 10px;
}
/* Soft accent gradient for the placeholder when no logo uploaded — keeps
   the column the same size + texture so heights stay consistent. */
.fe-mlist-card.is-3col:not(.has-logo) .fe-mlist-card-col-logo {
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--fe-accent, #3b82f6) 18%, transparent),
    color-mix(in srgb, var(--fe-accent, #3b82f6) 6%, transparent));
}

/* ── Column 2 — info */
.fe-mlist-card.is-3col .fe-mlist-card-col-info { gap: 10px; }
.fe-mlist-card.is-3col .fe-mlist-card-name {
  margin: 2px 0 6px;
  font-size: 1.6rem; line-height: 1.2;
  font-weight: 600;
}

/* Times grid — Starts / Ends / Opens stacked vertically with a tiny
   label above each value. The duration chip floats inline at the end
   of the row, aligned with the time values. */
.fe-mlist-card-times {
  display: flex; flex-wrap: wrap;
  gap: 18px 28px;
  margin: 0;
}
.fe-mlist-card-times-cell { display: flex; flex-direction: column; gap: 2px; }
.fe-mlist-card-times-cell dt {
  font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 55%, transparent);
}
.fe-mlist-card-times-cell dd {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.05rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.fe-mlist-card-times-cell-dur { justify-content: flex-end; }
.fe-mlist-card-dur-chip {
  display: inline-flex; align-items: center;
  padding: 4px 10px;
  font-size: 0.78rem; font-weight: 600;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent, #3b82f6) 32%, transparent);
}

.fe-mlist-card-also { margin: 0; }
.fe-mlist-card-also-day { white-space: nowrap; }

.fe-mlist-card.is-3col .fe-mlist-card-desc {
  margin: 0; font-size: 0.95rem; line-height: 1.55;
}

/* ── Column 3 — actions */
.fe-mlist-card.is-3col .fe-mlist-card-col-actions {
  gap: 14px;
  padding-left: 22px;
  border-left: 1px solid color-mix(in srgb, currentColor 10%, transparent);
  /* Override the grid's `align-items: start` for this one column so it
     stretches to the row's full height — which makes the divider line
     run from the card's top padding all the way to the bottom padding
     instead of stopping at the actions content height. */
  align-self: stretch;
}
/* Location pane at the top of the actions column (in-person + hybrid).
   Plain text stack — no icon — so the name + address read as
   straightforward where-to-go info. */
.fe-mlist-card-loc {
  display: flex; flex-direction: column; gap: 2px;
  margin: 0;
  min-width: 0;
}
.fe-mlist-card-loc-name {
  font-weight: 600; font-size: 0.95rem; color: inherit;
  word-wrap: break-word;
}
.fe-mlist-card-loc-line {
  font-size: 0.85rem;
  color: color-mix(in srgb, currentColor 65%, transparent);
}

.fe-mlist-card-zoom-creds {
  margin: 0;
  display: flex; flex-direction: column; gap: 8px;
}
.fe-mlist-card-zoom-creds > div { display: flex; flex-direction: column; gap: 2px; }
.fe-mlist-card-zoom-creds dt {
  font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 55%, transparent);
}
.fe-mlist-card-zoom-creds dd { margin: 0; }
.fe-mlist-card-zoom-creds code {
  display: inline-block;
  padding: 4px 10px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.92rem;
  background: rgba(15, 23, 42, 0.06);
  border-radius: 6px;
  letter-spacing: 0.02em;
}

/* Click-to-copy chip — wraps the credential <code> + a copy icon in a
   single button. Resets the default <button> styling so the chip
   looks like a regular cred line, then nudges the surface on hover
   to advertise the click target. The .is-copied state (added by
   _copy_script.html for ~1.4s after a successful clipboard write)
   flashes the chip green. */
.fe-copy-btn.fe-mlist-copy {
  /* Reset every property the base .fe-copy-btn (28×28 icon button) bakes
     in — this chip is text-shaped, not a square button. */
  position: relative;
  display: inline-flex; align-items: center; gap: 8px;
  width: auto; height: auto;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font: inherit; color: inherit;
  transition: transform 120ms ease;
}
/* "Copied!" confirmation tooltip — rendered as a ::after pseudo on the
   chip when the JS adds .is-copied. Floats above the chip, fades in
   then out over the same 1.4 s window the chip's green flash uses, and
   carries a small downward-pointing arrow rendered via ::before so the
   pill clearly attaches to the button it confirms. pointer-events:
   none so the tooltip never intercepts clicks. */
.fe-copy-btn.fe-mlist-copy.is-copied::after {
  content: "Copied!";
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  padding: 5px 12px;
  border-radius: 8px;
  background: #16a34a;
  color: #ffffff;
  font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 4px 14px rgba(22, 163, 74, 0.32);
  animation: fe-copy-tooltip 1400ms ease-out forwards;
  z-index: 5;
}
.fe-copy-btn.fe-mlist-copy.is-copied::before {
  content: "";
  position: absolute;
  bottom: calc(100% + 4px);
  left: 50%;
  width: 0; height: 0;
  border: 5px solid transparent;
  border-top-color: #16a34a;
  pointer-events: none;
  animation: fe-copy-tooltip-arrow 1400ms ease-out forwards;
  z-index: 5;
}
@keyframes fe-copy-tooltip {
  0%   { opacity: 0; transform: translate(-50%, 4px); }
  12%, 78% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -4px); }
}
@keyframes fe-copy-tooltip-arrow {
  0%   { opacity: 0; transform: translate(-50%, 4px); }
  12%, 78% { opacity: 1; transform: translate(-50%, 0); }
  100% { opacity: 0; transform: translate(-50%, -4px); }
}
@media (prefers-reduced-motion: reduce) {
  .fe-copy-btn.fe-mlist-copy.is-copied::after,
  .fe-copy-btn.fe-mlist-copy.is-copied::before {
    animation: none; opacity: 1;
    transform: translateX(-50%);
  }
}
.fe-copy-btn.fe-mlist-copy code { transition: background 120ms ease; }
.fe-copy-btn.fe-mlist-copy:hover code,
.fe-copy-btn.fe-mlist-copy:focus-visible code {
  background: rgba(15, 23, 42, 0.12);
}
.fe-mlist-copy-icon {
  display: inline-flex;
  width: 14px; height: 14px;
  color: color-mix(in srgb, currentColor 50%, transparent);
  transition: color 120ms ease, transform 120ms ease;
}
.fe-mlist-copy-icon .icon { width: 14px; height: 14px; }
.fe-copy-btn.fe-mlist-copy:hover .fe-mlist-copy-icon {
  color: var(--fe-accent, #3b82f6);
  transform: scale(1.05);
}
.fe-copy-btn.fe-mlist-copy.is-copied {
  /* Base .fe-copy-btn.is-copied paints the whole button green; the chip
     keeps its transparent surface and lets only the inner code +
     icon flash green for a more subtle confirmation. */
  background: transparent;
  color: inherit;
  border-color: transparent;
}
.fe-copy-btn.fe-mlist-copy.is-copied code {
  background: color-mix(in srgb, #16a34a 22%, transparent);
  color: #166534;
}
.fe-copy-btn.fe-mlist-copy.is-copied .fe-mlist-copy-icon { color: #16a34a; }
html[data-theme="dark"] .fe-copy-btn.fe-mlist-copy:hover code,
html[data-theme="dark"] .fe-copy-btn.fe-mlist-copy:focus-visible code {
  background: rgba(255, 255, 255, 0.16);
}
html[data-theme="dark"] .fe-copy-btn.fe-mlist-copy.is-copied code { color: #bbf7d0; }

.fe-mlist-card-action-stack {
  display: flex; flex-direction: column;
  gap: 8px; margin-top: auto;
}
.fe-mlist-card-action-stack .fe-btn {
  width: 100%;
  text-align: center;
  padding: 9px 14px;
  border-radius: 999px;
  font-size: 0.9rem;
}
/* Drop the global drop-shadow + hover lift on the Join Zoom button —
   inside the action column it sits next to a ghost button + a copy
   chip, where the lift makes the panel feel jumpy on hover. */
.fe-mlist-card-action-stack .fe-btn-primary,
.fe-mlist-card-action-stack .fe-btn-primary:hover {
  box-shadow: none;
  transform: none;
}
.fe-mlist-card.is-3col .fe-mlist-card-permalink {
  margin: 4px 0 0;
  text-align: center;
  font-size: 0.85rem; font-weight: 500;
  color: color-mix(in srgb, currentColor 65%, transparent);
  text-decoration: none;
}
.fe-mlist-card.is-3col .fe-mlist-card-permalink:hover {
  color: var(--fe-color-link-hover, var(--fe-color-link, var(--fe-accent)));
}

@media (max-width: 900px) {
  .fe-mlist-card.is-3col {
    grid-template-columns: 1fr;
    gap: 18px; padding: 20px;
  }
  .fe-mlist-card-col-logo {
    width: 100%; height: 200px;
  }
  .fe-mlist-card.is-3col .fe-mlist-card-col-actions {
    padding-left: 0; padding-top: 18px;
    border-left: none;
    border-top: 1px solid color-mix(in srgb, currentColor 10%, transparent);
  }
  .fe-mlist-card.is-3col .fe-mlist-card-name { font-size: 1.4rem; }
}

.fe-mlist-card-stack {
  display: flex; flex-direction: column;
  gap: 16px;
}

@media (max-width: 900px) {
  /* `minmax(0, 1fr)` so the grid column actually constrains its width
     to the viewport — bare `1fr` defaults to `minmax(auto, 1fr)` and
     would let the rail's flex chip row stretch the column past the
     screen edge, dragging the whole page into a horizontal scroll. */
  .fe-mlist-sidebar .fe-mlist-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
  /* Grid items inherit `min-width: auto`. Force it to 0 so the rail
     can shrink to its column width even when its flex children would
     otherwise puff it up. */
  .fe-mlist-rail { min-width: 0; }
  .fe-mlist-rail-inner {
    position: sticky; top: 64px;
    padding: 0;
    /* Defensively clip any horizontal overflow at the panel boundary —
       the inner day-list is the actual scroller, the panel itself
       should never propagate width past its grid cell. */
    min-width: 0;
    overflow-x: hidden;
  }

  /* Swipeable day-pill row. Scroll snap + smooth iOS scrolling +
     hidden scrollbar so the bar reads as a chip strip rather than a
     scroll container. The right-edge fade is a visual hint that
     there's more content past the visible chips. */
  .fe-mlist-day-list {
    display: flex; flex-wrap: nowrap;
    gap: 8px; overflow-x: auto;
    margin: 0; padding: 4px 0;
    min-width: 0;
    scroll-snap-type: x proximity;
    scroll-padding-inline: 4px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;        /* Firefox */
    -ms-overflow-style: none;     /* old Edge */
    /* Soft fade on both edges so the chip strip clearly affords a
       horizontal swipe. Mask is a black gradient, so opaque in the
       middle, transparent at the edges. */
    -webkit-mask-image: linear-gradient(to right,
      transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
    mask-image: linear-gradient(to right,
      transparent 0, #000 18px, #000 calc(100% - 18px), transparent 100%);
  }
  .fe-mlist-day-list::-webkit-scrollbar { display: none; }
  .fe-mlist-day-list > li { flex: 0 0 auto; margin: 0; scroll-snap-align: start; }
  /* Cancel the desktop vertical-stack margin (`li + li { margin-top: 4px }`)
     on the horizontal mobile strip — without this every chip past the
     first sits 4 px lower than the All-meetings + Sunday chips because
     the `+` rule wins specificity over the bare `li` rule above. */
  .fe-mlist-day-list > li + li { margin-top: 0; }
  /* On mobile the rail is a horizontal scroller. The section label
     would clutter the chip row, so hide it and put a vertical
     divider between the "All" chip and the first day chip instead. */
  .fe-mlist-rail-section { display: none; }
  .fe-mlist-day-list > li:nth-child(3) {
    margin: 0 0 0 10px; padding: 0 0 0 10px;
    border-top: none;
    border-left: 1px solid color-mix(in srgb, currentColor 14%, transparent);
  }
  .fe-mlist-day-pill { width: auto; padding: 7px 12px; white-space: nowrap; }
}

/* ===== Directory layout =====
   Sticky search/filter toolbar + single-column dense rows. The rows
   use the dense card variant so even with all info inline they stay
   compact enough that 20+ meetings fit on a screen of scrolling. */
.fe-mlist-toolbar {
  position: sticky; top: 64px; z-index: 5;
  display: flex; flex-direction: column; gap: 10px;
  margin-bottom: 24px; padding: 14px;
  background: var(--fe-bg, #fff);
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  backdrop-filter: blur(6px);
}
.fe-mlist-search {
  position: relative;
  display: flex; align-items: center;
}
.fe-mlist-search-icon {
  position: absolute; left: 14px;
  width: 18px; height: 18px;
  color: color-mix(in srgb, currentColor 50%, transparent);
  pointer-events: none;
}
.fe-mlist-search-icon .icon { width: 18px; height: 18px; }
.fe-mlist-search input {
  width: 100%;
  padding: 10px 14px 10px 42px;
  background: rgba(15, 23, 42, 0.04);
  border: 1px solid transparent; border-radius: 10px;
  font: inherit; color: inherit;
}
.fe-mlist-search input:focus {
  outline: none;
  background: rgba(255, 255, 255, 0.96);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 50%, transparent);
}
.fe-mlist-toolbar-row {
  display: flex; flex-wrap: wrap; gap: 14px 24px;
  align-items: center;
}
.fe-mlist-toolbar-group {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.fe-mlist-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 6px 12px; border-radius: 999px;
  background: rgba(15, 23, 42, 0.05);
  border: 1px solid transparent;
  color: inherit; font-size: 0.825rem; font-weight: 500;
  cursor: pointer; transition: background 120ms ease, border-color 120ms ease;
}
.fe-mlist-chip:hover { background: rgba(15, 23, 42, 0.1); }
.fe-mlist-chip.is-active {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 18%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 42%, transparent);
}
.fe-mlist-chip-count {
  font-size: 0.72rem; font-weight: 600;
  padding: 0 6px; border-radius: 999px;
  background: color-mix(in srgb, currentColor 14%, transparent);
}
.fe-mlist-rows {
  display: flex; flex-direction: column; gap: 10px;
}
/* Force the dense look in directory rows even when the partial caller
   didn't pass `dense=true` (we set it via host context but back this
   up for safety). */
.fe-mlist-directory .fe-mlist-rows .fe-mlist-card { padding: 16px 18px; }
@media (max-width: 600px) {
  .fe-mlist-toolbar { padding: 10px; }
  .fe-mlist-toolbar-row { gap: 8px; }
}

/* ===== Week-board layout =====
   Seven equal columns on desktop. Each column header carries the day
   name + a count chip; the body is a vertical stack of dense cards.
   Below 900px the column row turns into a horizontal swipe deck and
   each column snaps to the viewport edge. The mobile chip bar above
   the deck navigates between days. */
.fe-mlist-weekboard .fe-mlist-week-deck {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 12px;
  align-items: start;
}
.fe-mlist-week-col {
  display: flex; flex-direction: column;
  gap: 10px; padding: 12px;
  background: var(--fe-panel-soft, rgba(255,255,255,0.7));
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  min-height: 220px;
}
.fe-mlist-week-col-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px; padding-bottom: 8px;
  border-bottom: 1px solid rgba(15, 23, 42, 0.08);
}
.fe-mlist-week-col-head h2 {
  margin: 0;
  font-size: 0.95rem; font-weight: 600;
  letter-spacing: 0.02em;
}
.fe-mlist-week-col-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem; font-weight: 600;
  padding: 1px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 16%, transparent);
}
.fe-mlist-week-col-empty { margin: 6px 0 0; }
.fe-mlist-week-col-stack {
  display: flex; flex-direction: column; gap: 8px;
}
.fe-mlist-week-mobilebar {
  display: none;
  gap: 6px; flex-wrap: nowrap;
  margin-bottom: 14px; padding: 8px;
  background: rgba(15, 23, 42, 0.04);
  border-radius: 12px;
  overflow-x: auto;
}
@media (max-width: 900px) {
  .fe-mlist-weekboard .fe-mlist-week-deck {
    display: flex;
    grid-template-columns: none;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 8px;
    margin-right: -16px; padding-right: 16px; /* let the last col clear the gutter */
  }
  .fe-mlist-week-col {
    flex: 0 0 88%;
    scroll-snap-align: start;
    min-height: 0;
  }
  .fe-mlist-week-mobilebar {
    display: flex; position: sticky; top: 64px; z-index: 4;
  }
}

/* ===== Dark theme — meetings list =====
   The page background swaps to a deep slate that mirrors the rest of
   the dark-mode chrome; the cards + rail go translucent-light so they
   still read as elevated panels rather than disappearing into the bg. */
html[data-theme="dark"] .fe-mlist,
body.fe-frontend-force-dark .fe-mlist { background: #0b1026; }
/* Join Zoom button — mirrors the dark-mode treatment used on the
   homepage Upcoming Meetings card so the directory's primary CTA
   reads the same in both places. Deep-navy fill + soft indigo text
   on rest, brighter blue on hover. The subtle drop-shadow only
   appears in dark mode (light mode keeps the flat look). */
html[data-theme="dark"] .fe-mlist-card-action-stack .fe-btn-primary,
body.fe-frontend-force-dark .fe-mlist-card-action-stack .fe-btn-primary {
  background: #1e3a8a; color: #e0e7ff; border-color: transparent;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}
html[data-theme="dark"] .fe-mlist-card-action-stack .fe-btn-primary:hover,
body.fe-frontend-force-dark .fe-mlist-card-action-stack .fe-btn-primary:hover {
  background: #2747a3; color: #ffffff;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
}
/* Duplicate of the earlier `.fe-mlist-card` dark-mode rule kept in
   the cascade because rules further down in this file would
   otherwise win and reintroduce the translucent fill. Tracks the
   Primary-card dark tokens so admin re-tints from Site → Design →
   Card styles → Primary card → Background (dark) land here too. */
html[data-theme="dark"] .fe-mlist-card,
body.fe-frontend-force-dark .fe-mlist-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, #1f2a44);
}
/* Dark-mode hover border — brighten the meeting card's edge to the
   same indigo tint the Pro Tips accordion uses on hover, so the
   directory + Pro Tips share one hover treatment in dark mode. */
html[data-theme="dark"] .fe-mlist-card:hover,
body.fe-frontend-force-dark .fe-mlist-card:hover {
  border-color: rgba(122, 163, 255, 0.4);
}
html[data-theme="dark"] .fe-mlist-toolbar,
html[data-theme="dark"] .fe-mlist-week-col {
  background: rgba(255, 255, 255, 0.04);
  border-color: rgba(255, 255, 255, 0.08);
}
html[data-theme="dark"] .fe-mlist-search input { background: rgba(255, 255, 255, 0.06); }
html[data-theme="dark"] .fe-mlist-chip { background: rgba(255, 255, 255, 0.06); }

/* ===== Frontend-wide search modal =====
   Cmd/Ctrl+K from anywhere opens this modal (or click the search icon
   in the utility bar). Centered command-palette style with a fixed
   backdrop, live results list grouped by kind, and keyboard nav. */
.fe-search-modal {
  position: fixed; inset: 0;
  z-index: 9100;
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh 16px 16px;
  pointer-events: none;
}
.fe-search-modal[hidden] { display: none; }
.fe-search-modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(4px);
  pointer-events: auto;
  animation: fe-search-fade 160ms ease-out;
}
.fe-search-modal-panel {
  position: relative;
  width: min(640px, 100%);
  max-height: calc(100vh - 24vh);
  display: flex; flex-direction: column;
  background: #ffffff;
  color: var(--fe-ink, #111);
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  box-shadow: 0 30px 80px rgba(15, 23, 42, 0.45);
  pointer-events: auto;
  overflow: hidden;
  animation: fe-search-pop 180ms cubic-bezier(.18,.74,.3,1);
}
@keyframes fe-search-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes fe-search-pop {
  from { opacity: 0; transform: translateY(-8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.fe-search-modal-input-row {
  position: relative;
  display: flex; align-items: center;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(15, 23, 42, 0.08);
}
.fe-search-modal-icon {
  display: inline-flex; width: 20px; height: 20px;
  margin-right: 12px;
  color: color-mix(in srgb, currentColor 60%, transparent);
}
.fe-search-modal-icon .icon { width: 20px; height: 20px; }
.fe-search-modal-input {
  flex: 1 1 auto; min-width: 0;
  padding: 0; border: 0; background: transparent;
  font: inherit; font-size: 1.05rem; color: inherit;
  outline: none;
}
.fe-search-modal-input::placeholder { color: color-mix(in srgb, currentColor 50%, transparent); }
.fe-search-modal-kbd {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 2px 8px; border-radius: 6px;
  background: rgba(15, 23, 42, 0.08);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.72rem; font-weight: 600;
  color: color-mix(in srgb, currentColor 60%, transparent);
}
.fe-search-modal-results {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  padding: 6px 6px 4px;
}
.fe-search-modal-hint,
.fe-search-modal-empty {
  margin: 0;
  padding: 18px 16px;
  text-align: center;
}
.fe-search-modal-hint code {
  padding: 1px 6px; border-radius: 4px;
  background: rgba(15, 23, 42, 0.06);
  font-size: 0.85em;
}
.fe-search-modal-group { padding: 4px 0; }
.fe-search-modal-group + .fe-search-modal-group {
  border-top: 1px solid rgba(15, 23, 42, 0.06);
  margin-top: 4px;
}
.fe-search-modal-group-head {
  margin: 6px 12px 4px;
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 55%, transparent);
}
.fe-search-modal-result {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px;
  border-radius: 8px;
  color: inherit;
  text-decoration: none;
  transition: background 100ms ease;
}
.fe-search-modal-result:hover,
.fe-search-modal-result.is-active {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 10%, transparent);
}
.fe-search-modal-result-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  flex: 0 0 auto;
  border-radius: 8px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 12%, transparent);
  color: var(--fe-accent, #3b82f6);
}
.fe-search-modal-result-icon .icon { width: 16px; height: 16px; }
.fe-search-modal-result-text {
  display: flex; flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.fe-search-modal-result-title {
  font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fe-search-modal-result-sub {
  font-size: 0.8125rem;
  color: color-mix(in srgb, currentColor 60%, transparent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fe-search-modal-foot {
  display: flex; align-items: center; justify-content: center;
  gap: 18px;
  padding: 10px 16px;
  border-top: 1px solid rgba(15, 23, 42, 0.08);
  background: rgba(15, 23, 42, 0.03);
}
.fe-search-modal-foot kbd {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px;
  padding: 0 5px; margin: 0 2px;
  border-radius: 4px;
  background: rgba(15, 23, 42, 0.10);
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 0.7rem;
}
body.fe-search-open { overflow: hidden; }

/* Search-trigger button in the utility bar — sized + chromed to
   match `.fe-theme-toggle` so the two singleton singletons read as a
   matched pair regardless of where the admin drags them. Translucent
   white circle on the dark utility bar, brightens on hover with a
   small scale-up. */
.fe-utility-search-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  background: rgba(255, 255, 255, 0.12); color: #fff;
  border: 0; padding: 0; border-radius: 999px;
  cursor: pointer;
  transition: background 120ms ease, transform 120ms ease;
}
.fe-utility-search-btn:hover {
  background: rgba(255, 255, 255, 0.22);
  transform: scale(1.04);
}
.fe-utility-search-btn .icon { width: 16px; height: 16px; }
/* Live-meeting state forces black-on-yellow on the bar; mirror the
   theme toggle's same-mode override so the search button stays
   legible when the bar flips colour. */
.fe-utility-bar.is-live .fe-utility-search-btn { color: #111;
  background: rgba(0, 0, 0, 0.12); }
.fe-utility-bar.is-live .fe-utility-search-btn:hover { background: rgba(0, 0, 0, 0.22); }

/* Dark theme ------------------------------------------------------- */
html[data-theme="dark"] .fe-search-modal-panel,
body.fe-frontend-force-dark .fe-search-modal-panel {
  background: #131a33;
  color: #e2e8f0;
  border-color: rgba(255, 255, 255, 0.08);
}
html[data-theme="dark"] .fe-search-modal-input-row,
body.fe-frontend-force-dark .fe-search-modal-input-row {
  border-bottom-color: rgba(255, 255, 255, 0.08);
}
html[data-theme="dark"] .fe-search-modal-kbd,
html[data-theme="dark"] .fe-search-modal-foot kbd,
body.fe-frontend-force-dark .fe-search-modal-kbd,
body.fe-frontend-force-dark .fe-search-modal-foot kbd {
  background: rgba(255, 255, 255, 0.10);
  color: #cbd5e1;
}
html[data-theme="dark"] .fe-search-modal-hint code,
body.fe-frontend-force-dark .fe-search-modal-hint code {
  background: rgba(255, 255, 255, 0.10);
}
html[data-theme="dark"] .fe-search-modal-foot,
body.fe-frontend-force-dark .fe-search-modal-foot {
  background: rgba(0, 0, 0, 0.20);
  border-top-color: rgba(255, 255, 255, 0.08);
}
html[data-theme="dark"] .fe-search-modal-group + .fe-search-modal-group,
body.fe-frontend-force-dark .fe-search-modal-group + .fe-search-modal-group {
  border-top-color: rgba(255, 255, 255, 0.06);
}
html[data-theme="dark"] .fe-search-modal-result:hover,
html[data-theme="dark"] .fe-search-modal-result.is-active,
body.fe-frontend-force-dark .fe-search-modal-result:hover,
body.fe-frontend-force-dark .fe-search-modal-result.is-active {
  background: rgba(122, 163, 255, 0.16);
}

/* =====================================================================
   Events list (/events) — multi-template layouts
   ===================================================================== */
.fe-events-list-cards,
.fe-events-list-calendar,
.fe-events-list-timeline,
.fe-events-list-magazine,
.fe-events-list-omni,
.fe-events-archive { padding: 32px 0 64px; }

/* ===== /events/archive — sidebar layout for past events =====
   Reuses the .fe-mlist-* sidebar geometry from the meetings sidebar
   layout. The card grid in the main column reuses the shared event
   card. Local rules below cover the bits unique to the archive:
   the "back to upcoming" link in the rail header and the year
   headings in the main column. */
.fe-events-archive .fe-mlist-day-section + .fe-mlist-day-section {
  margin-top: 28px;
}
.fe-events-archive-back { margin: 1rem 0 0; font-size: 0.92rem; }
.fe-events-archive-back a {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--fe-color-link, var(--fe-accent));
  text-decoration: none;
}
.fe-events-archive-back a:hover { text-decoration: underline; }
.fe-events-archive-back .icon { width: 14px; height: 14px; }
.fe-events-archive-card-wrap[hidden] { display: none; }
html[data-theme="dark"] .fe-events-archive-back a,
body.fe-frontend-force-dark .fe-events-archive-back a {
  color: var(--fe-dm-text);
}

/* ===== Literature Library page (/library) =====
   Mirrors the archive's sidebar-rail-with-pills shell. The rules below
   only cover the parts unique to the library page: the per-section
   description and the literature item cards (which are denser than
   the event/announcement cards used in the archive). Same accent
   palette via --fe-accent / fe-color-link so the two pages read
   as siblings. */
.fe-library .fe-mlist-day-section + .fe-mlist-day-section { margin-top: 28px; }
.fe-library-section-desc { margin: 0.25rem 0 0.75rem; line-height: 1.45; }
.fe-library-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.fe-library-item {
  /* Primary card tokens — same recipe as the meetings / events /
     announcements / fellowships cards. Hover wired via the shared
     primary aggregator below. */
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 12px 14px;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 16px;
}
.fe-library-item[hidden] { display: none; }
.fe-library-thumb {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--fe-color-surface-alt, #f4f6fa);
  display: flex; align-items: center; justify-content: center;
}
.fe-library-thumb img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.fe-library-ftype {
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--fe-accent);
  background: color-mix(in srgb, var(--fe-accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent) 25%, transparent);
}
.fe-library-item-body { flex: 1 1 auto; min-width: 0; }
.fe-library-item-title { margin: 0; font-size: 1rem; line-height: 1.35; font-weight: 600; }
.fe-library-item-title a {
  color: var(--fe-color-text, inherit);
  text-decoration: none;
}
.fe-library-item-title a:hover { color: var(--fe-color-link, var(--fe-accent)); text-decoration: underline; }
.fe-library-item-sub { margin: 2px 0 0; }
.fe-library-item-sub.trunc {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.fe-library-item-cats { margin: 6px 0 0; display: flex; flex-wrap: wrap; gap: 4px 6px; }
.fe-library-item-cat {
  display: inline-block;
  padding: 2px 8px;
  font-size: 0.72rem;
  font-weight: 600;
  border-radius: 999px;
  color: var(--fe-accent);
  background: color-mix(in srgb, var(--fe-accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent) 20%, transparent);
}

html[data-theme="dark"] .fe-library-item,
body.fe-frontend-force-dark .fe-library-item {
  background: var(--fe-dm-surface);
  border-color: var(--fe-dm-border);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-library-item:hover,
body.fe-frontend-force-dark .fe-library-item:hover {
  border-color: var(--fe-dm-border-hover, rgba(122, 163, 255, 0.4));
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
}
html[data-theme="dark"] .fe-library-item-title a,
body.fe-frontend-force-dark .fe-library-item-title a { color: var(--fe-dm-text-strong); }
html[data-theme="dark"] .fe-library-item-title a:hover,
body.fe-frontend-force-dark .fe-library-item-title a:hover { color: var(--fe-dm-link-hover); }
html[data-theme="dark"] .fe-library-thumb,
body.fe-frontend-force-dark .fe-library-thumb { background: rgba(255, 255, 255, 0.04); }
html[data-theme="dark"] .fe-library-section-desc,
body.fe-frontend-force-dark .fe-library-section-desc { color: var(--fe-dm-text-muted); }

/* ===== Omni layout — tab switcher above the panels =====
   Segmented-control style centered above the content. Active tab
   has the accent fill; inactive tabs are translucent. Each tab also
   carries a small Lucide icon hinting at the layout.
   `width: fit-content` + `margin: auto` centres the strip as a
   block-level flex container — sized to its content but shrinking
   to fit the viewport on narrow screens. */
.fe-events-omni-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  background: color-mix(in srgb, currentColor 6%, transparent);
  border-radius: 999px;
  max-width: 100%;
}
/* Bar wrapper: tab strip flush left within the inner column, Archive
   pill pushed to the right by an auto-margin. Centered as a unit so
   short rows still feel balanced under the centered page heading. */
.fe-events-omni-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 8px auto 28px;
  width: fit-content;
  max-width: 100%;
}
.fe-events-omni-bar .fe-events-omni-archive { margin-left: auto; }
/* Archive pill — outer capsule mirrors the .fe-events-omni-tabs
   container exactly (same 4px padding ring, same translucent fill)
   so the bar reads as one continuous control row. The hover paint
   lands on a slightly smaller inner pill, matching how a tab inside
   the pillbar lights up — leaving a ring of whitespace between the
   hover surface and the capsule edge. */
a.fe-events-omni-archive {
  display: inline-flex;
  padding: 4px;
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 6%, transparent);
  color: color-mix(in srgb, var(--fe-color-text, #1f2933) 70%, transparent);
  font: inherit; font-size: 0.92rem; font-weight: 500;
  text-decoration: none;
}
a.fe-events-omni-archive:hover,
a.fe-events-omni-archive:focus-visible {
  /* Bring the label up to full text strength on hover so it picks
     up the same darken-on-hover behaviour `.fe-events-omni-tab:hover`
     uses (muted → currentColor). Also pins the colour so the global
     `.fe-page a:hover` link-blue rule can't repaint to blue. */
  color: var(--fe-color-text, #1f2933);
  text-decoration: none;
}
html[data-theme="dark"] a.fe-events-omni-archive:hover,
body.fe-frontend-force-dark a.fe-events-omni-archive:hover,
html[data-theme="dark"] a.fe-events-omni-archive:focus-visible,
body.fe-frontend-force-dark a.fe-events-omni-archive:focus-visible {
  /* Dark-mode equivalent: muted text → full text on hover, same as
     the dark-mode tab hover (`--fe-dm-text-muted` → `--fe-dm-text`). */
  color: var(--fe-dm-text);
}
.fe-events-omni-archive-inner {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px;
  border-radius: 999px;
  background: transparent;
  transition: background 140ms ease, color 140ms ease;
}
a.fe-events-omni-archive:hover .fe-events-omni-archive-inner,
a.fe-events-omni-archive:focus-visible .fe-events-omni-archive-inner {
  background: color-mix(in srgb, currentColor 6%, transparent);
}
html[data-theme="dark"] a.fe-events-omni-archive,
body.fe-frontend-force-dark a.fe-events-omni-archive {
  background: rgba(255, 255, 255, 0.06);
  color: var(--fe-dm-text-muted);
}
html[data-theme="dark"] a.fe-events-omni-archive:hover .fe-events-omni-archive-inner,
body.fe-frontend-force-dark a.fe-events-omni-archive:hover .fe-events-omni-archive-inner,
html[data-theme="dark"] a.fe-events-omni-archive:focus-visible .fe-events-omni-archive-inner,
body.fe-frontend-force-dark a.fe-events-omni-archive:focus-visible .fe-events-omni-archive-inner {
  /* Match `.fe-events-omni-tab:hover` exactly so the inner pill paints
     the same shade as a tab hover in the pillbar next to it. */
  background: rgba(255, 255, 255, 0.08);
}
@media (max-width: 600px) {
  /* Mobile: turn the bar into a horizontal scroller so the tab strip
     fills the viewport and the Archive pill sits off-screen to the
     right. Swiping left reveals it. Scroll-snap pulls focus back to
     either the tabs or the archive after a swipe. */
  .fe-events-omni-bar {
    width: 100%;
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 8px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .fe-events-omni-bar::-webkit-scrollbar { display: none; }
  .fe-events-omni-bar .fe-events-omni-tabs {
    flex: 0 0 100%;
    scroll-snap-align: start;
  }
  .fe-events-omni-bar a.fe-events-omni-archive {
    flex: 0 0 auto;
    margin-left: 0;
    scroll-snap-align: end;
  }
}
.fe-events-list-omni {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-events-list-omni .fe-mlist-head { margin-bottom: 18px; text-align: center; }
.fe-events-list-omni .fe-mlist-head p { margin-left: auto; margin-right: auto; }
/* Pillbar + archive sit on the surface-alt page background — flip both
   to white in light mode so they read as foreground chips, not as
   transparent shapes blending into the surrounding panel. Mirrors the
   announcements page treatment; dark mode is left to the existing
   .fe-events-omni-* dark-mode rules. */
html:not([data-theme="dark"]) .fe-events-list-omni .fe-events-omni-tabs {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
html:not([data-theme="dark"]) .fe-events-list-omni a.fe-events-omni-archive {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
/* Hover background falls through to the default `color-mix(currentColor 6%)`
   rule so it matches the tab-hover shade exactly. No surface-alt override
   needed — `currentColor` is already the muted text colour. */
.fe-events-omni-tab {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px;
  background: transparent;
  border: 0;
  border-radius: 999px;
  color: color-mix(in srgb, currentColor 70%, transparent);
  font: inherit; font-size: 0.92rem; font-weight: 500;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.fe-events-omni-tab:hover {
  background: color-mix(in srgb, currentColor 6%, transparent);
  color: currentColor;
}
.fe-events-omni-tab.is-active {
  background: var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff));
  color: var(--fe-btn-text, #ffffff);
  box-shadow: 0 4px 12px rgba(15, 23, 42, 0.12);
}
.fe-events-omni-tab.is-active:hover {
  background: var(--fe-btn-bg, var(--fe-color-btn-primary-bg, #2e6bff));
}
.fe-events-omni-tab-icon {
  display: inline-flex; width: 16px; height: 16px;
}
.fe-events-omni-tab-icon .icon { width: 16px; height: 16px; }
.fe-events-omni-panels { position: relative; }
.fe-events-omni-panel[hidden] { display: none; }

@media (max-width: 600px) {
  .fe-events-omni-tabs { width: 100%; justify-content: center; }
  .fe-events-omni-tab { flex: 1 1 auto; justify-content: center; padding: 8px 10px; }
  .fe-events-omni-tab-icon { display: none; }
}

/* Dark-mode tweaks for the omni tab strip — pull from the universal
   tokens defined at the top of this file. */
html[data-theme="dark"] .fe-events-omni-tabs,
body.fe-frontend-force-dark .fe-events-omni-tabs {
  background: rgba(255, 255, 255, 0.06);
}
html[data-theme="dark"] .fe-events-omni-tab,
body.fe-frontend-force-dark .fe-events-omni-tab {
  color: var(--fe-dm-text-muted);
}
html[data-theme="dark"] .fe-events-omni-tab:hover,
body.fe-frontend-force-dark .fe-events-omni-tab:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-events-omni-tab.is-active,
body.fe-frontend-force-dark .fe-events-omni-tab.is-active {
  background: var(--fe-dm-btn-primary-bg);
  color: var(--fe-dm-btn-primary-text);
  box-shadow: var(--fe-dm-btn-primary-shadow);
}

/* ===== Shared event-card partial ===== */
.fe-events-card {
  /* Primary card tokens — same recipe as `.fe-mlist-card` and the
     announcement / fellowships / library cards. Hover wired via the
     shared primary aggregator below. */
  position: relative;
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 22px;
  padding: 22px 24px;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 14px;
}
.fe-events-card.has-cover {
  grid-template-columns: 88px 180px minmax(0, 1fr);
}
.fe-events-card-date {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;
  padding: 12px 8px; border-radius: 10px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 8%, transparent);
  color: var(--fe-accent, #3b82f6);
}
.fe-events-card-date-month {
  font-size: 0.78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.fe-events-card-date-day {
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.8rem; font-weight: 600; line-height: 1;
  margin: 4px 0;
}
.fe-events-card-date-year {
  font-size: 0.72rem;
  color: color-mix(in srgb, currentColor 65%, transparent);
}
.fe-events-card-cover {
  display: block;
  border-radius: 10px;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}
.fe-events-card-cover img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 200ms ease;
}
.fe-events-card:hover .fe-events-card-cover img { transform: scale(1.04); }
.fe-events-card-body { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.fe-events-card-title {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.25rem; line-height: 1.25; font-weight: 600;
}
.fe-events-card-title a {
  color: inherit; text-decoration: none;
}
.fe-events-card-meta {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.875rem;
  color: color-mix(in srgb, currentColor 75%, transparent);
}
.fe-events-card-meta-row { display: inline-flex; align-items: center; gap: 8px; }
.fe-events-card-meta-icon { display: inline-flex; width: 14px; height: 14px; }
.fe-events-card-meta-icon .icon { width: 14px; height: 14px; }
.fe-events-card-summary { margin: 0; font-size: 0.95rem; line-height: 1.5; }

@media (max-width: 720px) {
  .fe-events-card,
  .fe-events-card.has-cover {
    grid-template-columns: 1fr;
    gap: 14px;
    padding: 18px;
  }
  .fe-events-card-date {
    flex-direction: row; gap: 8px; padding: 10px 14px;
    align-self: flex-start;
  }
  .fe-events-card-date-day { font-size: 1.4rem; margin: 0; }
  .fe-events-card-cover { aspect-ratio: 16 / 9; }
}

/* ===== Cards layout ===== */
.fe-events-list-stack {
  display: flex; flex-direction: column; gap: 16px;
}

/* ===== Calendar layout ===== */
.fe-events-cal {
  background: #ffffff;
  border: 1px solid var(--fe-accent);
  border-radius: 14px;
  overflow: hidden;
  /* Large drop-shadow reads through `--fe-shadow-lg` (the design-token
     scale's "lg" tier) so admins can adjust the calendar's depth from
     Site → Design alongside every other shadow. Hardcoded fallback
     mirrors the scale's `lg` value at a slightly heavier alpha so the
     calendar sheet feels lifted above the page surface. */
  box-shadow: var(--fe-shadow-lg, 0 12px 28px rgba(15, 23, 42, 0.14));
}
.fe-events-cal-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--fe-border, rgba(15, 23, 42, 0.08));
}
.fe-events-cal-title {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.3rem; font-weight: 600;
}
.fe-events-cal-nav {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0;
  background: transparent; border: 1px solid transparent;
  border-radius: 999px; cursor: pointer;
  color: inherit;
  transition: background 120ms ease, border-color 120ms ease;
}
.fe-events-cal-nav:hover {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 10%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #3b82f6) 30%, transparent);
}
.fe-events-cal-nav .icon { width: 18px; height: 18px; }
.fe-events-cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--fe-border, rgba(15, 23, 42, 0.08));
}
.fe-events-cal-head {
  padding: 10px 8px;
  text-align: center;
  background: color-mix(in srgb, currentColor 4%, transparent);
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: color-mix(in srgb, currentColor 60%, transparent);
}
.fe-events-cal-cell {
  position: relative;
  min-height: 92px;
  padding: 8px 8px 6px;
  background: #ffffff;
  display: flex; flex-direction: column; gap: 4px;
}
.fe-events-cal-cell.is-out { background: color-mix(in srgb, currentColor 3%, transparent); }
.fe-events-cal-cell-num {
  font-size: 0.85rem; font-weight: 600;
  color: color-mix(in srgb, currentColor 70%, transparent);
}
.fe-events-cal-cell.is-today .fe-events-cal-cell-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px;
  border-radius: 999px;
  background: var(--fe-accent, #3b82f6);
  color: #fff;
}
.fe-events-cal-chip {
  display: flex; flex-direction: column;
  padding: 3px 6px; border-radius: 4px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 14%, transparent);
  border-left: 2px solid var(--fe-accent, #3b82f6);
  font-size: 0.72rem; line-height: 1.2;
  color: inherit; text-decoration: none;
  overflow: hidden;
}
.fe-events-cal-chip.is-online {
  background: color-mix(in srgb, #6366f1 14%, transparent);
  border-left-color: #6366f1;
}
.fe-events-cal-chip-time {
  font-weight: 600; font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, currentColor 70%, transparent);
}
.fe-events-cal-chip-title {
  font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fe-events-cal-more {
  font-size: 0.7rem; font-weight: 600;
  color: color-mix(in srgb, currentColor 60%, transparent);
}
.fe-events-cal-list {
  padding: 18px;
  border-top: 1px solid var(--fe-border, rgba(15, 23, 42, 0.08));
}
.fe-events-cal-list-empty { margin: 0; }
.fe-events-cal-list-head {
  margin: 0 0 12px;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.05rem; font-weight: 600;
}
.fe-events-cal-list-items { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 6px; }
.fe-events-cal-list-item a {
  display: grid;
  grid-template-columns: 36px 96px minmax(0, 1fr) auto;
  gap: 10px; align-items: baseline;
  padding: 8px 10px; border-radius: 8px;
  color: inherit; text-decoration: none;
  transition: background 120ms ease;
}
.fe-events-cal-list-item a:hover {
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 10%, transparent);
}
.fe-events-cal-list-day {
  font-family: var(--fe-font-heading, sans-serif);
  font-weight: 600; font-size: 1.1rem;
  color: var(--fe-accent, #3b82f6);
}
.fe-events-cal-list-time {
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, currentColor 65%, transparent);
}
.fe-events-cal-list-title { font-weight: 500; }
.fe-events-cal-list-tag {
  font-size: 0.78rem;
  padding: 2px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 12%, transparent);
}
@media (max-width: 720px) {
  .fe-events-cal-cell { min-height: 64px; padding: 4px 4px 2px; }
  .fe-events-cal-chip-title { display: none; }
  .fe-events-cal-chip {
    width: 6px; height: 6px; padding: 0; border-radius: 50%;
    border-left: 0;
  }
  .fe-events-cal-chip-time { display: none; }
  .fe-events-cal-list-item a {
    grid-template-columns: 28px 80px minmax(0, 1fr);
    grid-template-rows: auto auto;
  }
  .fe-events-cal-list-tag { grid-column: 1 / -1; justify-self: flex-start; }
}

/* ===== Timeline layout ===== */
.fe-events-tl {
  list-style: none;
  position: relative;
  padding: 16px 0;
  margin: 0;
}
.fe-events-tl::before {
  /* Central spine */
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  left: 50%;
  width: 2px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 22%, transparent);
}
.fe-events-tl-row {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 96px 1fr;
  align-items: start;
  gap: 0;
  padding: 18px 0;
}
.fe-events-tl-date {
  grid-column: 2;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center;
  width: 72px; min-height: 88px;
  margin: 0 auto;
  padding: 10px 6px;
  border-radius: 12px;
  background: var(--fe-accent, #3b82f6);
  color: #fff;
  position: relative; z-index: 1;
  box-shadow: 0 6px 16px rgba(15, 23, 42, 0.15);
}
.fe-events-tl-month {
  font-size: 0.78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
}
.fe-events-tl-day {
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.8rem; font-weight: 700; line-height: 1;
  margin: 2px 0;
}
.fe-events-tl-year { font-size: 0.72rem; opacity: 0.85; }
.fe-events-tl-card {
  position: relative;
  padding: 18px 20px;
  background: #ffffff;
  border: 1px solid var(--fe-accent);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
.fe-events-tl-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
}
.fe-events-tl-card-link {
  position: absolute; inset: 0;
  border-radius: 12px;
}
.fe-events-tl-row--left .fe-events-tl-card { grid-column: 1; margin-right: 28px; }
.fe-events-tl-row--right .fe-events-tl-card { grid-column: 3; margin-left: 28px; }
/* Connector line from card to spine */
.fe-events-tl-row--left .fe-events-tl-card::after,
.fe-events-tl-row--right .fe-events-tl-card::after {
  content: "";
  position: absolute;
  top: 32px;
  width: 28px; height: 2px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 22%, transparent);
}
.fe-events-tl-row--left .fe-events-tl-card::after { right: -28px; }
.fe-events-tl-row--right .fe-events-tl-card::after { left: -28px; }
.fe-events-tl-cover {
  width: calc(100% + 40px); margin: -18px -20px 14px;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  /* Match the card's top-corner rounding. Without this the cover's
     square corners poke past the card's `border-radius: 12px`
     because `.fe-events-tl-card` can't use `overflow: hidden`
     itself (the ::after connector line to the timeline spine sits
     OUTSIDE the card via negative `left/right` and would get
     clipped). Bottom corners stay square — the cover bleeds into
     the card body below it. */
  border-radius: 12px 12px 0 0;
}
.fe-events-tl-cover img { width: 100%; height: 100%; object-fit: cover; }
.fe-events-tl-title {
  margin: 0 0 8px;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.15rem; line-height: 1.25; font-weight: 600;
}
.fe-events-tl-meta {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.85rem;
  color: color-mix(in srgb, currentColor 70%, transparent);
}
.fe-events-tl-meta-row { display: inline-flex; align-items: center; gap: 8px; }
.fe-events-tl-meta-icon .icon { width: 14px; height: 14px; }
.fe-events-tl-summary { margin: 10px 0 0; font-size: 0.92rem; line-height: 1.5; }

@media (max-width: 720px) {
  .fe-events-tl::before { left: 22px; }
  .fe-events-tl-row,
  .fe-events-tl-row--left,
  .fe-events-tl-row--right {
    grid-template-columns: 60px minmax(0, 1fr);
  }
  .fe-events-tl-date { grid-column: 1; width: 56px; min-height: 64px; padding: 6px 4px; }
  .fe-events-tl-day { font-size: 1.3rem; }
  .fe-events-tl-row--left .fe-events-tl-card,
  .fe-events-tl-row--right .fe-events-tl-card {
    grid-column: 2; margin: 0 0 0 8px;
  }
  .fe-events-tl-row--left .fe-events-tl-card::after,
  .fe-events-tl-row--right .fe-events-tl-card::after {
    left: -8px; right: auto; width: 8px;
  }
}

/* ===== Magazine layout ===== */
.fe-events-mag-hero {
  /* Primary card tokens — same recipe as the other list-page cards.
     Hover lift + shadow + hover border come from the shared primary
     aggregator near the bottom of this file (see the
     `.fe-events-mag-hero` entry there). */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 16px;
  overflow: hidden;
  margin-bottom: 40px;
}
.fe-events-mag-hero.has-cover {
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
}
.fe-events-mag-hero-cover {
  display: block;
  overflow: hidden;
}
.fe-events-mag-hero-cover img {
  width: 100%; height: 100%;
  min-height: 280px;
  object-fit: cover;
}
.fe-events-mag-hero-body {
  padding: 28px 32px;
  display: flex; flex-direction: column;
  gap: 12px;
}
.fe-events-mag-hero-eyebrow {
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--fe-accent, #3b82f6);
}
.fe-events-mag-hero-date {
  display: flex; flex-direction: column;
  font-family: var(--fe-font-heading, sans-serif);
}
.fe-events-mag-hero-day {
  font-size: 1.05rem; font-weight: 600;
}
.fe-events-mag-hero-time {
  font-size: 0.92rem;
  color: color-mix(in srgb, currentColor 65%, transparent);
}
.fe-events-mag-hero-title {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: clamp(1.6rem, 1.4rem + 0.6vw, 2rem);
  line-height: 1.15; font-weight: 700;
}
.fe-events-mag-hero-title a { color: inherit; text-decoration: none; }
.fe-events-mag-hero-summary {
  margin: 0;
  font-size: 1rem; line-height: 1.5;
  color: color-mix(in srgb, currentColor 80%, transparent);
}
.fe-events-mag-hero-meta {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 4px;
}
.fe-events-mag-hero-tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 999px;
  background: color-mix(in srgb, var(--fe-accent, #3b82f6) 12%, transparent);
  font-size: 0.85rem; font-weight: 500;
}
.fe-events-mag-hero-tag .icon { width: 14px; height: 14px; }
.fe-events-mag-rest-head {
  margin: 0 0 16px;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.2rem; font-weight: 600;
}
.fe-events-mag-grid {
  display: grid;
  /* Cap at 3 columns above 1024 px; drop to 2 below that and to 1
     at phone widths. Capping (vs `auto-fill`) keeps each tile wide
     enough to host a featured image at a sensible aspect ratio
     even when the page only has 4–5 secondary events to show. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
@media (max-width: 1024px) {
  .fe-events-mag-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .fe-events-mag-grid { grid-template-columns: minmax(0, 1fr); }
}
/* Vertical card layout in the More events grid: cover image at the
   top (bleeding to the card edges), then a small inline date chip,
   then the body block (title + meta + summary). The standalone
   Cards layout's horizontal date|body geometry doesn't apply here —
   the secondary tiles are narrower and read better as a stacked
   thumbnail card. Grid-template-areas pins each child to its slot
   regardless of source order in the partial. */
.fe-events-mag-grid .fe-events-card,
.fe-events-mag-grid .fe-events-card.has-cover {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "cover"
    "date"
    "body";
  gap: 10px;
  padding: 18px;
}
.fe-events-mag-grid .fe-events-card-cover {
  display: block;
  grid-area: cover;
  margin: -18px -18px 6px;
  border-radius: 14px 14px 0 0;
  aspect-ratio: 16 / 9;
}
.fe-events-mag-grid .fe-events-card-date {
  grid-area: date;
  flex-direction: row;
  align-items: center;
  align-self: flex-start;
  justify-self: flex-start;
  padding: 4px 10px;
  gap: 4px;
}
.fe-events-mag-grid .fe-events-card-date-day,
.fe-events-mag-grid .fe-events-card-date-month,
.fe-events-mag-grid .fe-events-card-date-year {
  font-size: 0.78rem;
  font-family: inherit;
  font-weight: 600;
  margin: 0;
  line-height: 1;
}
.fe-events-mag-grid .fe-events-card-body { grid-area: body; }
@media (max-width: 720px) {
  .fe-events-mag-hero,
  .fe-events-mag-hero.has-cover {
    grid-template-columns: 1fr;
  }
  .fe-events-mag-hero-cover img { min-height: 200px; }
  .fe-events-mag-hero-body { padding: 22px; }
  .fe-events-mag-grid .fe-events-card,
  .fe-events-mag-grid .fe-events-card.has-cover {
    grid-template-columns: 1fr;
  }
}

/* ===== Dark theme — events list ===== */
html[data-theme="dark"] .fe-events-card,
html[data-theme="dark"] .fe-events-cal,
html[data-theme="dark"] .fe-events-tl-card,
html[data-theme="dark"] .fe-events-mag-hero,
body.fe-frontend-force-dark .fe-events-card,
body.fe-frontend-force-dark .fe-events-cal,
body.fe-frontend-force-dark .fe-events-tl-card,
body.fe-frontend-force-dark .fe-events-mag-hero {
  background: var(--fe-dm-surface);
  border-color: var(--fe-dm-border);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-events-card:hover,
html[data-theme="dark"] .fe-events-tl-card:hover,
html[data-theme="dark"] .fe-events-mag-hero:hover,
body.fe-frontend-force-dark .fe-events-card:hover,
body.fe-frontend-force-dark .fe-events-tl-card:hover,
body.fe-frontend-force-dark .fe-events-mag-hero:hover {
  border-color: var(--fe-dm-border-hover);
}
html[data-theme="dark"] .fe-events-cal-cell,
body.fe-frontend-force-dark .fe-events-cal-cell {
  background: var(--fe-dm-surface);
}
html[data-theme="dark"] .fe-events-cal-cell.is-out,
body.fe-frontend-force-dark .fe-events-cal-cell.is-out {
  background: rgba(0, 0, 0, 0.25);
}
html[data-theme="dark"] .fe-events-cal-grid,
body.fe-frontend-force-dark .fe-events-cal-grid {
  background: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-events-cal-toolbar,
html[data-theme="dark"] .fe-events-cal-list,
body.fe-frontend-force-dark .fe-events-cal-toolbar,
body.fe-frontend-force-dark .fe-events-cal-list {
  border-color: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-events-mag-hero .fe-btn-primary,
body.fe-frontend-force-dark .fe-events-mag-hero .fe-btn-primary {
  background: var(--fe-dm-btn-primary-bg) !important;
  color: var(--fe-dm-btn-primary-text) !important;
  border-color: var(--fe-dm-btn-primary-bg) !important;
  box-shadow: var(--fe-dm-btn-primary-shadow);
}
html[data-theme="dark"] .fe-events-mag-hero .fe-btn-primary:hover,
body.fe-frontend-force-dark .fe-events-mag-hero .fe-btn-primary:hover {
  background: var(--fe-dm-btn-primary-bg-hover) !important;
  color: var(--fe-dm-btn-primary-text-hover) !important;
  border-color: var(--fe-dm-btn-primary-bg-hover) !important;
}

/* ====================================================================
   /announcements — list page with two views (Cards + GSR Summary).
   Reuses the events omni pillbar (.fe-events-omni-*) for the view
   switcher so the bar style matches /events. Local rules below cover
   the announcement cards and the paper-styled GSR Summary view.
   ==================================================================== */
.fe-announcements-list {
  padding: 32px 0 64px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-announcements-list .fe-mlist-head { text-align: center; }
.fe-announcements-list .fe-mlist-head p { margin-left: auto; margin-right: auto; }

/* Pillbar + archive sit on the surface-alt page background — flip both
   to white in light mode so they read as foreground chips, not as
   transparent shapes blending into the surrounding panel. The shared
   .fe-events-omni-* defaults still apply on /events; these overrides
   are scoped to the announcements page only. Dark mode is left to the
   existing dark-mode rules. */
html:not([data-theme="dark"]) .fe-announcements-list .fe-events-omni-tabs {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
html:not([data-theme="dark"]) .fe-announcements-list a.fe-events-omni-archive {
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}

/* Card stack for the Cards view. */
.fe-announcements-stack {
  display: flex; flex-direction: column;
  gap: 20px;
}
.fe-announcements-card {
  /* Primary card tokens — same recipe as the meetings list / events
     list / fellowships / library cards. Hover wired via the shared
     primary aggregator below. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
  padding: 24px 26px;
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 16px;
}
.fe-announcements-card.has-cover {
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 22px;
  align-items: start;
}
.fe-announcements-card-cover {
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-announcements-card-cover img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.fe-announcements-card-body {
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
}
.fe-announcements-card-title {
  margin: 0;
  font-family: var(--fe-font-heading, sans-serif);
  font-size: 1.25rem; line-height: 1.25;
  font-weight: 600;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-announcements-card-title a {
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
}
/* Subtle hover underline so the title reads as plain text at rest
   but still feels like a navigable link on hover. The card no
   longer has a separate "View details" CTA — the title IS the
   click target. */
.fe-announcements-card-title a:hover { text-decoration: underline; }
.fe-announcements-card-meta {
  display: flex; flex-wrap: wrap; gap: 14px;
  font-size: 0.85rem;
  color: color-mix(in srgb, currentColor 65%, transparent);
}
.fe-announcements-card-meta-row {
  display: inline-flex; align-items: center; gap: 6px;
}
.fe-announcements-card-meta-icon {
  display: inline-flex; width: 14px; height: 14px;
}
.fe-announcements-card-meta-icon .icon { width: 14px; height: 14px; }
.fe-announcements-card-fulltext {
  font-size: 0.95rem; line-height: 1.6;
}
.fe-announcements-card-fulltext > :first-child { margin-top: 0; }
.fe-announcements-card-fulltext > :last-child  { margin-bottom: 0; }
.fe-announcements-card-fulltext ul,
.fe-announcements-card-fulltext ol {
  margin: 0.5em 0;
  padding-left: 1.4em;
}
.fe-announcements-card-fulltext li { margin: 0.25em 0; }
.fe-announcements-card-actions {
  display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; margin-top: 4px;
}
.fe-announcements-card-cta {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.9rem; font-weight: 500;
  color: var(--fe-color-link, var(--fe-accent));
  text-decoration: none;
}
.fe-announcements-card-cta:hover { text-decoration: underline; }
.fe-announcements-card-cta-icon { display: inline-flex; width: 14px; height: 14px; }
.fe-announcements-card-cta-icon .icon { width: 14px; height: 14px; }

@media (max-width: 600px) {
  .fe-announcements-card,
  .fe-announcements-card.has-cover {
    grid-template-columns: minmax(0, 1fr);
    padding: 18px 18px;
  }
  .fe-announcements-card-cover { aspect-ratio: 16 / 9; }
}

/* ====================================================================
   GSR Summary view — a "piece of paper" centered on the page's Surface-
   Alt background. The outer panel is a transparent passthrough; the
   paper sheet itself carries all the visual chrome (max-width, white
   background, drop-shadow, Libre Baskerville).
   ==================================================================== */
.fe-announcements-gsr-panel {
  background: transparent;
  padding: 0;
  border-radius: 0;
}
.fe-announcements-gsr-paper {
  position: relative;
  max-width: 720px;
  margin: 0 auto;
  background: #ffffff;
  color: #1f2933;
  padding: clamp(28px, 5vw, 56px) clamp(24px, 5vw, 64px);
  border-radius: 4px;
  box-shadow:
    0 1px 0 rgba(15, 23, 42, 0.04),
    0 18px 40px -16px rgba(15, 23, 42, 0.18);
  font-family: 'Libre Baskerville', Georgia, 'Times New Roman', serif;
  font-size: 1.0625rem;
  line-height: 1.65;
}
.fe-announcements-gsr-paper-head {
  text-align: center;
  border-bottom: 1px solid #e7e2d6;
  padding-bottom: 18px;
  margin-bottom: 28px;
}
.fe-announcements-gsr-paper-head h2 {
  font-family: 'Libre Baskerville', Georgia, 'Times New Roman', serif;
  font-size: clamp(1.6rem, 1.2rem + 1vw, 2.2rem);
  font-weight: 700;
  margin: 0 0 6px;
  letter-spacing: 0.01em;
}
.fe-announcements-gsr-paper-head p {
  margin: 0;
  font-style: italic;
  color: #6b6358;
  font-size: 0.95rem;
}
.fe-announcements-gsr-list {
  list-style: decimal;
  padding-left: 1.4em;
  margin: 0;
}
.fe-announcements-gsr-item {
  margin: 0 0 24px;
  padding-left: 6px;
}
.fe-announcements-gsr-item:last-child { margin-bottom: 0; }
.fe-announcements-gsr-title {
  font-family: 'Libre Baskerville', Georgia, 'Times New Roman', serif;
  font-size: 1.15rem;
  font-weight: 700;
  margin: 0 0 6px;
  line-height: 1.3;
}
/* GSR title links read as plain text (no link colour, no underline)
   so the printed-digest aesthetic stays intact, with a hover
   underline for affordance. Selectors prefixed with `.fe-page` /
   `.frontend-body` so the rule beats the global `.fe-page a` link
   styling on specificity. */
.fe-page .fe-announcements-gsr-title a,
.frontend-body .fe-announcements-gsr-title a {
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
}
.fe-page .fe-announcements-gsr-title a:hover,
.frontend-body .fe-announcements-gsr-title a:hover {
  color: inherit;
  text-decoration: underline;
}
.fe-announcements-gsr-summary {
  margin: 0;
  font-size: 1rem;
  line-height: 1.65;
  color: #2b3441;
}
.fe-announcements-gsr-empty {
  text-align: center;
  font-style: italic;
  color: #6b6358;
  margin: 0;
}

/* Archive page — minimal, reuses the sidebar shell. */
.fe-announcements-archive { padding: 32px 0 64px; }

/* Unified /archive page — type-toggle checkboxes in the rail. Sit
   between the search input and the year pills, styled as compact rows
   with a count chip on the right (mirroring the day-pill counts) so
   the rail reads as one consistent column. */
.fe-archive-types {
  border: 0;
  margin: 0 0 14px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fe-archive-types-legend {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: color-mix(in srgb, currentColor 55%, transparent);
  padding: 0;
  margin: 0 0 6px;
}
.fe-archive-type-check {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 0.92rem;
  cursor: pointer;
  transition: background 140ms ease;
}
.fe-archive-type-check:hover {
  background: color-mix(in srgb, currentColor 6%, transparent);
}
.fe-archive-type-check input[type="checkbox"] {
  margin: 0;
  flex: 0 0 auto;
}
.fe-archive-type-check-label { flex: 1 1 auto; }
.fe-archive-type-check .fe-mlist-day-count { flex: 0 0 auto; }

/* ====================================================================
   Dark-mode coverage for /events, /announcements, and /archive.

   The page-level wrappers default to `--fe-color-surface-alt`, which
   the design system always emits as a light value — so without these
   overrides the section paints a pale-grey rectangle on top of the
   dark body. Cards and the GSR "paper" sheet have the same problem
   (hard-coded #ffffff). Each rule pairs `html[data-theme="dark"]`
   with `body.fe-frontend-force-dark` so the admin's force-dark toggle
   matches the visitor's saved theme choice.
   ==================================================================== */

/* Page wrappers — swap surface-alt for the dark page colour. */
html[data-theme="dark"] .fe-events-list-omni,
html[data-theme="dark"] .fe-announcements-list,
body.fe-frontend-force-dark .fe-events-list-omni,
body.fe-frontend-force-dark .fe-announcements-list {
  background: var(--fe-dm-page-bg);
  color: var(--fe-dm-text);
}

/* Announcement cards — same surface/border recipe the event cards
   use, so the two card types read consistently on the archive page
   (which intermixes them) and on the announcements list. */
html[data-theme="dark"] .fe-announcements-card,
body.fe-frontend-force-dark .fe-announcements-card {
  background: var(--fe-dm-surface);
  border-color: var(--fe-dm-border);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-announcements-card:hover,
body.fe-frontend-force-dark .fe-announcements-card:hover {
  border-color: var(--fe-dm-border-hover);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-announcements-card-title,
body.fe-frontend-force-dark .fe-announcements-card-title {
  color: var(--fe-dm-text-strong);
}
html[data-theme="dark"] .fe-announcements-card-meta,
body.fe-frontend-force-dark .fe-announcements-card-meta {
  color: var(--fe-dm-text-muted);
}
html[data-theme="dark"] .fe-announcements-card-cover,
body.fe-frontend-force-dark .fe-announcements-card-cover {
  background: rgba(255, 255, 255, 0.04);
}
html[data-theme="dark"] .fe-announcements-card-cta,
body.fe-frontend-force-dark .fe-announcements-card-cta {
  color: var(--fe-dm-link-hover);
}

/* GSR Summary "paper" — keep the print-style sheet metaphor in dark
   mode by swapping the white sheet for an elevated dark surface and
   updating the rule + caption colours so contrast holds. */
html[data-theme="dark"] .fe-announcements-gsr-paper,
body.fe-frontend-force-dark .fe-announcements-gsr-paper {
  background: var(--fe-dm-surface);
  color: var(--fe-dm-text);
  box-shadow:
    0 1px 0 rgba(0, 0, 0, 0.2),
    0 18px 40px -16px rgba(0, 0, 0, 0.55);
}
html[data-theme="dark"] .fe-announcements-gsr-paper-head,
body.fe-frontend-force-dark .fe-announcements-gsr-paper-head {
  border-bottom-color: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-announcements-gsr-paper-head h2,
body.fe-frontend-force-dark .fe-announcements-gsr-paper-head h2 {
  color: var(--fe-dm-text-strong);
}
html[data-theme="dark"] .fe-announcements-gsr-paper-head p,
body.fe-frontend-force-dark .fe-announcements-gsr-paper-head p {
  color: var(--fe-dm-text-muted);
}
html[data-theme="dark"] .fe-announcements-gsr-title,
body.fe-frontend-force-dark .fe-announcements-gsr-title {
  color: var(--fe-dm-text-strong);
}
html[data-theme="dark"] .fe-announcements-gsr-summary,
body.fe-frontend-force-dark .fe-announcements-gsr-summary {
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-announcements-gsr-empty,
body.fe-frontend-force-dark .fe-announcements-gsr-empty {
  color: var(--fe-dm-text-muted);
}

/* Archive page rail — type checkboxes use currentColor for hover so
   they inherit, but the count chip on each label uses the meetings
   day-count chrome which needs a dark mode shade so it reads on the
   dark rail. The day-pill rules handle this site-wide; this scoped
   rule covers the legend label and the in-archive counts. */
html[data-theme="dark"] .fe-archive-types-legend,
body.fe-frontend-force-dark .fe-archive-types-legend {
  color: var(--fe-dm-text-muted);
}

/* ───────────────────────────────────────────────────────────────────
   Shared top-filter strip — chrome for the non-sidebar archive layouts
   (timeline, compact list, magazine). Mirrors the rail's information
   architecture (title + subhead + back-links + search + type chips +
   year pills) in a single horizontal band above the results column.
   ───────────────────────────────────────────────────────────────── */
.fe-archive-strip {
  display: flex; flex-direction: column; gap: 1rem;
  padding: 2.5rem 0 1.5rem;
}
.fe-archive-strip-head { display: flex; flex-direction: column; gap: 0.35rem; }
.fe-archive-strip-title {
  margin: 0;
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: clamp(2rem, 1.4rem + 2.4vw, 3rem);
  line-height: 1.1;
}
.fe-archive-strip-sub { max-width: 60ch; margin: 0; }
.fe-archive-strip-back-links {
  display: flex; flex-wrap: wrap; gap: 0.75rem 1.25rem;
  margin-top: 0.35rem;
}
.fe-archive-strip-back-links a {
  display: inline-flex; align-items: center; gap: 0.4rem;
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.65));
  text-decoration: none;
  font-size: 0.9rem;
}
.fe-archive-strip-back-links a:hover { color: var(--fe-color-accent, #0b5cff); }
.fe-archive-strip-back-links svg { width: 14px; height: 14px; }
.fe-archive-strip-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem;
}
.fe-archive-strip-search { flex: 1 1 320px; min-width: 0; }
.fe-archive-types--inline {
  display: flex; gap: 0.5rem; margin: 0; padding: 0; border: 0;
  flex-wrap: wrap;
}
.fe-archive-types--inline .fe-archive-type-check {
  border: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.12));
  border-radius: 999px;
  padding: 0.35rem 0.85rem 0.35rem 0.5rem;
}
.fe-archive-strip-years {
  display: flex; flex-wrap: wrap; gap: 0.4rem;
  list-style: none; margin: 0; padding: 0;
}

/* ───────────────────────────────────────────────────────────────────
   Timeline layout — vertical spine on the centerline with year markers
   stamped along it and cards alternating left/right.
   ───────────────────────────────────────────────────────────────── */
.fe-archive-tl {
  position: relative;
  padding-top: 1rem;
}
.fe-archive-tl-spine {
  position: absolute; left: 50%; top: 0; bottom: 0; width: 2px;
  background: var(--fe-color-border, rgba(15, 23, 42, 0.12));
  transform: translateX(-50%);
  z-index: 0;
}
.fe-archive-tl-year { position: relative; margin: 2rem 0 1.25rem; }
.fe-archive-tl-marker {
  position: relative;
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: 1.4rem;
  margin: 0 auto 1.5rem;
  padding: 0.4rem 1.25rem;
  background: var(--fe-color-surface, #fff);
  border: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.14));
  border-radius: 999px;
  width: fit-content;
  z-index: 1;
}
.fe-archive-tl-marker-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--fe-color-accent, #0b5cff);
}
.fe-archive-tl-marker-count {
  font-size: 0.8rem;
  font-family: var(--fe-font-body, var(--font-body, system-ui, sans-serif));
  font-weight: 600;
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.65));
}
.fe-archive-tl-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 1.25rem;
}
.fe-archive-tl-item {
  position: relative;
  width: calc(50% - 1.5rem);
  z-index: 1;
}
.fe-archive-tl-item--left  { align-self: flex-start; }
.fe-archive-tl-item--right { align-self: flex-end; }
.fe-archive-tl-item::before {
  content: "";
  position: absolute; top: 1.25rem;
  width: 1.5rem; height: 2px;
  background: var(--fe-color-border, rgba(15, 23, 42, 0.18));
}
.fe-archive-tl-item--left::before  { right: -1.5rem; }
.fe-archive-tl-item--right::before { left:  -1.5rem; }
.fe-archive-tl-item::after {
  content: "";
  position: absolute; top: 1.05rem;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--fe-color-surface, #fff);
  border: 2px solid var(--fe-color-accent, #0b5cff);
}
.fe-archive-tl-item--left::after  { right: calc(-1.5rem - 6px); }
.fe-archive-tl-item--right::after { left:  calc(-1.5rem - 6px); }
.fe-archive-tl-card {
  background: var(--fe-color-surface, #fff);
  border: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.1));
  border-radius: 14px;
  padding: 1rem 1.15rem;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  display: flex; flex-direction: column; gap: 0.6rem;
}
.fe-archive-tl-card-head {
  display: flex; align-items: center; gap: 0.6rem;
  flex-wrap: wrap;
}
.fe-archive-tl-card-date {
  display: inline-flex; flex-direction: column; align-items: center;
  padding: 0.3rem 0.55rem;
  border-radius: 8px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
  line-height: 1;
  min-width: 2.5rem;
}
.fe-archive-tl-card-date-month {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--fe-color-accent, #0b5cff);
  font-weight: 700;
}
.fe-archive-tl-card-date-day {
  font-size: 1.25rem; font-weight: 700;
  margin-top: 2px;
}
.fe-archive-tl-card-kind {
  font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.15rem 0.55rem; border-radius: 999px;
  background: rgba(11, 92, 255, 0.08);
  color: var(--fe-color-accent, #0b5cff);
}
.fe-archive-tl-card-kind--announcement {
  background: rgba(168, 85, 247, 0.1);
  color: #7c3aed;
}
.fe-archive-tl-card-cover {
  display: block; overflow: hidden; border-radius: 10px;
  aspect-ratio: 16 / 9;
}
.fe-archive-tl-card-cover img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.fe-archive-tl-card-title {
  margin: 0;
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: 1.1rem; line-height: 1.3;
}
.fe-archive-tl-card-title a {
  color: inherit; text-decoration: none;
}
.fe-archive-tl-card-title a:hover { color: var(--fe-color-accent, #0b5cff); }
.fe-archive-tl-card-summary { margin: 0; font-size: 0.92rem; line-height: 1.4; }
.fe-archive-tl-card-meta { margin: 0; font-size: 0.82rem; display: inline-flex; align-items: center; gap: 0.35rem; }
.fe-archive-tl-card-meta svg { width: 14px; height: 14px; }

@media (max-width: 720px) {
  /* Collapse the timeline into a single left-aligned column on
     phones so cards don't squeeze to <40 char wide strips. The spine
     moves to the left edge and every item becomes a left-side card. */
  .fe-archive-tl-spine { left: 1rem; transform: none; }
  .fe-archive-tl-marker { margin-left: 0; }
  .fe-archive-tl-item, .fe-archive-tl-item--left, .fe-archive-tl-item--right {
    width: calc(100% - 2.5rem);
    margin-left: 2.5rem; align-self: flex-start;
  }
  .fe-archive-tl-item::before { left: -1.5rem; right: auto; }
  .fe-archive-tl-item::after  { left: calc(-1.5rem - 6px); right: auto; }
}

/* ───────────────────────────────────────────────────────────────────
   Compact List layout — dense single-column rows. Each row is a single
   clickable link with a date block, kind chip, title + summary.
   ───────────────────────────────────────────────────────────────── */
.fe-archive-cl { padding-top: 0.5rem; }
.fe-archive-cl-year { margin: 1.75rem 0 0.75rem; }
.fe-archive-cl-year-heading {
  display: flex; align-items: baseline; gap: 0.6rem;
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: 1.5rem;
  margin: 0 0 0.5rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.12));
}
.fe-archive-cl-year-count {
  font-size: 0.8rem; font-weight: 600;
  font-family: var(--fe-font-body, var(--font-body, system-ui, sans-serif));
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.55));
  padding: 0.1rem 0.55rem; border-radius: 999px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-cl-list { list-style: none; margin: 0; padding: 0; }
.fe-archive-cl-row {
  border-bottom: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.08));
}
.fe-archive-cl-row:last-child { border-bottom: 0; }
.fe-archive-cl-link {
  display: grid;
  grid-template-columns: 4.5rem auto 1fr auto;
  align-items: center; gap: 0.85rem;
  padding: 0.85rem 0.5rem;
  color: inherit; text-decoration: none;
  transition: background 120ms ease;
}
.fe-archive-cl-link:hover {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-cl-date {
  display: flex; flex-direction: column; align-items: center;
  line-height: 1;
  padding: 0.35rem 0.4rem;
  border-radius: 8px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-cl-date-month {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--fe-color-accent, #0b5cff);
  font-weight: 700;
}
.fe-archive-cl-date-day { font-size: 1.15rem; font-weight: 700; margin-top: 2px; }
.fe-archive-cl-date-year { font-size: 0.7rem; color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.55)); margin-top: 2px; }
.fe-archive-cl-kind {
  font-size: 0.7rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.15rem 0.55rem; border-radius: 999px;
  background: rgba(11, 92, 255, 0.08);
  color: var(--fe-color-accent, #0b5cff);
  white-space: nowrap;
}
.fe-archive-cl-kind--announcement {
  background: rgba(168, 85, 247, 0.1);
  color: #7c3aed;
}
.fe-archive-cl-body { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.fe-archive-cl-title { font-weight: 600; line-height: 1.3; }
.fe-archive-cl-summary { font-size: 0.9rem; line-height: 1.4; }
.fe-archive-cl-meta { font-size: 0.82rem; display: inline-flex; align-items: center; gap: 0.3rem; }
.fe-archive-cl-meta svg { width: 13px; height: 13px; }
.fe-archive-cl-arrow {
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.4));
  transition: color 120ms ease, transform 120ms ease;
}
.fe-archive-cl-arrow svg { width: 16px; height: 16px; }
.fe-archive-cl-link:hover .fe-archive-cl-arrow {
  color: var(--fe-color-accent, #0b5cff);
  transform: translateX(3px);
}

@media (max-width: 560px) {
  .fe-archive-cl-link {
    grid-template-columns: 3.5rem 1fr;
    grid-template-rows: auto auto;
    row-gap: 0.4rem;
  }
  .fe-archive-cl-kind { grid-column: 2; justify-self: start; }
  .fe-archive-cl-body { grid-column: 2; }
  .fe-archive-cl-date { grid-row: 1 / span 2; }
  .fe-archive-cl-arrow { display: none; }
}

/* ───────────────────────────────────────────────────────────────────
   Magazine layout — 3-up grid of editorial tiles with the very first
   tile spanning two columns as a feature. Each tile is a clickable
   link wrapping a cover, kind chip, date, title, and summary.
   ───────────────────────────────────────────────────────────────── */
.fe-archive-mag { padding-top: 1rem; }
.fe-archive-mag-year { margin: 2rem 0; }
.fe-archive-mag-year-heading {
  display: flex; align-items: baseline; gap: 0.6rem;
  margin: 0 0 1rem;
}
.fe-archive-mag-year-eyebrow {
  font-size: 0.7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.55));
}
.fe-archive-mag-year-num {
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: 1.75rem; line-height: 1;
}
.fe-archive-mag-year-count {
  font-size: 0.8rem; font-weight: 600;
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.55));
  padding: 0.1rem 0.55rem; border-radius: 999px;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-mag-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.5rem;
}
.fe-archive-mag-tile {
  background: var(--fe-color-surface, #fff);
  border: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.1));
  border-radius: 16px;
  overflow: hidden;
  transition: transform 160ms ease, box-shadow 160ms ease;
  display: flex;
}
.fe-archive-mag-tile:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.08);
}
.fe-archive-mag-tile--feature {
  grid-column: 1 / -1;
}
.fe-archive-mag-tile--feature .fe-archive-mag-tile-link {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  align-items: stretch;
}
.fe-archive-mag-tile--feature .fe-archive-mag-tile-cover {
  aspect-ratio: auto;
  min-height: 240px;
}
.fe-archive-mag-tile--feature .fe-archive-mag-tile-title {
  font-size: clamp(1.4rem, 1rem + 1.4vw, 2rem);
}
@media (max-width: 720px) {
  .fe-archive-mag-tile--feature .fe-archive-mag-tile-link { grid-template-columns: 1fr; }
  .fe-archive-mag-tile--feature .fe-archive-mag-tile-cover { min-height: 0; aspect-ratio: 16 / 9; }
}
.fe-archive-mag-tile-link {
  display: flex; flex-direction: column;
  color: inherit; text-decoration: none;
  width: 100%;
}
.fe-archive-mag-tile-cover {
  display: block; overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-mag-tile-cover img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 200ms ease;
}
.fe-archive-mag-tile:hover .fe-archive-mag-tile-cover img { transform: scale(1.03); }
.fe-archive-mag-tile-body {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 1rem 1.15rem 1.15rem;
  flex: 1;
}
.fe-archive-mag-tile-meta {
  display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  font-size: 0.78rem;
}
.fe-archive-mag-tile-kind {
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  padding: 0.15rem 0.5rem; border-radius: 999px;
  background: rgba(11, 92, 255, 0.08); color: var(--fe-color-accent, #0b5cff);
}
.fe-archive-mag-tile-kind--announcement {
  background: rgba(168, 85, 247, 0.1); color: #7c3aed;
}
.fe-archive-mag-tile-date {
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.6));
}
.fe-archive-mag-tile-title {
  font-family: var(--fe-font-heading, var(--font-heading, "Fraunces", serif));
  font-size: 1.2rem; line-height: 1.25;
}
.fe-archive-mag-tile-summary { font-size: 0.92rem; line-height: 1.45; }

/* Dark-mode parity — every new archive layout reads against the dark
   page tones too. Lifts surfaces, softens borders, and tints the kind
   chips so they stay legible. */
html[data-theme="dark"] .fe-archive-tl-marker,
html[data-theme="dark"] .fe-archive-tl-card,
html[data-theme="dark"] .fe-archive-mag-tile,
body.fe-frontend-force-dark .fe-archive-tl-marker,
body.fe-frontend-force-dark .fe-archive-tl-card,
body.fe-frontend-force-dark .fe-archive-mag-tile {
  background: var(--fe-dm-surface, rgba(255, 255, 255, 0.05));
  border-color: var(--fe-dm-border, rgba(255, 255, 255, 0.12));
  color: var(--fe-dm-text, #f8fafc);
}
html[data-theme="dark"] .fe-archive-cl-year-heading,
html[data-theme="dark"] .fe-archive-cl-row,
body.fe-frontend-force-dark .fe-archive-cl-year-heading,
body.fe-frontend-force-dark .fe-archive-cl-row {
  border-color: var(--fe-dm-border, rgba(255, 255, 255, 0.12));
}
html[data-theme="dark"] .fe-archive-cl-date,
html[data-theme="dark"] .fe-archive-tl-card-date,
html[data-theme="dark"] .fe-archive-mag-year-count,
html[data-theme="dark"] .fe-archive-cl-year-count,
html[data-theme="dark"] .fe-archive-tl-marker-count,
body.fe-frontend-force-dark .fe-archive-cl-date,
body.fe-frontend-force-dark .fe-archive-tl-card-date,
body.fe-frontend-force-dark .fe-archive-mag-year-count,
body.fe-frontend-force-dark .fe-archive-cl-year-count,
body.fe-frontend-force-dark .fe-archive-tl-marker-count {
  background: var(--fe-dm-surface-alt, rgba(255, 255, 255, 0.08));
  color: var(--fe-dm-text-muted, rgba(255, 255, 255, 0.65));
}
html[data-theme="dark"] .fe-archive-cl-link:hover,
body.fe-frontend-force-dark .fe-archive-cl-link:hover {
  background: var(--fe-dm-surface-alt, rgba(255, 255, 255, 0.08));
}
html[data-theme="dark"] .fe-archive-tl-spine,
body.fe-frontend-force-dark .fe-archive-tl-spine {
  background: var(--fe-dm-border, rgba(255, 255, 255, 0.12));
}

/* Archive pagination — numbered page links + infinite-scroll sentinel.
   The sentinel is an invisible anchor an IntersectionObserver watches
   to trigger the next-batch reveal in infinite mode; it just needs to
   occupy a sliver of vertical space at the bottom of the list. */
.fe-archive-load-sentinel {
  height: 1px;
  width: 100%;
  margin-top: 1rem;
}
.fe-archive-pagination {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: center;
  align-items: center;
  margin: 2rem 0 0.5rem;
}
.fe-archive-page-btn {
  font: inherit;
  min-width: 2.25rem;
  padding: 0.4rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--fe-color-border, rgba(15, 23, 42, 0.12));
  background: var(--fe-color-surface, #fff);
  color: var(--fe-color-text, #0f172a);
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, border-color 120ms ease, transform 120ms ease;
}
.fe-archive-page-btn:hover:not(:disabled):not(.is-active) {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-archive-page-btn.is-active {
  background: var(--fe-color-accent, #0b5cff);
  border-color: var(--fe-color-accent, #0b5cff);
  color: var(--fe-color-accent-contrast, #fff);
  cursor: default;
}
.fe-archive-page-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.fe-archive-page-gap {
  padding: 0 0.25rem;
  color: var(--fe-color-text-muted, rgba(15, 23, 42, 0.55));
}
html[data-theme="dark"] .fe-archive-page-btn,
body.fe-frontend-force-dark .fe-archive-page-btn {
  background: transparent;
  border-color: var(--fe-dm-border, rgba(255, 255, 255, 0.16));
  color: var(--fe-dm-text, #f8fafc);
}
html[data-theme="dark"] .fe-archive-page-btn:hover:not(:disabled):not(.is-active),
body.fe-frontend-force-dark .fe-archive-page-btn:hover:not(:disabled):not(.is-active) {
  background: var(--fe-dm-surface-alt, rgba(255, 255, 255, 0.05));
}
html[data-theme="dark"] .fe-archive-page-btn.is-active,
body.fe-frontend-force-dark .fe-archive-page-btn.is-active {
  background: var(--fe-color-accent, #0b5cff);
  border-color: var(--fe-color-accent, #0b5cff);
  color: var(--fe-color-accent-contrast, #fff);
}
html[data-theme="dark"] .fe-archive-page-gap,
body.fe-frontend-force-dark .fe-archive-page-gap {
  color: var(--fe-dm-text-muted, rgba(255, 255, 255, 0.55));
}


/* ===== Submission form (public /submissionform + global modal) =====
   Visual language mirrors the login-page Request Access modal:
   centered card, vertical stack of labelled inputs, clean
   typography. Lives both inline on /submissionform and as a
   site-wide modal that any nav link / button can open. */
/* Page area sits on the soft surface-alt tone — same recipe the
   /meetings list uses, so the submission page reads as part of the
   same visual family. Dark mode swaps to the page-bg token via the
   dark-mode block below. */
.fe-submission-section {
  padding: 4rem 0;
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f1f5f9));
}
.fe-submission-page { max-width: 720px; margin: 0 auto; }
.fe-submission-page-head {
  text-align: center;
  margin: 0 0 2rem;
}
.fe-submission-page-title {
  margin: 0 0 0.5rem;
  font-size: clamp(1.6rem, 1.4rem + 1vw, 2.2rem);
  font-weight: 700;
  color: var(--fe-color-text, #0a0a0a);
  line-height: 1.2;
}
.fe-submission-page-blurb {
  margin: 0;
  color: var(--fe-color-text-muted, #475569);
  font-size: 1rem;
  max-width: 50ch;
  margin-left: auto;
  margin-right: auto;
}
/* Form card mirrors the .fe-mlist-card recipe on /meetings:
   solid 1px accent border, soft 1px shadow at rest, 1px translateY
   lift + deeper shadow on hover. Border colour stays put on hover
   so the card feels stable rather than visibly recolouring under
   the cursor. */
.fe-submission-card {
  background: #ffffff;
  border: 1px solid var(--fe-accent);
  border-radius: 16px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  padding: 2rem 2rem 2.25rem;
  transition: transform 160ms ease, box-shadow 160ms ease;
}
.fe-submission-card:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.08);
}
.fe-submission-flashes { margin: 0 0 1.25rem; }
.fe-submission-flashes .flash {
  padding: 12px 16px;
  border-radius: 8px;
  margin: 0 0 8px;
  font-size: 0.95rem;
}
.fe-submission-flashes .flash-success {
  background: color-mix(in srgb, #10b981 12%, transparent);
  color: #047857;
  border: 1px solid color-mix(in srgb, #10b981 35%, transparent);
}
.fe-submission-flashes .flash-danger {
  background: color-mix(in srgb, #ef4444 12%, transparent);
  color: #b91c1c;
  border: 1px solid color-mix(in srgb, #ef4444 35%, transparent);
}

/* Public-display warning that sits above the event-contact fieldset.
   Soft amber band so submitters can't miss that whatever they type
   below will be visible to the public on the published post. */
.fe-submission-public-warning {
  margin: -2px 0 4px;
  padding: 8px 12px;
  border-radius: 6px;
  background: color-mix(in srgb, #f59e0b 14%, transparent);
  color: #92400e;
  border: 1px solid color-mix(in srgb, #f59e0b 35%, transparent);
  line-height: 1.45;
}
.fe-submission-public-warning strong { color: #78350f; }
html[data-theme="dark"] .fe-submission-public-warning,
body.fe-frontend-force-dark .fe-submission-public-warning {
  background: color-mix(in srgb, #f59e0b 22%, transparent);
  color: #fde68a;
  border-color: color-mix(in srgb, #f59e0b 50%, transparent);
}
html[data-theme="dark"] .fe-submission-public-warning strong,
body.fe-frontend-force-dark .fe-submission-public-warning strong {
  color: #fffbeb;
}

/* Dark-mode flash colours — light reds / greens against the
   near-black card surface so the message is readable when the page
   is in dark mode. The light-mode rule's #047857 / #b91c1c text
   colour is too dark to read on the dark card otherwise. */
html[data-theme="dark"] .fe-submission-flashes .flash-success,
body.fe-frontend-force-dark .fe-submission-flashes .flash-success {
  background: color-mix(in srgb, #10b981 22%, transparent);
  color: #6ee7b7;
  border-color: color-mix(in srgb, #10b981 45%, transparent);
}
html[data-theme="dark"] .fe-submission-flashes .flash-danger,
body.fe-frontend-force-dark .fe-submission-flashes .flash-danger {
  background: color-mix(in srgb, #ef4444 22%, transparent);
  color: #fca5a5;
  border-color: color-mix(in srgb, #ef4444 45%, transparent);
}

/* Form layout — matches the request-access form's vertical stack. */
.fe-submission-form {
  display: flex;
  flex-direction: column;
  gap: 1.125rem;
}
.fe-submission-form .req { color: #e11d48; }
.fe-submission-form label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--fe-color-text, #0a0a0a);
}
.fe-submission-form label > span:first-child { line-height: 1.4; }
.fe-submission-form input[type="text"],
.fe-submission-form input[type="email"],
.fe-submission-form input[type="tel"],
.fe-submission-form input[type="url"],
.fe-submission-form input[type="datetime-local"],
.fe-submission-form input[type="file"],
.fe-submission-form textarea {
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--fe-color-border, rgba(0, 0, 0, 0.16));
  background: var(--fe-color-surface, #fff);
  color: var(--fe-color-text, #0a0a0a);
  font-size: 0.9375rem;
  font-family: inherit;
  width: 100%;
}
.fe-submission-form input:focus,
.fe-submission-form textarea:focus {
  outline: none;
  border-color: var(--fe-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fe-accent) 22%, transparent);
}
.fe-submission-form textarea { resize: vertical; min-height: 4rem; }
.fe-submission-form fieldset {
  border: 1px solid var(--fe-color-border, rgba(0, 0, 0, 0.08));
  border-radius: 10px;
  padding: 1rem 1.125rem;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
}
.fe-submission-form fieldset legend {
  font-size: 0.875rem;
  font-weight: 600;
  padding: 0 0.5rem;
  color: var(--fe-color-text, #0a0a0a);
}
.fe-submission-form .check {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: 10px;
  font-weight: 400;
  font-size: 0.9375rem;
}
.fe-submission-form .check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  accent-color: var(--fe-accent);
  flex-shrink: 0;
}
.fe-submission-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.875rem;
}
@media (max-width: 540px) {
  .fe-submission-grid-2 { grid-template-columns: 1fr; }
  .fe-submission-card { padding: 1.5rem 1.25rem 1.75rem; }
  .fe-submission-section { padding: 2rem 0; }
}
.fe-submission-event-fields[hidden] { display: none; }
.fe-submission-submitter-help { margin: -4px 0 0; }
.fe-submission-turnstile { margin: 4px 0 0; min-height: 65px; }
.fe-submission-actions {
  margin-top: 0.5rem;
  display: flex;
  justify-content: flex-end;
}
/* Submit button inherits the same brand-tokenised recipe used by
   .fe-btn-primary on every other public surface. The four admin-
   toggleable design tokens — --fe-color-btn-primary-bg /
   --fe-color-btn-primary-text / --fe-btn-shadow / --fe-btn-hover-
   transform / --fe-btn-hover-glow — flow through transparently, so
   changing the primary-button colour or toggling the rest shadow /
   hover lift / hover glow on the Design tokens admin page recolours
   and animates this button automatically. The `transition` is also
   the same shape as .fe-btn so the timing and properties match. */
.fe-submission-submit {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--fe-color-btn-primary-bg, var(--fe-accent));
  color: var(--fe-color-btn-primary-text, #fff);
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 14px 28px;
  font-size: 1rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--fe-btn-shadow, 0 8px 20px rgba(15, 23, 42, 0.18));
  transition: transform 150ms ease, box-shadow 150ms ease, background 150ms ease;
}
.fe-submission-submit:hover {
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  background: color-mix(in srgb,
                        var(--fe-color-btn-primary-bg, var(--fe-accent)) 88%,
                        black);
  color: var(--fe-color-btn-primary-text, #fff);
  box-shadow: var(--fe-btn-hover-glow, 0 10px 28px rgba(81, 100, 255, 0.32));
}
/* Dark mode mirrors the .fe-btn-primary dark-theme rules — bright
   surface on a dark page, slightly brighter on hover. The token
   chain still applies for installs that customised dark-mode
   button colours; the hardcoded #e2e8f0 / #fff matches the dark
   .fe-btn-primary baked defaults so the two buttons stay in sync. */
html[data-theme="dark"] .fe-submission-submit,
body.fe-frontend-force-dark .fe-submission-submit {
  background: var(--fe-dm-btn-primary-bg, #e2e8f0);
  color: var(--fe-dm-btn-primary-text, #0b1026);
}
html[data-theme="dark"] .fe-submission-submit:hover,
body.fe-frontend-force-dark .fe-submission-submit:hover {
  background: var(--fe-dm-btn-primary-bg-hover, #ffffff);
  color: var(--fe-dm-btn-primary-text, #0b1026);
}
.fe-submission-fineprint {
  text-align: center;
  margin: 0;
  line-height: 1.5;
}

/* ── Modal variant ─────────────────────────────────────────────────
   Same form body; wraps in a centered overlay panel split into
   three regions: a fixed header (title + X), a scrollable body
   (the form), and a fixed footer (the submit button via HTML5
   ``form="…"``). The panel itself caps at 90vh so the body's
   internal scroll triggers when the form gets long. JS toggles
   ``.fe-submission-modal[hidden]`` and the ``fe-submission-open``
   body class to lock background scroll while open. */
.fe-submission-modal[hidden] { display: none !important; }
.fe-submission-modal {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4vh 1rem;
}
.fe-submission-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(2px);
  pointer-events: none;  /* click-off does NOT close the modal */
}
.fe-submission-modal-panel {
  position: relative;
  width: 100%;
  max-width: 640px;
  max-height: 92vh;
  background: var(--fe-color-surface, #fff);
  border-radius: 18px;
  box-shadow: 0 28px 70px rgba(0, 0, 0, 0.35);
  z-index: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;  /* corners stay rounded as the body scrolls */
}
.fe-submission-modal-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid var(--fe-color-border, rgba(0,0,0,0.08));
  background: var(--fe-color-surface, #fff);
}
.fe-submission-modal-head-text { flex: 1 1 auto; min-width: 0; }
.fe-submission-modal-head h2 {
  margin: 0 0 0.125rem;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--fe-color-text, #0a0a0a);
  line-height: 1.3;
}
.fe-submission-modal-head p { margin: 0; }
.fe-submission-modal-close {
  flex: 0 0 auto;
  width: 36px; height: 36px;
  background: transparent; border: 0;
  border-radius: 8px;
  display: grid; place-items: center;
  cursor: pointer;
  color: var(--fe-color-text-muted, #64748b);
}
.fe-submission-modal-close:hover { background: rgba(0,0,0,0.06); color: var(--fe-accent); }
.fe-submission-modal-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1.25rem 1.5rem;
  /* Subtle scroll-edge shadow shows when the body content is taller
     than the panel — gives visitors a hint that there's more below. */
  background:
    linear-gradient(var(--fe-color-surface, #fff) 30%, rgba(255,255,255,0)) center top,
    linear-gradient(rgba(255,255,255,0), var(--fe-color-surface, #fff) 70%) center bottom,
    radial-gradient(farthest-side at 50% 0, rgba(15,23,42,0.08), rgba(0,0,0,0)) center top,
    radial-gradient(farthest-side at 50% 100%, rgba(15,23,42,0.08), rgba(0,0,0,0)) center bottom;
  background-repeat: no-repeat;
  background-size: 100% 32px, 100% 32px, 100% 12px, 100% 12px;
  background-attachment: local, local, scroll, scroll;
}
.fe-submission-modal-foot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--fe-color-border, rgba(0,0,0,0.08));
  background: var(--fe-color-surface, #fff);
  flex-wrap: wrap;
}
.fe-submission-modal-foot .fe-submission-fineprint {
  margin: 0;
  text-align: left;
  flex: 1 1 220px;
  line-height: 1.45;
}
.fe-submission-modal-foot-actions {
  display: flex;
  justify-content: flex-end;
  flex: 0 0 auto;
}
@media (max-width: 540px) {
  .fe-submission-modal { padding: 0; }
  .fe-submission-modal-panel { max-height: 100vh; height: 100vh; border-radius: 0; }
  .fe-submission-modal-head, .fe-submission-modal-body, .fe-submission-modal-foot {
    padding-left: 1rem; padding-right: 1rem;
  }
}
body.fe-submission-open { overflow: hidden; }

/* Dark-mode page surface — same recipe the /meetings list uses:
   the page sits on the dark page-bg, the card flips to a near-black
   surface with a soft white border, and the hover state brightens
   the edge to the same indigo tint .fe-mlist-card adopts on hover. */
html[data-theme="dark"] .fe-submission-section,
body.fe-frontend-force-dark .fe-submission-section {
  background: var(--fe-dm-page-bg);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-submission-card,
body.fe-frontend-force-dark .fe-submission-card {
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.08);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-submission-card:hover,
body.fe-frontend-force-dark .fe-submission-card:hover {
  border-color: rgba(122, 163, 255, 0.4);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.35);
}
html[data-theme="dark"] .fe-submission-modal-panel,
body.fe-frontend-force-dark .fe-submission-modal-panel {
  background: var(--fe-dm-surface);
  border-color: var(--fe-dm-border);
  color: var(--fe-dm-text);
}
html[data-theme="dark"] .fe-submission-page-blurb,
body.fe-frontend-force-dark .fe-submission-page-blurb {
  color: var(--fe-dm-text-muted);
}
html[data-theme="dark"] .fe-submission-form input,
html[data-theme="dark"] .fe-submission-form textarea,
body.fe-frontend-force-dark .fe-submission-form input,
body.fe-frontend-force-dark .fe-submission-form textarea {
  background: var(--fe-dm-surface);
  color: var(--fe-dm-text);
  border-color: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-submission-form fieldset,
body.fe-frontend-force-dark .fe-submission-form fieldset {
  border-color: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-submission-modal-head h2,
html[data-theme="dark"] .fe-submission-page-title,
body.fe-frontend-force-dark .fe-submission-modal-head h2,
body.fe-frontend-force-dark .fe-submission-page-title {
  color: var(--fe-dm-text-strong);
}
html[data-theme="dark"] .fe-submission-modal-head,
html[data-theme="dark"] .fe-submission-modal-foot,
body.fe-frontend-force-dark .fe-submission-modal-head,
body.fe-frontend-force-dark .fe-submission-modal-foot {
  background: var(--fe-dm-surface);
  border-color: var(--fe-dm-border);
}
html[data-theme="dark"] .fe-submission-modal-body,
body.fe-frontend-force-dark .fe-submission-modal-body {
  /* Disable the light-mode scroll-edge shadow stack — its baked
     white stops would smear across the dark body. */
  background: var(--fe-dm-surface);
}

/* ===== Content pages (Pages admin → /<slug>) ─ Showcase layout =====
   Elegant single-column rendering for admin-authored content pages.
   Lives under .fe-pp ("page page") so the existing block-render CSS
   for Zoom Tech and other rich-content surfaces stays untouched.

   Top / bottom / horizontal padding all read CSS custom properties
   the template stamps inline so admins can dial the page to
   flush-against-header (0px) or extra-airy (200px+) per-page from
   the page-settings card. Fallback values match the original
   hard-coded numbers so pages saved before the controls existed
   render unchanged. */
.fe-pp {
  padding-top: var(--fe-pp-pad-top, 80px);
  padding-bottom: var(--fe-pp-pad-bottom, 96px);
  padding-left: 0;
  padding-right: 0;
}
/* Shell width is now driven by inline style on the element (boxed
   uses page.max_width, full uses no max-width with viewport-%
   padding) so each page can pick its own page-wide width formatting
   from the admin. The auto margins keep boxed mode centered when
   the inline max-width applies. Horizontal padding reads from the
   same per-page custom property as the article above so a single
   `pad_x = 0` setting drops the gutter for a true edge-to-edge
   layout (e.g. hero blocks that need to bleed).

   Desktop fallback chains through the design-token container padding
   so a site-wide adjustment in Site → Design flows to every page that
   hasn't explicitly set its own pad_x. Mobile is a separate, single
   global knob — the per-page pad_x is overridden at ≤768px so the
   admin can dial mobile gutter from one place. Edge-to-edge full-width
   pages still set their own inline padding which beats this rule. */
.fe-pp-shell {
  margin-left: auto; margin-right: auto;
  padding-left: var(--fe-pp-pad-x, 16px);
  padding-right: var(--fe-pp-pad-x, 16px);
}
/* Full-width pages drive their own gutter through `--fe-pp-pad-x-full`
   (set inline by the page template from `page.full_padding_pct`). */
.fe-pp-w-full .fe-pp-shell {
  padding-left: var(--fe-pp-pad-x-full, 0);
  padding-right: var(--fe-pp-pad-x-full, 0);
}

/* Hero: title + lead + image + CTA, all centered, generous whitespace. */
.fe-pp-hero { text-align: center; }
.fe-pp-title {
  font-family: var(--fe-font-display);
  font-size: clamp(2.25rem, 4.6vw, 3.75rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-weight: 700;
  margin: 0 0 22px;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-lead {
  font-size: clamp(1.0625rem, 1.4vw, 1.25rem);
  line-height: 1.55;
  color: var(--fe-ink-muted);
  max-width: 60ch;
  margin: 0 auto 28px;
}
.fe-pp-lead p { margin: 0; }
.fe-pp-lead p + p { margin-top: 14px; }

/* Image figure — minimal CSS. No baked-in border-radius, shadow,
   text-align, or margin so a freshly-dropped image renders exactly
   like its block data says: unstyled. The renderer applies inline
   styles ONLY when the admin chose alignment, width, or caption
   colour/size in the block settings. The figcaption keeps its
   font-size + colour because those are reading defaults, not
   visual chrome — admins can override via the caption fields. */
.fe-pp-figure { margin: 0; }
.fe-pp-figure img {
  max-width: 100%;
  height: auto;
  /* Image-block corner roundness rides through the `--img-radius`
     custom property the renderer sets (see `frontend/page.html` +
     `_blocks.html`). Falls back to 0 (sharp corners) when the admin
     hasn't set a radius. The mobile media query below halves the
     value so a hero image with 32px corners on desktop softens to
     16px on phones — the proportional scale keeps the radius
     visually balanced as the image itself shrinks. */
  border-radius: var(--img-radius, 0);
}
@media (max-width: 560px) {
  .fe-pp-figure img,
  .block-image img {
    border-radius: calc(var(--img-radius, 0) / 2);
  }
}
.fe-pp-figure figcaption {
  /* Caption gets minimal reading-comfort defaults — small spacing
     above so it doesn't kiss the image, font-size + colour come
     from the block's caption_color / caption_size settings via
     inline style. No baked-in muted colour; the figcaption inherits
     the surrounding text colour unless the admin explicitly
     overrides via the Caption colour field in the image settings. */
  margin-top: 8px;
}

/* Single-link paragraph → centered CTA row. */
.fe-pp-cta-row {
  margin: 32px 0;
  text-align: center;
}
.fe-pp-cta-row .fe-btn { display: inline-flex; }

/* Icon block — wrapper handles alignment via flex; the icon's font-size
   sits on the wrapper's inline style so the `.icon` em-sized SVG scales
   from there. Each alignment variant maps to flex justify-content. */
.block-icon {
  display: flex;
  width: 100%;
}
.block-icon--left   { justify-content: flex-start; }
.block-icon--center { justify-content: center; }
.block-icon--right  { justify-content: flex-end; }
.block-icon a { color: inherit; line-height: 0; }

/* Slightly larger CTA pill for hero affordances. */
.fe-page .fe-btn-lg,
.frontend-body .fe-btn-lg {
  padding: 16px 36px;
  font-size: 1.0625rem;
  letter-spacing: 0.01em;
}

/* Subsequent sections — minimal margin-only separation. The
   previous chrome (top-border + padding-top) imposed visual styling
   the admin couldn't see or override from any edit modal; now
   sections just stack with consistent vertical breathing room.
   Container blocks inside a section can opt INTO chrome (border,
   shadow, padding) via their settings panel. */
/* Spacing between sections within a page. Reads a per-page custom
   property so the admin can collapse the gap to 0 when blocks
   should sit flush, or open it up for breathing room. First
   section keeps margin-top: 0 so the article's pad-top isn't
   doubled by a leading section gap. */
.fe-pp-section { margin-top: var(--fe-pp-section-gap, 32px); }
.fe-pp-section:first-of-type { margin-top: 0; }
.fe-pp-section-title {
  /* Section titles only render when the admin set `sec.title`, and
     they keep theme-derived font + colour (typography, not chrome).
     `text-align: center` was forced visual styling and has been
     removed — the title now follows its parent's text alignment so
     the admin's choice (left-aligned page, centered page, etc.)
     wins. */
  font-family: var(--fe-font-display);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
  font-weight: 700;
  margin: 0 0 20px;
  color: var(--fe-color-text, var(--fe-ink));
}

/* Body prose inside sections. Centered for the hero, left-aligned
   for content sections — picked up via CSS specificity. */
.fe-pp-prose {
  font-size: 1.03125rem;
  line-height: 1.65;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-prose p { margin: 0 0 16px; }
.fe-pp-prose p:last-child { margin-bottom: 0; }
/* Paragraphs in sections used to auto-center + clamp to 640px and
   text-align: center, which silently styled every body paragraph.
   Removed — paragraph blocks now render with their natural width
   and text alignment, so the typography panel's Alignment toggle
   is the only thing that decides text-align. */
/* (Was: `.fe-pp-section .fe-pp-prose:has(+ .fe-pp-steps) { margin-bottom: 28px }`
   — auto-applied 28px gap before a steps list. Removed; container
   `gap` is the only source of inter-block spacing now.) */

/* Numbered step list — each item is a soft card with a circular
   numeral on the left. Used for conduct policies, FAQs, anything
   list-shaped that benefits from visual rhythm. */
.fe-pp-steps {
  list-style: none;
  /* No top/bottom margin so the cards sit flush against neighbour
     blocks; spacing inside the parent container is owned by the
     container's `gap` setting (the admin's source of truth). */
  margin: 0;
  padding: 0;
  display: grid;
  gap: 14px;
  counter-reset: fe-pp-step;
}
.fe-pp-step {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 18px;
  align-items: start;
  padding: 18px 22px;
  background: var(--fe-panel-soft);
  /* Border style mirrors `.fe-meeting-card` so list cards visually
     pair with meeting cards across the site — accent-coloured 1 px
     stroke + 16 px radius + the same hover lift / shadow recipe. */
  border: 1px solid var(--fe-accent);
  border-radius: 16px;
  transition: transform 200ms ease, box-shadow 200ms ease;
}
.fe-pp-step:hover {
  /* Hover behaviour gates on per-card CSS variables so the admin
     can disable the lift via the "Hover lift" toggle in the card
     style panel. Default (no override): lift -2px + soft shadow.
     `--fe-pp-step-hover-lift: 0` sets the translation to 0;
     `--fe-pp-step-hover-shadow: none` swaps the shadow to none. */
  transform: translateY(calc(var(--fe-pp-step-hover-lift, 1) * -2px));
  box-shadow: var(--fe-pp-step-hover-shadow, 0 8px 28px rgba(15, 23, 42, 0.10));
}
.fe-pp-step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  background: var(--fe-color-btn-primary-bg, var(--fe-ink));
  color: var(--fe-color-btn-primary-text, #fff);
  font-weight: 700;
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
  margin-top: 2px;
}
.fe-pp-step-body {
  font-size: 1rem;
  line-height: 1.55;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-step-body p { margin: 0; }
.fe-pp-step-body p + p { margin-top: 8px; }

/* No baked-in spacing between a steps block and the next prose
   block — the parent container's `gap` setting owns inter-block
   spacing. Same for tonal/sizing chrome that used to apply by
   adjacency: kept the visual neutral so the admin's typography +
   container settings are the only source of layout truth. */

/* ── List block — alternative display styles ────────────────────
   Each style is opt-in via the block's `display_style` field
   (the "Display style" dropdown in the list editor). The default
   plain ul/ol with marker style stays unchanged when no style is
   selected. */

/* Checklist + arrows: a marker glyph in a brand-tinted circle, plus
   the item text. Items stack vertically with consistent rhythm. */
.fe-pp-list-checklist,
.fe-pp-list-arrows {
  list-style: none; padding: 0; margin: 16px 0;
  display: flex; flex-direction: column; gap: 10px;
}
.fe-pp-list-checklist > li,
.fe-pp-list-arrows > li {
  display: grid; grid-template-columns: 28px 1fr;
  gap: 12px; align-items: start;
}
.fe-pp-list-checklist .fe-pp-list-mark,
.fe-pp-list-arrows .fe-pp-list-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border-radius: 999px;
  font-size: 0.875rem; font-weight: 700; line-height: 1;
  margin-top: 2px;
}
.fe-pp-list-checklist .fe-pp-list-mark {
  background: color-mix(in srgb, var(--fe-accent, #5164ff) 18%, transparent);
  color: var(--fe-accent, #5164ff);
}
.fe-pp-list-arrows .fe-pp-list-mark {
  background: var(--fe-panel-soft, transparent);
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-list-checklist .fe-pp-list-text,
.fe-pp-list-arrows .fe-pp-list-text {
  display: inline-block;
  font-size: 1rem; line-height: 1.55;
}
.fe-pp-list-checklist .fe-pp-list-text > p,
.fe-pp-list-arrows .fe-pp-list-text > p { margin: 0; }
.fe-pp-list-checklist .fe-pp-list-text > p + p,
.fe-pp-list-arrows .fe-pp-list-text > p + p { margin-top: 6px; }

/* Inline pills — each item becomes a rounded chip; the list flows
   horizontally and wraps. Useful for tag clouds, feature highlights,
   "what we offer" rows. */
.fe-pp-list-pills {
  list-style: none; padding: 0; margin: 16px 0;
  display: flex; flex-wrap: wrap; gap: 8px;
}
.fe-pp-list-pills > li {
  padding: 6px 14px; border-radius: 999px;
  background: var(--fe-panel-soft, color-mix(in srgb, currentColor 6%, transparent));
  border: 1px solid var(--fe-border, color-mix(in srgb, currentColor 12%, transparent));
  font-size: 0.9375rem; line-height: 1.4;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-list-pills > li > p { margin: 0; display: inline; }

/* Dark-mode parity for the alternate styles. */
html[data-theme="dark"] .fe-pp-list-arrows .fe-pp-list-mark {
  background: color-mix(in srgb, var(--fe-color-surface, #131a2c) 70%, #1a2236);
  color: var(--fe-dm-text, #cbd5e1);
}
/* Dark-mode pills — read from the dark-mode surface / border / text
   tokens directly. The previous `color-mix(var(--fe-color-surface) …)`
   mix used a LIGHT-mode token in dark mode (the admin's chosen surface
   colour is usually near-white), so the resulting fill landed on a
   light grey, not dark. Paired with `body.fe-frontend-force-dark` so
   the admin-locked dark-mode toggle (e.g. dark sections on a light
   page) gets the same treatment as system-driven dark mode. */
html[data-theme="dark"] .fe-pp-list-pills > li,
body.fe-frontend-force-dark .fe-pp-list-pills > li {
  background: var(--fe-dm-surface, #131a33);
  border-color: var(--fe-dm-border, #1f2a44);
  color: var(--fe-dm-text, #e2e8f0);
}

/* Dark mode parity — surface, border, and shadow tokens already shift
   via the design system; tune the step-card contrast so it reads
   cleanly against the dark page surface. */
html[data-theme="dark"] .fe-pp-step {
  /* Dark-mode parity with `.fe-meeting-card` — same surface + border
     tokens so list cards and meeting cards read as the same family
     under either theme. */
  background: #131a33;
  border-color: #1f2a44;
}
html[data-theme="dark"] .fe-pp-step:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}
@media (max-width: 640px) {
  /* Honor the admin's `--fe-pp-pad-top` / `--fe-pp-pad-bottom` on
     mobile too — the previous hardcoded `padding: 56px 0 72px`
     defeated the inline vars (`pad_top: 0` from the page settings
     stayed pinned at 56px on mobile). Tighter fallback values
     (56 / 72) preserve the historic mobile-default look for pages
     that haven't customised the vars. Horizontal padding stays 0 so
     `.fe-pp-shell`'s own `--fe-pp-pad-x` is the only gutter driver.
     Section gap is intentionally NOT overridden here — the desktop
     rule `.fe-pp-section { margin-top: var(--fe-pp-section-gap, 32px) }`
     carries through, so admins who set `section_gap: 0` see zero
     margin on mobile too (the previous hardcoded `24px` defeated
     that). */
  .fe-pp {
    padding-top: var(--fe-pp-pad-top, 56px);
    padding-bottom: var(--fe-pp-pad-bottom, 72px);
  }
  .fe-pp-step { grid-template-columns: 36px 1fr; gap: 14px; padding: 14px 16px; }
  .fe-pp-step-num { width: 30px; height: 30px; font-size: 0.875rem; }
}

/* ===== Pages: two-column hero (split) ===== */
.fe-pp-split .fe-pp-shell { max-width: 1100px; }
.fe-pp-hero-split {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: 1rem;
  align-items: center;
  text-align: left;
  margin-bottom: 12px;
}
.fe-pp-hero-media { display: flex; flex-direction: column; gap: 24px; }
.fe-pp-hero-split .fe-pp-title {
  text-align: left;
  margin: 0;
}
.fe-pp-hero-split .fe-pp-figure { margin: 0; }
.fe-pp-hero-split .fe-pp-figure img {
  border-radius: 22px;
}

/* Copy column — soft card with breathing-room padding. Children stack
   with a uniform 1rem gap (paragraph → CTA → supporting copy, etc.)
   so spacing reads consistently regardless of which blocks the admin
   added. Per-element margins are zeroed because the flex `gap`
   handles the rhythm — using both would double-count the spacing. */
.fe-pp-hero-card {
  background: var(--fe-color-surface, var(--fe-panel));
  border: 1px solid var(--fe-border);
  border-radius: 22px;
  padding: 40px 44px;
  box-shadow: var(--fe-shadow, 0 12px 40px rgba(15, 23, 42, 0.10));
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.fe-pp-hero-card > * { margin: 0; }
.fe-pp-hero-card .fe-pp-lead {
  text-align: left;
  max-width: none;
  color: var(--fe-color-text, var(--fe-ink));
}
.fe-pp-hero-card .fe-pp-prose { text-align: left; }
.fe-pp-hero-card .fe-pp-prose p { margin: 0; }
.fe-pp-hero-card .fe-pp-prose p + p { margin-top: 0.75rem; }
.fe-pp-hero-card .fe-pp-cta-row {
  text-align: left;
  margin: 0;
}

/* Section after a split hero gets its own breathing room — the hero
   card already provides closure so we don't need a heavy divider. */
.fe-pp-split .fe-pp-section:first-of-type {
  margin-top: 80px;
}

@media (max-width: 880px) {
  .fe-pp-hero-split {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .fe-pp-hero-card { padding: 28px 24px; border-radius: 18px; }
  .fe-pp-hero-split .fe-pp-figure img { border-radius: 16px; }
}

/* ===== Pages: background image surface =====
   The article carries an inline background-image when an admin uploads
   one; these rules tune the legibility scaffolding (slight overlay in
   tile mode so dense patterns don't fight the prose, none for cover so
   photographs stay vivid). */
.fe-pp-has-bg { position: relative; isolation: isolate; }
.fe-pp-bg-tile {
  /* Faint white veil over busy patterns so body copy stays readable
     when no hero card is present. The two-column variant places its
     prose inside .fe-pp-hero-card which already opaque-fills the
     reading surface, so the veil there is barely necessary — it just
     softens the edges. */
  background-color: color-mix(in srgb, var(--fe-color-surface, #fff) 12%, transparent);
}
html[data-theme="dark"] .fe-pp-bg-tile {
  background-color: color-mix(in srgb, var(--fe-color-surface, #0e111b) 25%, transparent);
}

/* Pages with a background image used to auto-wrap every section in
   white-card chrome (background + border + radius + padding) so the
   content sat on a "readable surface". That imposed visual styling
   the admin couldn't see or override from any block edit modal —
   removed entirely. If the admin wants a card around their content
   on a busy bg, they wrap it in a Container block and set the
   background / border / radius via the container's settings panel. */

/* (admin page-bg + page-typo styles live in app.css) */

/* ===== Pages: per-page typography overrides + alignment ===== */
.fe-pp-title {
  font-family: var(--fe-pp-heading-font, var(--fe-font-display));
  color: var(--fe-pp-heading-color, var(--fe-color-text, var(--fe-ink)));
}
.fe-pp-section-title {
  font-family: var(--fe-pp-subheading-font, var(--fe-font-display));
  color: var(--fe-pp-subheading-color, var(--fe-color-text, var(--fe-ink)));
}
.fe-pp-hero-card .fe-pp-lead { color: var(--fe-color-text, var(--fe-ink)); }

/* Alignment — `.fe-pp-halign-auto` is the default (centered single-col,
   left-aligned in split). The explicit modes win regardless of layout. */
.fe-pp-halign-left  .fe-pp-title,
.fe-pp-halign-left  .fe-pp-section-title { text-align: left; }
.fe-pp-halign-center .fe-pp-title,
.fe-pp-halign-center .fe-pp-section-title { text-align: center; }
.fe-pp-halign-right .fe-pp-title,
.fe-pp-halign-right .fe-pp-section-title { text-align: right; }
/* Explicit alignment overrides the split default of left-aligned. */
.fe-pp-split.fe-pp-halign-center .fe-pp-title,
.fe-pp-split.fe-pp-halign-right  .fe-pp-title { text-align: inherit; }
.fe-pp-split.fe-pp-halign-center .fe-pp-hero-media { text-align: center; }
.fe-pp-split.fe-pp-halign-right  .fe-pp-hero-media { text-align: right; }

/* ===== Pages: dark-mode bg dim + full dark-mode audit ===== */

/* Dim overlay sits between the bg image and the page content. The
   variable defaults to transparent; dark mode raises it to a soft
   black wash so admin-uploaded photos / patterns don't blow out the
   dark theme's contrast. The `> *` rule promotes the .fe-container
   into the same stacking context as the overlay so content paints
   above it (without this, ::after's z-index 0 sits above non-
   positioned children's auto z-index in painting order). */
.fe-pp-has-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--fe-pp-bg-dim, transparent);
  pointer-events: none;
  z-index: 0;
}
.fe-pp-has-bg > * { position: relative; z-index: 1; }

html[data-theme="dark"] .fe-pp-has-bg.fe-pp-bg-cover { --fe-pp-bg-dim: rgba(0, 0, 0, 0.55); }
html[data-theme="dark"] .fe-pp-has-bg.fe-pp-bg-tile  { --fe-pp-bg-dim: rgba(0, 0, 0, 0.45); }

/* Page chrome dark-mode — surfaces, borders, text. The base tokens
   already shift via the design system; these rules tighten the
   specific contrast for the showcase elements. */
html[data-theme="dark"] .fe-pp-title { color: var(--fe-pp-heading-color, var(--fe-dm-text-strong, #f1f5f9)); }
html[data-theme="dark"] .fe-pp-section-title { color: var(--fe-pp-subheading-color, var(--fe-dm-text-strong, #f1f5f9)); }
html[data-theme="dark"] .fe-pp-prose { color: var(--fe-dm-text, #cbd5e1); }
html[data-theme="dark"] .fe-pp-prose a { color: var(--fe-dm-link, var(--fe-color-link)); }
html[data-theme="dark"] .fe-pp-lead { color: var(--fe-dm-text-soft, #94a3b8); }

html[data-theme="dark"] .fe-pp-hero-card {
  background: var(--fe-dm-surface, #131a2c);
  border-color: var(--fe-dm-border, #1f2a40);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-pp-hero-card .fe-pp-lead,
html[data-theme="dark"] .fe-pp-hero-card .fe-pp-prose {
  color: var(--fe-dm-text, #cbd5e1);
}

html[data-theme="dark"] .fe-pp-step {
  /* Mirrors `.fe-meeting-card` dark-mode for the same family look. */
  background: #131a33;
  border-color: #1f2a44;
}
html[data-theme="dark"] .fe-pp-step:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-pp-step-body { color: var(--fe-dm-text, #cbd5e1); }
html[data-theme="dark"] .fe-pp-step-body a { color: var(--fe-dm-link, var(--fe-color-link)); }
html[data-theme="dark"] .fe-pp-step-num {
  background: color-mix(in srgb, var(--fe-accent, #5164ff) 70%, #fff 30%);
  color: var(--fe-dm-bg, #0e111b);
}

/* Sections no longer carry a top hairline divider, so the previous
   dark-mode border-color override is unnecessary. */

/* Dark-mode tokens — define defaults so the rules above resolve even
   when the surrounding theme hasn't set them. Existing themes that
   already define `--fe-dm-*` continue to win via cascade. */
html[data-theme="dark"] .fe-pp {
  --fe-dm-bg:           #0e111b;
  --fe-dm-surface:      #131a2c;
  --fe-dm-border:       #1f2a40;
  --fe-dm-text:         #cbd5e1;
  --fe-dm-text-strong:  #f1f5f9;
  --fe-dm-text-soft:    #94a3b8;
  --fe-dm-link:         #93c5fd;
}

/* ===== Admin: typography editor ===== */
/* (page-typo admin styles moved to app.css) */

/* ===== Button block (admin-authored CTAs) ===== */
.block-button { margin: 22px 0; }
.block-button-align-left   { text-align: left; }
.block-button-align-center { text-align: center; }
.block-button-align-right  { text-align: right; }
.block-button .fe-btn { display: inline-flex; }

/* Inside the showcase hero card the button alignment still wins — and
   spacing tightens slightly so a button doesn't read as an after-
   thought when it's the last node before supporting copy. */
.fe-pp-hero-card .block-button { margin: 18px 0 0; }

/* ===== Custom-styled button =====
   Each `.fe-btn-custom` consumes CSS custom properties set inline by
   the renderer. Every variable has a sane fallback so a partly-
   configured button still reads — the admin can just set the bits
   that matter and inherit the rest. The hover recipe layers two
   transitions: colour state via the var fallback, lift via the
   tokenised `--fe-btn-hover-transform`. The shadow sits at the
   primary-button intensity when `--btn-shadow-color` is set,
   otherwise nothing. */
.fe-btn-custom,
.fe-page .fe-btn-custom,
.frontend-body .fe-btn-custom {
  background: var(--btn-bg, var(--fe-color-btn-primary-bg, var(--fe-ink)));
  color: var(--btn-text, var(--fe-color-btn-primary-text, #fff));
  border: 1px solid var(--btn-border, transparent);
  box-shadow: 0 8px 20px var(--btn-shadow-color, transparent);
  transition: transform 150ms ease, box-shadow 150ms ease,
              background 150ms ease, color 150ms ease,
              border-color 150ms ease;
}
.fe-btn-custom:hover,
.fe-page .fe-btn-custom:hover,
.frontend-body .fe-btn-custom:hover {
  background: var(--btn-hover-bg, var(--btn-bg, var(--fe-color-btn-primary-bg, var(--fe-ink))));
  color: var(--btn-hover-text, var(--btn-text, var(--fe-color-btn-primary-text, #fff)));
  border-color: var(--btn-hover-border, var(--btn-border, transparent));
  transform: var(--fe-btn-hover-transform, translateY(-1px));
  box-shadow: 0 12px 28px var(--btn-shadow-color, transparent);
}

/* (admin button-block editor styles live in app.css alongside .be-*) */

/* ===== Container block (admin-authored nested layout primitive) =====
   Inline styles handle layout / spacing / colours / borders / radius
   per instance. Hover behaviour is driven by CSS custom properties so
   each container can carry its own hover recipe inline:
     --block-cont-hover-bg / -border / -shadow / -transform
   Each var defaults to its non-hover counterpart so an unset hover
   variable simply means "no change on hover". */
.block-container {
  /* Reset every container-scoped CSS variable to its default at the
     start of each container instance. CSS custom properties inherit
     by default — without this reset, a nested container would pick
     up the OUTER container's `--block-cont-shadow` / `-bg` / etc.
     through inheritance even when the inner container has none of
     those properties configured. The reset means each container
     starts from a clean slate; only the inline-stamped variables
     on its own element take effect.

     Inline styles on this element still win over this rule
     (specificity 1,0,0,0 vs 0,0,1,0), so a container that DOES
     declare its own shadow / bg / border / hover values keeps them
     intact. */
  --block-cont-padding: 1rem;
  --block-cont-padding-mobile: initial;
  --block-cont-bg: transparent;
  --block-cont-border-color: currentColor;
  --block-cont-border-width: 0;
  --block-cont-shadow: none;
  --block-cont-grid-cols: initial;
  --block-cont-flex-dir: initial;
  --block-cont-flex-dir-mobile: initial;
  --block-cont-hover-bg: initial;
  --block-cont-hover-border: initial;
  --block-cont-hover-border-width: initial;
  --block-cont-hover-shadow: initial;
  --block-cont-hover-transform: initial;
  /* Smooth transitions across every property the hover recipe might
     touch — kept short so hover feels responsive even on touch.
     `border-width` is included so the "border appears on hover"
     pattern (rest 0 → hover N) animates rather than jump-cutting. */
  transition: background-color 180ms ease,
              border-color 180ms ease,
              border-width 180ms ease,
              box-shadow 180ms ease,
              transform 180ms ease;
  /* Box-sizing parity so padding doesn't blow out the container's
     intended max-width. */
  box-sizing: border-box;
  /* Padding reads from a CSS custom property the renderer stamps on
     the instance (rather than `padding:` directly inline) so the
     mobile media query can override it without an inline-style war. */
  padding: var(--block-cont-padding, 1rem);
  /* Background colour, border colour, and box-shadow all read from
     inline-set variables so the `:hover` rules below can swap them.
     Setting these as direct inline properties would have inline
     specificity (1,0,0,0) and beat the hover rules' (0,0,2,0). The
     variable-driven approach keeps inline as variable declarations
     only, so the hover rules win on the cascade as soon as the admin
     ticks one of the hover overrides. */
  background-color: var(--block-cont-bg, transparent);
  border-color: var(--block-cont-border-color, currentColor);
  border-width: var(--block-cont-border-width, 0);
  box-shadow: var(--block-cont-shadow, none);
}
/* Grid + flex containers consume the layout values from CSS custom
   properties the renderer stamps on each instance, so the mobile
   media query below can collapse multi-column layouts to a single
   column without fighting inline-style specificity. */
.block-container--grid {
  grid-template-columns: var(--block-cont-grid-cols, repeat(2, 1fr));
}
.block-container--flex {
  flex-direction: var(--block-cont-flex-dir, column);
}
/* Phone breakpoint — any grid container with two or more columns
   collapses to a single column so two-column page-builder blocks
   stack on phones rather than squeezing into 50%-wide strips.
   Flex containers default to stacking column on phones (matches the
   pre-mobile-override behaviour), but `--block-cont-flex-dir-mobile`
   wins when the admin set Mobile direction in the editor — letting
   them keep a row, reverse it, or pick column-reverse to surface the
   bottom child first on phones. Padding likewise prefers
   `--block-cont-padding-mobile` and falls back to the desktop value
   when the admin left mobile padding blank. The 720px ceiling
   matches the rest of the public site's mobile rules. */
@media (max-width: 720px) {
  .block-container--grid {
    grid-template-columns: 1fr;
  }
  .block-container--flex {
    flex-direction: var(--block-cont-flex-dir-mobile, column);
  }
  .block-container {
    padding: var(--block-cont-padding-mobile, var(--block-cont-padding, 1rem));
  }
}
/* Hover behaviour is opt-in per property — each `--hover-*` class is
   only added by the renderer when the admin actually configured that
   property, so an unset hover doesn't clobber the resting style. */
.block-container--hover-bg:hover     { background-color: var(--block-cont-hover-bg); }
.block-container--hover-border:hover { border-color: var(--block-cont-hover-border); }
.block-container--hover-border-width:hover { border-width: var(--block-cont-hover-border-width); }
.block-container--hover-shadow:hover { box-shadow: var(--block-cont-hover-shadow, var(--block-cont-shadow, none)); }
.block-container--hover-lift:hover   { transform: translateY(-2px); }
/* Direct children of a container honour its gap, but stand-alone
   blocks (paragraphs, headings, lists, etc.) usually carry their
   own top/bottom margins. Zero those when nested inside a container
   so the gap drives the rhythm. */
.block-container > .block { margin: 0; }
.block-container > .block-paragraph p:first-child { margin-top: 0; }
.block-container > .block-paragraph p:last-child  { margin-bottom: 0; }

/* Showcase wrapper — make sure containers inside the showcase layout
   inherit reasonable defaults (text colour, prose colour) without
   fighting per-section tinting. */
.fe-pp-container { color: inherit; }

/* ── Per-block dark-mode color overrides ────────────────────────
   Each block-level color picker (typography colour, container bg,
   container border) supports a "Same / Auto / Manual" dark-mode
   mode toggle. When the admin chooses Auto or Manual, the renderer
   stamps a CSS custom property on the element's inline style:
     • --tsp-color-dm   → text colour for typography fields
     • --tsp-bg-dm      → background-color for containers
     • --tsp-border-dm  → border-color for containers
   These rules apply those properties under the dark theme. The
   `[style*="--tsp-…"]` attribute selector keeps the rule narrow —
   it only matches elements that actually carry the variable. The
   `!important` is needed because inline styles otherwise win over
   any cascade rule on the same property. */
html[data-theme="dark"] [style*="--tsp-color-dm"] {
  color: var(--tsp-color-dm) !important;
}
html[data-theme="dark"] [style*="--tsp-bg-dm"] {
  background-color: var(--tsp-bg-dm) !important;
}
html[data-theme="dark"] [style*="--tsp-border-dm"] {
  border-color: var(--tsp-border-dm) !important;
}

/* Per-template page-background dark-mode swap. The frontend-templates
   "Override page background color" picker emits `--tpl-bg` (light) and
   optionally `--tpl-bg-dark` (auto-derived or manual). In dark theme,
   swap the live `--tpl-bg` value to the dark variant so every consumer
   of `var(--tpl-bg, …)` — section backgrounds, card surfaces — flips
   in one step. */
html[data-theme="dark"] [style*="--tpl-bg-dark"] {
  --tpl-bg: var(--tpl-bg-dark) !important;
}

/* ── Wiki sidebar block (toc_sidebar) ────────────────────────────
   On-page TOC built from heading blocks. Sits in any column of any
   container layout; sticks to the viewport top when `is-sticky` is
   set (top offset comes from inline style). Hides on narrow
   viewports because the sticky behaviour fights mobile scroll. */
.fe-pp-toc {
  background: var(--fe-panel, #fff);
  border: 1px solid var(--fe-border, rgba(15, 23, 42, 0.1));
  border-radius: 12px;
  padding: 18px 20px;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--fe-color-text, var(--fe-ink));
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.04);
}
.fe-pp-toc.is-sticky {
  position: sticky;
  align-self: start;
}
.fe-pp-toc-title {
  font-weight: 700; font-size: 0.75rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--fe-ink-muted);
  margin-bottom: 10px;
}
.fe-pp-toc-list { list-style: none; padding: 0; margin: 0;
  display: flex; flex-direction: column; gap: 4px; }
.fe-pp-toc-item a {
  display: block; padding: 4px 0;
  color: var(--fe-color-text, var(--fe-ink));
  text-decoration: none; border-left: 2px solid transparent;
  padding-left: 10px; margin-left: -12px;
  transition: color 120ms ease, border-color 120ms ease;
}
.fe-pp-toc-item a:hover {
  color: var(--fe-brand, var(--access));
  border-left-color: var(--fe-brand, var(--access));
}
.fe-pp-toc-l3 { padding-left: 12px; }
.fe-pp-toc-l4 { padding-left: 24px; }
.fe-pp-toc-l5, .fe-pp-toc-l6 { padding-left: 36px; }
.fe-pp-toc-l3 a, .fe-pp-toc-l4 a, .fe-pp-toc-l5 a, .fe-pp-toc-l6 a {
  font-size: 0.875rem; color: var(--fe-ink-muted);
}
.fe-pp-toc-empty { margin: 0; }
@media (max-width: 880px) {
  .fe-pp-toc.is-sticky { position: static; }
}

/* ── Contact us page ────────────────────────────────────────────
   Two-column layout: an aside on the left (eyebrow + heading +
   blurb + admin-tunable markdown intro + auto-populated PIC
   contact channels) and a glassy form card on the right. The
   surrounding section paints three soft animated blobs behind
   everything so the page reads as "contact" without a stock hero
   image. Form chrome reuses .fe-submission-form so the inputs
   stay visually consistent with /submissionform; the wrapper
   styling is unique to contact. */
.fe-contact-section {
  /* `fe-dynbg-host` on the section element provides position +
     isolation so the dynbg paints below content. The aurora-blobs
     dynbg owns the animated backdrop now; this rule only handles
     the section-level chrome (clipping + vertical padding). */
  overflow: hidden;
  padding: clamp(3.5rem, 6vw, 6rem) 0;
}
.fe-contact-container { position: relative; z-index: 1; }
.fe-contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 4vw, 3.5rem);
  align-items: start;
}
@media (max-width: 880px) {
  .fe-contact-grid { grid-template-columns: 1fr; gap: 2rem; }
}

.fe-contact-aside { padding-top: 0.5rem; }
.fe-contact-eyebrow {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fe-accent, #0b5cff);
  background: color-mix(in srgb, var(--fe-accent, #0b5cff) 12%, transparent);
  padding: 6px 12px;
  border-radius: 999px;
  margin: 0 0 1.25rem;
}
.fe-contact-title {
  margin: 0 0 0.75rem;
  font-size: clamp(2rem, 1.4rem + 2.4vw, 3.25rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--fe-color-text, #0a0a0a);
}
.fe-contact-blurb {
  margin: 0 0 1.5rem;
  font-size: 1.0625rem;
  line-height: 1.55;
  color: var(--fe-color-text-muted, #475569);
  max-width: 36ch;
}
.fe-contact-intro {
  margin: 0 0 1.5rem;
  color: var(--fe-color-text, #0a0a0a);
  line-height: 1.6;
}
.fe-contact-intro p { margin: 0 0 0.75rem; }
.fe-contact-intro p:last-child { margin-bottom: 0; }

.fe-contact-channels {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
}
.fe-contact-channel {
  display: flex;
  align-items: center;
  gap: 0.875rem;
}
.fe-contact-channel-icon {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--fe-accent, #0b5cff) 12%, transparent);
  color: var(--fe-accent, #0b5cff);
}
.fe-contact-channel-icon .icon { width: 18px; height: 18px; }
.fe-contact-channel-body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.fe-contact-channel-label {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fe-color-text-muted, #64748b);
}
.fe-contact-channel-value {
  font-size: 1rem;
  color: var(--fe-color-text, #0a0a0a);
  font-weight: 500;
  word-break: break-word;
}
.fe-contact-channel-link {
  text-decoration: none;
  transition: color 120ms ease;
}
.fe-contact-channel-link:hover {
  color: var(--fe-accent, #0b5cff);
}

.fe-contact-form-wrap {
  position: relative;
}
/* Form card — same hover lift + brand-accent border the submission
   card uses, but with a generous padding spec and a slight tilt
   into the foreground so the two-column hero feels balanced. */
.fe-contact-card {
  background: var(--fe-color-surface, #fff);
  border: 1px solid var(--fe-color-border, rgba(0, 0, 0, 0.08));
  border-radius: 20px;
  padding: clamp(1.75rem, 3vw, 2.5rem);
  box-shadow: 0 18px 48px rgba(15, 23, 42, 0.08),
              0 1px 2px rgba(15, 23, 42, 0.04);
  transition: transform 200ms ease, box-shadow 200ms ease;
}
.fe-contact-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.12),
              0 1px 2px rgba(15, 23, 42, 0.04);
}

/* The form itself — slightly tighter gap than /submissionform since
   the contact form has fewer rows and benefits from the breath. */
.fe-contact-form { gap: 1rem; }
.fe-contact-form fieldset {
  border: 1px solid var(--fe-color-border, rgba(0, 0, 0, 0.08));
  border-radius: 12px;
}
.fe-contact-form textarea { min-height: 8rem; }

.fe-contact-honeypot {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.fe-contact-flashes { margin: 0 0 1.25rem; }
.fe-contact-actions { justify-content: stretch; }
.fe-contact-actions .fe-submission-submit {
  width: 100%;
  padding: 14px 20px;
  font-size: 1rem;
}
.fe-contact-fineprint { text-align: center; }

/* Dark-mode swap — section bg falls to the page-bg token, card swaps
   to the dark surface token, blurred blobs lift in opacity so they
   still register against the darker backdrop. Mirrors the
   submission-section dark-mode treatment. */
html[data-theme="dark"] .fe-contact-card,
body.fe-frontend-force-dark .fe-contact-card {
  background: var(--fe-color-surface, #111827);
  border-color: var(--fe-color-border, rgba(255, 255, 255, 0.08));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-contact-card:hover,
body.fe-frontend-force-dark .fe-contact-card:hover {
  box-shadow: 0 32px 80px rgba(0, 0, 0, 0.55);
}
html[data-theme="dark"] .fe-contact-blurb,
body.fe-frontend-force-dark .fe-contact-blurb {
  color: var(--fe-color-text-muted, #94a3b8);
}
html[data-theme="dark"] .fe-contact-channel-icon,
body.fe-frontend-force-dark .fe-contact-channel-icon {
  background: color-mix(in srgb, var(--fe-accent, #60a5fa) 22%, transparent);
}
html[data-theme="dark"] .fe-contact-eyebrow,
body.fe-frontend-force-dark .fe-contact-eyebrow {
  background: color-mix(in srgb, var(--fe-accent, #60a5fa) 24%, transparent);
}

/* ── Dynamic backgrounds library ────────────────────────────────
   Eight CSS-only backdrops that any frontend surface (page, hero,
   section, container block, etc.) can opt into via the .fe-dynbg
   wrapper rendered by frontend/_dynbg.html.

   Common contract:
     • .fe-dynbg sits absolutely-positioned inside a positioned host
       and stretches to its bounds. Hosts that want a dynbg must set
       `position: relative; overflow: hidden;` and place .fe-dynbg as
       the first child so it paints under everything else.
     • Every preset draws using brand tokens (--fe-accent, --fe-color-
       bg, --fe-color-surface) so the same key produces a brand-
       coloured backdrop on every install.
     • Animations honour prefers-reduced-motion globally below.
     • Dark-mode rules stack at the bottom of each preset's section.
*/
.fe-dynbg {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.fe-dynbg > * {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
/* When a dynbg lives inside a host that also lays out content, the
   host's children need a positive z-index OR a stacking context so
   they paint above the dynbg. The :has() selector promotes the host
   so consumers don't have to remember; falls back gracefully on
   browsers without :has() (the host class can opt in manually). */
:where(.fe-dynbg-host) { position: relative; isolation: isolate; }
:where(.fe-dynbg-host) > :not(.fe-dynbg) { position: relative; z-index: 1; }
/* The :where rule above promotes every direct child (other than the
   `.fe-dynbg` layer itself) to `position: relative` so content paints
   above the dynbg layer. That's correct for content blocks but BREAKS
   absolutely-positioned overlays that also live as direct children —
   the hero's particle <canvas>, background <video>, and frosty blob
   container all want `position: absolute; inset: 0` to overlay the
   section without taking flex flow space. When forced into flow,
   `initLoginFX`'s `parent.getBoundingClientRect()` resize loop
   inflates the canvas, which inflates the parent, which fires
   ResizeObserver, which inflates the canvas — and the hero balloons
   well past 100vh. Re-assert absolute positioning for those
   overlays specifically. Higher specificity than the :where rule
   above so we always win the cascade. */
.fe-dynbg-host > .fe-hero-particles,
.fe-dynbg-host > .fe-hero-video,
.fe-dynbg-host > .fe-hero-bg {
  position: absolute;
  z-index: 0;
}
/* Particle canvas in particular should sit ABOVE the dynbg layer
   but below content. Keep its z-index: 1 from `.fe-hero-particles`. */
.fe-dynbg-host > .fe-hero-particles {
  z-index: 1;
}

/* Page-level dynbg wrapper: when an admin sets a dynamic background
   on a list/index page (meetings list, events list, etc.), the inner
   page-section's flat soft-panel background would paint on top of the
   dynbg and hide it. Force the immediate child sections of the page
   wrapper to render transparent so the chosen backdrop shows through.
   Cards inside those sections keep their own backgrounds (they're
   nested deeper, so the rule doesn't reach them). */
.fe-page-dynbg-host.fe-dynbg-host > .fe-section,
.fe-page-dynbg-host.fe-dynbg-host > .fe-mlist,
.fe-page-dynbg-host.fe-dynbg-host > section {
  background: transparent;
}

@media (prefers-reduced-motion: reduce) {
  .fe-dynbg * { animation: none !important; }
}

/* Admin opt-out: when a `.fe-dynbg-no-anim-marker` element is
   present inside the host (emitted by `_dynbg_apply.html` when the
   admin checked "Freeze movement"), freeze every animated child in
   the base dynbg and the overlay layer.

   Uses `:has()` so each surface's host doesn't need a separate
   server-side class stamp — the marker span travels with the
   apply-partial include and the CSS finds it from any host. The
   explicit `.fe-dynbg-no-anim` class below is kept as a fallback
   so future server-side stamping (or :has()-less browsers) can
   still opt out by adding it directly. Same `!important` recipe
   as the reduced-motion gate. */
.fe-dynbg-host:has(> .fe-dynbg-no-anim-marker) .fe-dynbg > *,
.fe-dynbg-host:has(> .fe-dynbg-no-anim-marker) .fe-dynbg-overlay > *,
.fe-dynbg-no-anim .fe-dynbg > *,
.fe-dynbg-no-anim .fe-dynbg-overlay > * {
  animation: none !important;
}

/* ── aurora-blobs ─── 3 brand-tinted blurred circles drifting.
   Each blob's colour resolves through `--fe-dynbg-c1/c2/c3` first
   (set by the per-surface custom-colour stamp on the dynbg-host)
   and falls through to the brand accent when no override is set. */
.fe-dynbg-aurora-blobs {
  --_db-c1: var(--fe-dynbg-c1, color-mix(in srgb, var(--fe-accent, #0b5cff) 60%, #16c2ba));
  --_db-c2: var(--fe-dynbg-c2, color-mix(in srgb, var(--fe-accent, #0b5cff) 50%, #8b5cf6));
  --_db-c3: var(--fe-dynbg-c3, color-mix(in srgb, var(--fe-accent, #0b5cff) 35%, #fb923c));
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f8fafc));
  filter: none;
}
/* Each blob's position + size reads from `--fe-dynbg-blob-<slot>-*`
   custom properties. When `randomize_positions` is on, the host
   stamps fresh values per render; otherwise the var() fallbacks
   reproduce the original hand-tuned layout. */
.fe-dynbg-aurora-blobs .fe-dynbg-blob {
  width: var(--_db-blob-size, 380px);
  height: var(--_db-blob-size, 380px);
  border-radius: 50%;
  filter: blur(60px);
  opacity: 0.55;
  animation: feDynbgBlobDrift 18s ease-in-out infinite;
  inset: auto;
}
.fe-dynbg-aurora-blobs .fe-dynbg-blob-a {
  --_db-blob-size: var(--fe-dynbg-blob-a-size, 380px);
  top: var(--fe-dynbg-blob-a-top, -120px);
  left: var(--fe-dynbg-blob-a-left, -80px);
  bottom: var(--fe-dynbg-blob-a-bottom, auto);
  right: var(--fe-dynbg-blob-a-right, auto);
  background: var(--_db-c1);
}
.fe-dynbg-aurora-blobs .fe-dynbg-blob-b {
  --_db-blob-size: var(--fe-dynbg-blob-b-size, 380px);
  top: var(--fe-dynbg-blob-b-top, auto);
  left: var(--fe-dynbg-blob-b-left, auto);
  bottom: var(--fe-dynbg-blob-b-bottom, -160px);
  right: var(--fe-dynbg-blob-b-right, -100px);
  background: var(--_db-c2);
  animation-delay: -6s; animation-duration: 22s;
}
.fe-dynbg-aurora-blobs .fe-dynbg-blob-c {
  --_db-blob-size: var(--fe-dynbg-blob-c-size, 240px);
  top: var(--fe-dynbg-blob-c-top, 30%);
  left: var(--fe-dynbg-blob-c-left, auto);
  bottom: var(--fe-dynbg-blob-c-bottom, auto);
  right: var(--fe-dynbg-blob-c-right, 35%);
  background: var(--_db-c3);
  opacity: 0.45;
  animation-delay: -12s; animation-duration: 26s;
}
@keyframes feDynbgBlobDrift {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(40px, -25px) scale(1.05); }
  66%      { transform: translate(-30px, 30px) scale(0.95); }
}
html[data-theme="dark"] .fe-dynbg-aurora-blobs,
body.fe-frontend-force-dark .fe-dynbg-aurora-blobs {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-aurora-blobs .fe-dynbg-blob,
body.fe-frontend-force-dark .fe-dynbg-aurora-blobs .fe-dynbg-blob {
  opacity: 0.65;
}

/* ── mesh-gradient ─── three overlapping conic gradients, no motion */
.fe-dynbg-mesh-gradient {
  --_db-c1: var(--fe-dynbg-c1, var(--fe-accent, #0b5cff));
  --_db-c2: var(--fe-dynbg-c2, color-mix(in srgb, var(--fe-accent, #0b5cff) 60%, #8b5cf6));
  --_db-c3: var(--fe-dynbg-c3, color-mix(in srgb, var(--fe-accent, #0b5cff) 40%, #fb923c));
  background: var(--fe-color-bg, #f8fafc);
}
.fe-dynbg-mesh-gradient .fe-dynbg-mesh {
  inset: -10%;
  filter: blur(80px);
  opacity: 0.5;
}
.fe-dynbg-mesh-gradient .fe-dynbg-mesh-a {
  background: conic-gradient(
    from var(--fe-dynbg-mesh-a-angle, 0deg)
      at var(--fe-dynbg-mesh-a-x, 25%) var(--fe-dynbg-mesh-a-y, 30%),
    var(--_db-c1) 0deg,
    transparent 90deg,
    transparent 270deg,
    var(--_db-c2) 360deg);
}
.fe-dynbg-mesh-gradient .fe-dynbg-mesh-b {
  background: conic-gradient(
    from var(--fe-dynbg-mesh-b-angle, 180deg)
      at var(--fe-dynbg-mesh-b-x, 75%) var(--fe-dynbg-mesh-b-y, 60%),
    var(--_db-c2) 0deg,
    transparent 120deg,
    transparent 240deg,
    var(--_db-c3) 360deg);
  opacity: 0.4;
}
.fe-dynbg-mesh-gradient .fe-dynbg-mesh-c {
  background: radial-gradient(ellipse 60% 40%
    at var(--fe-dynbg-mesh-c-x, 50%) var(--fe-dynbg-mesh-c-y, 50%),
    color-mix(in srgb, var(--_db-c1) 25%, transparent),
    transparent 70%);
  opacity: 0.7;
}
html[data-theme="dark"] .fe-dynbg-mesh-gradient,
body.fe-frontend-force-dark .fe-dynbg-mesh-gradient {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-mesh-gradient .fe-dynbg-mesh,
body.fe-frontend-force-dark .fe-dynbg-mesh-gradient .fe-dynbg-mesh {
  opacity: 0.4;
}

/* ── aurora-bands ─── wide angled bands sweeping vertically */
.fe-dynbg-aurora-bands {
  --_db-c1: var(--fe-dynbg-c1, color-mix(in srgb, var(--fe-accent, #0b5cff) 70%, #16c2ba));
  --_db-c2: var(--fe-dynbg-c2, color-mix(in srgb, var(--fe-accent, #0b5cff) 55%, #8b5cf6));
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f8fafc));
}
.fe-dynbg-aurora-bands .fe-dynbg-band {
  inset: -20%;
  filter: blur(70px);
  opacity: 0.55;
  animation: feDynbgBandDrift 24s ease-in-out infinite alternate;
}
.fe-dynbg-aurora-bands .fe-dynbg-band-a {
  background: linear-gradient(var(--fe-dynbg-band-a-angle, 115deg),
    transparent 30%,
    var(--_db-c1) 50%,
    transparent 70%);
}
.fe-dynbg-aurora-bands .fe-dynbg-band-b {
  background: linear-gradient(var(--fe-dynbg-band-b-angle, 75deg),
    transparent 25%,
    var(--_db-c2) 55%,
    transparent 80%);
  animation-delay: -8s; animation-duration: 30s;
  opacity: 0.45;
}
@keyframes feDynbgBandDrift {
  0%   { transform: translate(-5%, -3%); }
  100% { transform: translate(5%, 3%); }
}
html[data-theme="dark"] .fe-dynbg-aurora-bands,
body.fe-frontend-force-dark .fe-dynbg-aurora-bands {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-aurora-bands .fe-dynbg-band,
body.fe-frontend-force-dark .fe-dynbg-aurora-bands .fe-dynbg-band {
  opacity: 0.7;
}

/* ── starfield ─── three layered dot fields with twinkle */
.fe-dynbg-starfield {
  --_db-c1: var(--fe-dynbg-c1, var(--fe-accent, #0b5cff));
  background:
    radial-gradient(ellipse 120% 80% at 50% 0%,
      color-mix(in srgb, var(--_db-c1) 18%, transparent),
      transparent 70%),
    var(--fe-color-bg, #07101e);
}
.fe-dynbg-starfield .fe-dynbg-stars {
  inset: 0;
  background-repeat: repeat;
  animation: feDynbgStarsTwinkle 6s ease-in-out infinite;
}
.fe-dynbg-starfield .fe-dynbg-stars-a {
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, rgba(255,255,255,0.85), transparent 50%),
    radial-gradient(1px   1px   at 27% 62%, rgba(255,255,255,0.7),  transparent 50%),
    radial-gradient(1.2px 1.2px at 56% 24%, rgba(255,255,255,0.8),  transparent 50%),
    radial-gradient(1px   1px   at 73% 78%, rgba(255,255,255,0.6),  transparent 50%),
    radial-gradient(1.5px 1.5px at 88% 41%, rgba(255,255,255,0.75), transparent 50%);
  background-size: 400px 400px;
}
.fe-dynbg-starfield .fe-dynbg-stars-b {
  background-image:
    radial-gradient(2px 2px at 8% 70%, color-mix(in srgb, var(--_db-c1) 70%, #fff), transparent 50%),
    radial-gradient(1px 1px at 45% 35%, rgba(255,255,255,0.5),       transparent 50%),
    radial-gradient(1px 1px at 65% 90%, rgba(255,255,255,0.55),      transparent 50%);
  background-size: 600px 600px;
  animation-delay: -2s;
}
.fe-dynbg-starfield .fe-dynbg-stars-c {
  background-image:
    radial-gradient(0.8px 0.8px at 22% 38%, rgba(255,255,255,0.4), transparent 50%),
    radial-gradient(0.8px 0.8px at 52% 80%, rgba(255,255,255,0.4), transparent 50%),
    radial-gradient(0.8px 0.8px at 84% 14%, rgba(255,255,255,0.5), transparent 50%);
  background-size: 240px 240px;
  animation-delay: -4s;
  animation-duration: 8s;
}
@keyframes feDynbgStarsTwinkle {
  0%, 100% { opacity: 0.85; }
  50%      { opacity: 0.4;  }
}
/* Light-mode swap: dim the dots and tint the bg toward the brand
   so the same recipe reads as "soft sky" rather than "deep space". */
html[data-theme="light"] .fe-dynbg-starfield,
body:not(.fe-frontend-force-dark) html:not([data-theme="dark"]) .fe-dynbg-starfield {
  background:
    radial-gradient(ellipse 120% 80% at 50% 0%,
      color-mix(in srgb, var(--_db-c1) 12%, transparent),
      transparent 70%),
    var(--fe-color-surface-alt, #eef2f7);
}
html[data-theme="light"] .fe-dynbg-starfield .fe-dynbg-stars,
body:not(.fe-frontend-force-dark) html:not([data-theme="dark"]) .fe-dynbg-starfield .fe-dynbg-stars {
  filter: invert(1) hue-rotate(180deg);
  opacity: 0.35;
}

/* ── dotted-grid ─── subtle dot lattice */
.fe-dynbg-dotted-grid {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f8fafc));
}
.fe-dynbg-dotted-grid .fe-dynbg-dots {
  background-image: radial-gradient(
    circle at 1px 1px,
    color-mix(in srgb, var(--fe-color-text, #0a0a0a) 22%, transparent) 1px,
    transparent 0);
  background-size: 18px 18px;
  background-position: 0 0;
  opacity: 0.5;
}
html[data-theme="dark"] .fe-dynbg-dotted-grid,
body.fe-frontend-force-dark .fe-dynbg-dotted-grid {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-dotted-grid .fe-dynbg-dots,
body.fe-frontend-force-dark .fe-dynbg-dotted-grid .fe-dynbg-dots {
  background-image: radial-gradient(
    circle at 1px 1px,
    color-mix(in srgb, #ffffff 28%, transparent) 1px,
    transparent 0);
  opacity: 0.3;
}

/* ── diagonal-lines ─── soft diagonal stripes */
.fe-dynbg-diagonal-lines {
  background: var(--fe-color-surface-alt, var(--fe-panel-soft, #f8fafc));
}
.fe-dynbg-diagonal-lines .fe-dynbg-lines {
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--fe-color-text, #0a0a0a) 7%, transparent) 0 1px,
    transparent 1px 14px);
}
html[data-theme="dark"] .fe-dynbg-diagonal-lines,
body.fe-frontend-force-dark .fe-dynbg-diagonal-lines {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-diagonal-lines .fe-dynbg-lines,
body.fe-frontend-force-dark .fe-dynbg-diagonal-lines .fe-dynbg-lines {
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, #ffffff 9%, transparent) 0 1px,
    transparent 1px 14px);
}

/* ── noise-paper ─── fractal-noise grain */
.fe-dynbg-noise-paper {
  background: var(--fe-color-surface, #fdfbf7);
}
.fe-dynbg-noise-paper .fe-dynbg-noise {
  width: 100%; height: 100%;
  opacity: 0.55;
  mix-blend-mode: multiply;
}
html[data-theme="dark"] .fe-dynbg-noise-paper,
body.fe-frontend-force-dark .fe-dynbg-noise-paper {
  background: var(--fe-color-bg, #14181f);
}
html[data-theme="dark"] .fe-dynbg-noise-paper .fe-dynbg-noise,
body.fe-frontend-force-dark .fe-dynbg-noise-paper .fe-dynbg-noise {
  opacity: 0.4;
  mix-blend-mode: screen;
}

/* ── spotlight ─── two corner-anchored radial glows */
.fe-dynbg-spotlight {
  --_db-c1: var(--fe-dynbg-c1, var(--fe-accent, #0b5cff));
  --_db-c2: var(--fe-dynbg-c2, color-mix(in srgb, var(--fe-accent, #0b5cff) 55%, #8b5cf6));
  background: var(--fe-color-bg, #f8fafc);
}
.fe-dynbg-spotlight .fe-dynbg-spot {
  filter: blur(90px);
  opacity: 0.55;
}
.fe-dynbg-spotlight .fe-dynbg-spot-a {
  top:    var(--fe-dynbg-spot-a-top, -25%);
  bottom: var(--fe-dynbg-spot-a-bottom, auto);
  left:   var(--fe-dynbg-spot-a-left, -15%);
  right:  var(--fe-dynbg-spot-a-right, auto);
  width:  var(--fe-dynbg-spot-a-w, 70%);
  height: var(--fe-dynbg-spot-a-h, 90%);
  background: radial-gradient(circle at 50% 50%,
    color-mix(in srgb, var(--_db-c1) 70%, transparent),
    transparent 70%);
  inset: auto;
}
.fe-dynbg-spotlight .fe-dynbg-spot-b {
  top:    var(--fe-dynbg-spot-b-top, auto);
  bottom: var(--fe-dynbg-spot-b-bottom, -25%);
  left:   var(--fe-dynbg-spot-b-left, auto);
  right:  var(--fe-dynbg-spot-b-right, -15%);
  width:  var(--fe-dynbg-spot-b-w, 70%);
  height: var(--fe-dynbg-spot-b-h, 90%);
  background: radial-gradient(circle at 50% 50%,
    var(--_db-c2),
    transparent 70%);
  opacity: 0.4;
  inset: auto;
}
html[data-theme="dark"] .fe-dynbg-spotlight,
body.fe-frontend-force-dark .fe-dynbg-spotlight {
  background: var(--fe-color-bg, #0b1220);
}
html[data-theme="dark"] .fe-dynbg-spotlight .fe-dynbg-spot,
body.fe-frontend-force-dark .fe-dynbg-spotlight .fe-dynbg-spot {
  opacity: 0.7;
}

/* ── Dynamic-background overlays ────────────────────────────────
   Independent layer that paints above the base dynbg AND above
   page content (with `pointer-events: none` so clicks pass through).
   Each overlay textures the surface without intercepting input;
   they compose with any base dynbg or stand on their own. */
.fe-dynbg-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Above content (host promotes content to z-index: 1) but well
     below modals (z-index: 100+). The texture rides on top of the
     hero / cards / typography to give the whole surface a tactile
     finish, the way the viibeware project's body::before does
     site-wide. The `--bg-only` modifier below opts out of this
     placement and tucks the overlay between the base dynbg and
     content, so cards / typography paint without the texture. */
  z-index: 10;
}
.fe-dynbg-overlay--bg-only {
  /* z-index: 0 keeps the overlay above the base .fe-dynbg (also at
     z-index: auto / 0) while content (forced to z-index: 1 by the
     host's :where rule) paints on top — content stays clean while
     the bg gets textured. DOM order matters here: this overlay must
     render AFTER the base dynbg and BEFORE content for the stacking
     to work; _dynbg_apply.html follows that order. */
  z-index: 0;
}
/* ── noise-grain ─── viibeware's recipe: SVG fractal-noise tiled.
   Data-URL (vs inline <svg>) because inline SVGs without an explicit
   viewBox sometimes render blank when sized 100%/100% inside a
   positioned parent — particularly under <body> on Safari. The
   data-URL has its own viewBox so the SVG paints reliably and tiles
   via the default `background-repeat: repeat`. The 0.03 alpha on the
   rect inside the SVG matches the viibeware recipe exactly; CSS-side
   opacity stays at 1 so the texture reads at the same intensity it
   does on viibeware. */
.fe-dynbg-overlay-noise-grain {
  background-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.03'/%3E%3C/svg%3E");
}
html[data-theme="dark"] .fe-dynbg-overlay-noise-grain,
body.fe-frontend-force-dark .fe-dynbg-overlay-noise-grain {
  /* Same grain reads quieter against dark backgrounds, so flip to a
     blend mode that lifts the texture against deep tones rather than
     bumping the alpha (which would look noisy on light theme). */
  mix-blend-mode: screen;
  opacity: 0.6;
}

/* ── scanlines ─── 2px horizontal stripes at 1.5% alpha */
.fe-dynbg-overlay-scanlines {
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(0, 0, 0, 0.015) 2px,
    rgba(0, 0, 0, 0.015) 4px
  );
}
html[data-theme="dark"] .fe-dynbg-overlay-scanlines,
body.fe-frontend-force-dark .fe-dynbg-overlay-scanlines {
  background: repeating-linear-gradient(
    0deg,
    transparent,
    transparent 2px,
    rgba(255, 255, 255, 0.025) 2px,
    rgba(255, 255, 255, 0.025) 4px
  );
}

/* ── linen ─── two-direction stripe weave */
.fe-dynbg-overlay-linen {
  background:
    repeating-linear-gradient(0deg,
      transparent 0 1px,
      rgba(0, 0, 0, 0.025) 1px 2px),
    repeating-linear-gradient(90deg,
      transparent 0 1px,
      rgba(0, 0, 0, 0.025) 1px 2px);
}
html[data-theme="dark"] .fe-dynbg-overlay-linen,
body.fe-frontend-force-dark .fe-dynbg-overlay-linen {
  background:
    repeating-linear-gradient(0deg,
      transparent 0 1px,
      rgba(255, 255, 255, 0.04) 1px 2px),
    repeating-linear-gradient(90deg,
      transparent 0 1px,
      rgba(255, 255, 255, 0.04) 1px 2px);
}

/* ── vignette ─── radial darken from corners */
.fe-dynbg-overlay-vignette {
  background: radial-gradient(ellipse at center,
    transparent 40%,
    rgba(0, 0, 0, 0.18) 100%);
}
html[data-theme="dark"] .fe-dynbg-overlay-vignette,
body.fe-frontend-force-dark .fe-dynbg-overlay-vignette {
  background: radial-gradient(ellipse at center,
    transparent 40%,
    rgba(0, 0, 0, 0.45) 100%);
}

/* ── crosshatch ─── two diagonal stripe sets */
.fe-dynbg-overlay-crosshatch {
  background:
    repeating-linear-gradient(45deg,
      transparent 0 4px,
      rgba(0, 0, 0, 0.04) 4px 5px),
    repeating-linear-gradient(-45deg,
      transparent 0 4px,
      rgba(0, 0, 0, 0.04) 4px 5px);
}
html[data-theme="dark"] .fe-dynbg-overlay-crosshatch,
body.fe-frontend-force-dark .fe-dynbg-overlay-crosshatch {
  background:
    repeating-linear-gradient(45deg,
      transparent 0 4px,
      rgba(255, 255, 255, 0.05) 4px 5px),
    repeating-linear-gradient(-45deg,
      transparent 0 4px,
      rgba(255, 255, 255, 0.05) 4px 5px);
}

/* ── dot-weave ─── tiny dot lattice for halftone newsprint feel */
.fe-dynbg-overlay-dot-weave {
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(0, 0, 0, 0.18) 1px,
    transparent 0);
  background-size: 6px 6px;
  opacity: 0.4;
}
html[data-theme="dark"] .fe-dynbg-overlay-dot-weave,
body.fe-frontend-force-dark .fe-dynbg-overlay-dot-weave {
  background-image: radial-gradient(
    circle at 1px 1px,
    rgba(255, 255, 255, 0.22) 1px,
    transparent 0);
  opacity: 0.3;
}

/* All overlays respect reduced-motion just like the base dynbg
   layer (none of them animate today, but keep the gate uniform so
   future animated overlays inherit the rule for free). */
@media (prefers-reduced-motion: reduce) {
  .fe-dynbg-overlay { animation: none !important; }
  .fe-dynbg-overlay * { animation: none !important; }
}

/* ── Site Index ─────────────────────────────────────────────────
   Auto-populated /siteindex page. Two layouts share the same
   chrome — a centered page heading + blurb on top, then either
   sectioned groups (`grouped.html`) or a single A-Z list
   (`alphabet.html`). The kind-chip in the alphabet layout uses
   the same brand-tinted pill recipe as the schedule pills on
   meeting cards so it reads as part of the same family. */
.fe-site-index {
  padding: clamp(2.5rem, 5vw, 4rem) 0;
}
.fe-site-index-shell {
  max-width: 920px;
  margin: 0 auto;
}
.fe-site-index-head {
  text-align: center;
  margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
}
.fe-site-index-title {
  margin: 0 0 0.5rem;
  font-family: var(--fe-font-heading, inherit);
  font-size: clamp(1.8rem, 1.4rem + 1.6vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--fe-color-text, inherit);
}
.fe-site-index-blurb {
  margin: 0 auto;
  max-width: 56ch;
  color: var(--fe-color-text-muted, var(--fe-ink-muted, #475569));
  font-size: 1.05rem;
  line-height: 1.5;
}
.fe-site-index-empty {
  text-align: center;
  padding: 3rem 1rem;
}

/* ── Grouped layout ─────────────────────────────────────────── */
.fe-site-index-group {
  margin: 0 0 clamp(1.75rem, 3vw, 2.5rem);
}
.fe-site-index-group-head {
  display: flex; align-items: baseline;
  gap: 10px;
  margin: 0 0 0.75rem;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--fe-color-border, rgba(0, 0, 0, 0.08));
}
.fe-site-index-eyebrow {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fe-accent, #0b5cff);
}
.fe-site-index-count {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--fe-color-text-muted, var(--fe-ink-muted, #64748b));
  background: color-mix(in srgb, var(--fe-accent, #0b5cff) 8%, transparent);
  padding: 1px 8px;
  border-radius: 999px;
  min-width: 1.25rem;
  text-align: center;
}

/* ── Row chrome (shared by both layouts) ────────────────────── */
.fe-site-index-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.fe-site-index-list-flat {
  /* Alphabet layout: a touch more breathing room since it's a
     single column with no group headings to pace the scroll. */
  gap: 6px;
}
.fe-site-index-row {
  margin: 0;
}
.fe-site-index-row-link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 10px;
  text-decoration: none;
  color: var(--fe-color-text, inherit);
  border: 1px solid transparent;
  transition: background-color 120ms ease, border-color 120ms ease,
              transform 120ms ease;
}
.fe-site-index-row-link:hover {
  background: color-mix(in srgb, var(--fe-accent, #0b5cff) 6%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #0b5cff) 20%, transparent);
  transform: translateX(2px);
}
.fe-site-index-row-link:focus-visible {
  outline: 2px solid var(--fe-accent, #0b5cff);
  outline-offset: 2px;
}
.fe-site-index-row-chip {
  /* Same recipe as `.fe-mlist-card-sched` — soft brand tint pill,
     compact font, 12px text. */
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fe-accent, #0b5cff);
  background: color-mix(in srgb, var(--fe-accent, #0b5cff) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--fe-accent, #0b5cff) 22%, transparent);
  white-space: nowrap;
}
.fe-site-index-row-text {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.fe-site-index-row-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--fe-color-text, inherit);
  word-break: break-word;
}
.fe-site-index-row-sub {
  margin-top: 1px;
  line-height: 1.35;
  word-break: break-word;
}
.fe-site-index-row-arrow {
  flex: 0 0 auto;
  color: var(--fe-color-text-muted, #94a3b8);
  font-size: 1.1rem;
  transition: transform 120ms ease, color 120ms ease;
}
.fe-site-index-row-link:hover .fe-site-index-row-arrow {
  color: var(--fe-accent, #0b5cff);
  transform: translateX(2px);
}

@media (max-width: 540px) {
  .fe-site-index-row-link { padding: 10px 12px; }
  .fe-site-index-row-chip { font-size: 0.7rem; padding: 2px 8px; }
}

/* Dark-mode tweaks — mirror the meetings-list card recipe so the
   index reads as one piece with the rest of the dark-themed site. */
html[data-theme="dark"] .fe-site-index-blurb,
body.fe-frontend-force-dark .fe-site-index-blurb {
  color: #94a3b8;
}
html[data-theme="dark"] .fe-site-index-group-head,
body.fe-frontend-force-dark .fe-site-index-group-head {
  border-bottom-color: #1f2a44;
}
html[data-theme="dark"] .fe-site-index-row-link:hover,
body.fe-frontend-force-dark .fe-site-index-row-link:hover {
  background: color-mix(in srgb, var(--fe-accent, #60a5fa) 14%, transparent);
  border-color: color-mix(in srgb, var(--fe-accent, #60a5fa) 35%, transparent);
}

/* ── Blog block on frontend custom Pages ──────────────────────── */
.block-blog { margin: 1.4rem 0; }
.block-blog-head { margin-bottom: 1.4rem; }
.block-blog-title {
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-weight: 700; font-size: 1.6rem; line-height: 1.15;
  margin: 0 0 .35rem; letter-spacing: -.01em;
}
.block-blog-sub {
  margin: 0; color: var(--fe-color-muted, #6b7280);
  font-size: 1rem; line-height: 1.55;
}
.block-blog-empty {
  padding: 1rem; background: var(--fe-color-surface, #f3f4f6);
  border-radius: 8px; text-align: center;
}
.block-blog-cats { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: .55rem; }
.block-blog-cat-chip {
  --blog-cat-color: var(--fe-color-accent, #2563eb);
  color: var(--blog-cat-color);
  background: color-mix(in srgb, var(--blog-cat-color) 14%, transparent);
  padding: 2px 9px; border-radius: 6px;
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
}
.block-blog-more { margin-top: 1.4rem; text-align: right; }
.block-blog-more-link {
  color: var(--fe-color-accent, #2563eb); font-weight: 600; text-decoration: none;
  font-size: .92rem;
}
.block-blog-more-link:hover { text-decoration: underline; }

.block-blog-cards { display: grid; }
.block-blog-card {
  background: var(--fe-color-bg, #fff);
  border: 1px solid var(--fe-color-border, #e5e7eb);
  border-radius: 12px; overflow: hidden;
  transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.block-blog-card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--fe-color-accent, #2563eb) 30%, var(--fe-color-border, #e5e7eb));
  box-shadow: 0 12px 30px -12px rgba(15,23,42,.15);
}
.block-blog-card.is-featured { box-shadow: inset 3px 0 0 var(--fe-color-accent, #2563eb); }
.block-blog-card-link { display: block; color: inherit; text-decoration: none; }
.block-blog-card-art { aspect-ratio: 16 / 10; overflow: hidden; }
.block-blog-card-art img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }
.block-blog-card:hover .block-blog-card-art img { transform: scale(1.04); }
.block-blog-card-body { padding: 1rem 1.2rem 1.2rem; }
.block-blog-card-title {
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-weight: 700; font-size: 1.1rem; line-height: 1.25;
  margin: 0 0 .5rem;
}
.block-blog-card-summary { margin: 0 0 .7rem; color: var(--fe-color-muted, #6b7280); font-size: .9rem; line-height: 1.55; }
.block-blog-card-meta { display: flex; flex-wrap: wrap; gap: .5rem; color: var(--fe-color-muted, #6b7280); font-size: .78rem; }
.block-blog-card-meta > span + span::before { content: "·"; margin-right: .5rem; opacity: .55; }
@media (max-width: 720px) { .block-blog-cards { grid-template-columns: 1fr !important; } }

.block-blog-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.1rem; }
.block-blog-list-item {
  background: var(--fe-color-bg, #fff);
  border: 1px solid var(--fe-color-border, #e5e7eb);
  border-radius: 10px; overflow: hidden;
  transition: border-color .2s ease, box-shadow .2s ease;
}
.block-blog-list-item:hover {
  border-color: color-mix(in srgb, var(--fe-color-accent, #2563eb) 30%, var(--fe-color-border, #e5e7eb));
  box-shadow: 0 6px 18px -8px rgba(15,23,42,.15);
}
.block-blog-list-link {
  display: grid; grid-template-columns: 180px 1fr; gap: 1rem;
  color: inherit; text-decoration: none; align-items: center;
}
.block-blog-list-link:not(:has(.block-blog-list-art)) { grid-template-columns: 1fr; padding: 1rem 1.2rem; }
@media (max-width: 600px) { .block-blog-list-link { grid-template-columns: 1fr; } }
.block-blog-list-art { aspect-ratio: 4 / 3; overflow: hidden; }
.block-blog-list-art img { width: 100%; height: 100%; object-fit: cover; }
.block-blog-list-body { padding: 1rem 1.2rem; }
.block-blog-list-title {
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-weight: 700; font-size: 1.1rem; line-height: 1.25;
  margin: 0 0 .4rem;
}
.block-blog-list-summary { margin: 0 0 .55rem; color: var(--fe-color-muted, #6b7280); font-size: .92rem; line-height: 1.55; }
.block-blog-list-meta { display: flex; flex-wrap: wrap; gap: .5rem; color: var(--fe-color-muted, #6b7280); font-size: .78rem; }
.block-blog-list-meta > span + span::before { content: "·"; margin-right: .5rem; opacity: .55; }

.block-blog-headlines { list-style: none; padding: 0; margin: 0; }
.block-blog-headline {
  border-bottom: 1px solid var(--fe-color-border, #e5e7eb);
  padding: .9rem 0;
}
.block-blog-headline:last-child { border-bottom: 0; }
.block-blog-headline a { color: inherit; text-decoration: none; display: flex; flex-direction: column; gap: 4px; }
.block-blog-headline-date {
  text-transform: uppercase; letter-spacing: .12em;
  font-size: .72rem; color: var(--fe-color-muted, #6b7280);
  font-weight: 600;
}
.block-blog-headline-title {
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-weight: 700; font-size: 1.18rem; line-height: 1.25;
}
.block-blog-headline a:hover .block-blog-headline-title { color: var(--fe-color-accent, #2563eb); }
.block-blog-headline-summary { color: var(--fe-color-muted, #6b7280); font-size: .95rem; line-height: 1.5; }

/* ── Fellowships Index (/fellowships) ───────────────────────────────
   Two layouts share these styles:
     - Sidebar (default): inherits .fe-mlist-* shell + rail + pill list
       from the meetings/library/archive lineage.
     - Compact: top filter strip + dense single-column rows.
   Card + chip + sort styles below sit on top of the shared shell so
   admins can pick either layout without losing the chips / hover. */
.fe-mlist-sort {
  display: flex; align-items: center; gap: 8px;
  margin: 12px 0;
}
.fe-mlist-sort-label {
  font-size: .82rem; color: var(--fe-color-muted, #6b7280);
  text-transform: uppercase; letter-spacing: .08em; font-weight: 600;
}
.fe-mlist-sort select {
  flex: 1; min-width: 0;
  padding: 6px 10px; border-radius: 8px;
  border: 1px solid var(--fe-color-border, #e5e7eb);
  background: var(--fe-color-surface, #fff);
  color: inherit; font-size: .92rem;
  font-family: var(--fe-font-body, inherit);
}

.fe-fellowships-stack { display: grid; gap: 14px; }

.fe-fellowships-card {
  /* Primary card tokens — same recipe as the other list-page cards.
     Hover wired via the shared primary aggregator below. */
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 14px;
  padding: 16px 18px;
  display: grid; gap: 8px;
}
.fe-fellowships-card-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  justify-content: space-between;
}
.fe-fellowships-card-title {
  margin: 0;
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-size: 1.12rem; line-height: 1.25; font-weight: 700;
}
.fe-fellowships-card-chip {
  display: inline-flex; align-items: center;
  padding: 3px 10px;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase;
  border-radius: 999px;
  background: color-mix(in srgb, var(--fe-color-accent, #2563eb) 14%, transparent);
  color: var(--fe-color-accent, #2563eb);
  border: 1px solid color-mix(in srgb, var(--fe-color-accent, #2563eb) 30%, transparent);
}
.fe-fellowships-card-chip--virtual {
  background: color-mix(in srgb, #10b981 14%, transparent);
  color: #047857;
  border-color: color-mix(in srgb, #10b981 30%, transparent);
}
.fe-fellowships-card-where {
  margin: 0; font-size: .92rem;
}
.fe-fellowships-card-cta { margin: 0; }
.fe-fellowships-card-cta a {
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 600; color: var(--fe-color-accent, #2563eb);
  text-decoration: none;
}
.fe-fellowships-card-cta a:hover { text-decoration: underline; }
.fe-fellowships-card-cta a svg { width: 14px; height: 14px; }

/* Dark-mode — fellowships card surface reads from the Primary card
   background (dark) design token so admins control the swatch from
   Settings → Design rather than wrangling per-component overrides.
   Border + hover shadow follow the lineage the rest of the dark-mode
   cards use (transparent-ish white border, deeper shadow on lift).
   The sort <select> rides the same token so the dropdown sits on the
   same surface as the cards instead of the page background. */
html[data-theme="dark"] .fe-fellowships-card {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: rgba(255, 255, 255, 0.08);
}
html[data-theme="dark"] .fe-fellowships-card:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}
html[data-theme="dark"] .fe-mlist-sort select {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: rgba(255, 255, 255, 0.10);
  color: var(--fe-color-text, #e2e8f0);
}
/* Dropdown <option> elements are rendered by the browser chrome on
   most platforms and don't inherit the select's bg in dark mode, so
   set it explicitly so the open list isn't a blinding white pane. */
html[data-theme="dark"] .fe-mlist-sort select option {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  color: var(--fe-color-text, #e2e8f0);
}

/* Compact-list layout */
.fe-fellowships-compact-head { margin-bottom: 16px; }
.fe-fellowships-compact-filters {
  display: grid; gap: 12px;
  grid-template-columns: minmax(220px, 1fr) auto auto;
  align-items: end;
  margin-bottom: 12px;
}
@media (max-width: 720px) {
  .fe-fellowships-compact-filters { grid-template-columns: 1fr; }
}
.fe-fellowships-compact-pills {
  display: flex; flex-wrap: wrap; gap: 6px;
  list-style: none; padding: 0; margin: 0 0 14px;
}
.fe-fellowships-compact-section { margin-bottom: 18px; }
.fe-fellowships-compact-section-heading {
  display: flex; align-items: baseline; gap: 10px;
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
  font-size: 1.05rem; font-weight: 700;
  margin: 0 0 8px;
}
.fe-fellowships-compact-rows {
  list-style: none; padding: 0; margin: 0;
  border-top: 1px solid var(--fe-color-border, #e5e7eb);
}
.fe-fellowships-compact-row {
  display: grid; gap: 12px;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  padding: 12px 6px;
  border-bottom: 1px solid var(--fe-color-border, #e5e7eb);
}
.fe-fellowships-compact-row-info { display: grid; gap: 2px; min-width: 0; }
.fe-fellowships-compact-row-name { font-weight: 600; }
.fe-fellowships-compact-row-where { font-size: .85rem; }
.fe-fellowships-compact-row-link {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--fe-color-accent, #2563eb); text-decoration: none; font-weight: 600;
  font-size: .9rem;
}
.fe-fellowships-compact-row-link:hover { text-decoration: underline; }
.fe-fellowships-compact-row-link svg { width: 14px; height: 14px; }

/* Admin Fellowships Index card (Settings → Global) — toggle styles */
.fellowships-tbl input[type="text"],
.fellowships-tbl input[type="url"] { width: 100%; }
.fellowships-tbl tr.is-virtual .fellowship-cell-region-field { opacity: 0; pointer-events: none; }
.fellowships-actions { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }
.fellowship-virtual-toggle { display: inline-flex; align-items: center; }

/* ===== Card-style token aggregator =====
   Every card class that opts into the primary or secondary card style
   shares one source of truth for shadow, transition timing, and
   hover lift / shadow. Border-width + colour-tokens are applied
   directly on each card's own rule (so different cards keep their
   own radius / padding); the structural language is normalised here.
   The `.fe-card-primary` / `.fe-card-secondary` classes at the
   bottom of this block are the public opt-in classes builder
   containers can apply to inherit the same look. */
.fe-meeting-card,
.fe-meeting-detail-card,
.fe-meeting-extended-card,
.fe-recovery-threeup-card,
.fe-event-detail-card,
.fe-event-row,
.fe-meeting-resources,
.fe-mlist-card,
.fe-events-card,
.fe-events-mag-hero,
.fe-announcements-card,
.fe-fellowships-card,
.fe-library-item,
.fe-card-primary {
  box-shadow: var(--fe-card-primary-shadow, none);
  transition:
    background-color var(--fe-card-primary-transition, 200ms ease),
    border-color var(--fe-card-primary-transition, 200ms ease),
    box-shadow var(--fe-card-primary-transition, 200ms ease),
    transform var(--fe-card-primary-transition, 200ms ease);
}
.fe-meeting-card:hover,
.fe-meeting-detail-card:hover,
.fe-meeting-extended-card:hover,
.fe-recovery-threeup-card:hover,
.fe-event-detail-card:hover,
.fe-event-row:hover,
.fe-meeting-resources:hover,
.fe-mlist-card:hover,
.fe-events-card:hover,
.fe-events-mag-hero:hover,
.fe-announcements-card:hover,
.fe-fellowships-card:hover,
.fe-library-item:hover,
.fe-card-primary:hover {
  box-shadow: var(--fe-card-primary-hover-shadow, 0 8px 28px rgba(15, 23, 42, 0.10));
  transform: var(--fe-card-primary-hover-transform, translateY(-2px));
  border-color: var(--fe-color-card-primary-hover-border,
                    var(--fe-color-card-primary-border, var(--fe-accent)));
}

.fe-feature-card,
.fe-quick-card,
.fe-faq-item,
.fe-inclusion-card,
.fe-meeting-mag-side-card,
.fe-card-secondary {
  box-shadow: var(--fe-card-secondary-shadow, none);
  transition:
    background-color var(--fe-card-secondary-transition, 200ms ease),
    border-color var(--fe-card-secondary-transition, 200ms ease),
    box-shadow var(--fe-card-secondary-transition, 200ms ease),
    transform var(--fe-card-secondary-transition, 200ms ease);
}
.fe-feature-card:hover,
.fe-quick-card:hover,
.fe-faq-item:hover,
.fe-inclusion-card:hover,
.fe-meeting-mag-side-card:hover,
.fe-card-secondary:hover {
  box-shadow: var(--fe-card-secondary-hover-shadow, 0 10px 32px rgba(15, 23, 42, 0.08));
  transform: var(--fe-card-secondary-hover-transform, translateY(-3px));
  border-color: var(--fe-color-card-secondary-hover-border,
                    var(--fe-accent, var(--fe-border)));
}

/* Opt-in classes that turn any container into a primary / secondary
   card surface — colour tokens for bg + border come along too so the
   block reads like a meeting / feature card without further styling.
   Used by the page builder's "Card style" option on container blocks. */
.fe-card-primary {
  background: var(--fe-color-card-primary-bg, #ffffff);
  border: var(--fe-card-primary-border-width, 1px) solid
          var(--fe-color-card-primary-border, var(--fe-accent));
  border-radius: 14px;
}
html[data-theme="dark"] .fe-card-primary,
body.fe-frontend-force-dark .fe-card-primary {
  background: var(--fe-color-card-primary-bg-dark, #131a33);
  border-color: var(--fe-color-card-primary-border-dark, #1f2a44);
}
.fe-card-secondary {
  background: var(--fe-color-card-secondary-bg, var(--fe-panel-soft));
  border: var(--fe-card-secondary-border-width, 1px) solid
          var(--fe-color-card-secondary-border, var(--fe-border));
  border-radius: 14px;
}
html[data-theme="dark"] .fe-card-secondary,
body.fe-frontend-force-dark .fe-card-secondary {
  background: var(--fe-color-card-secondary-bg-dark, #131a33);
  border-color: var(--fe-color-card-secondary-border-dark, #131a33);
}

/* ── Blog body blocks (.bb-*) ─────────────────────────────────────
   Rendered output of the visual drag-and-drop blog-body editor (see
   `app/templates/_blog_blocks.html` and `static/js/post_body_editor.js`).
   Variables on the host (`.fe-blog-post-body`, `.fe-blog-cls-body`,
   `.fe-blog-longform-body`) supply blog-specific palette colours so
   per-template overrides win without selector gymnastics. */
.bb-paragraph { margin: 0 0 1.3rem; line-height: 1.75; }
.bb-paragraph:last-child { margin-bottom: 0; }
.bb-paragraph p:last-child { margin-bottom: 0; }
.bb-paragraph a { color: var(--blog-accent, var(--fe-color-accent, #1f4e79)); }

.bb-heading {
  font-weight: 700; line-height: 1.2;
  margin: 2rem 0 .85rem;
  font-family: var(--tpl-heading-font, var(--fe-font-heading, inherit));
}
.bb-heading-h2 { font-size: clamp(1.5rem, 3vw, 1.85rem); }
.bb-heading-h3 { font-size: 1.3rem; font-weight: 600; }
.bb-heading-h4 { font-size: 1.1rem; font-weight: 600; }
.bb-heading:first-child { margin-top: 0; }

.bb-image {
  margin: 1.5rem auto; max-width: var(--bb-img-width, 100%);
}
.bb-image--left  { margin-left: 0; margin-right: auto; }
.bb-image--right { margin-left: auto; margin-right: 0; }
.bb-image--center { margin-left: auto; margin-right: auto; }
.bb-image img { display: block; width: 100%; height: auto; border-radius: 8px; }
.bb-image-caption {
  margin-top: .55rem; font-size: .88rem; line-height: 1.45;
  color: var(--blog-ink-soft, var(--fe-color-muted, #6b7280));
  text-align: center;
}

.bb-button {
  margin: 1.5rem 0;
  display: flex;
}
.bb-button--left   { justify-content: flex-start; }
.bb-button--center { justify-content: center; }
.bb-button--right  { justify-content: flex-end; }
.bb-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 22px; border-radius: 999px;
  font-weight: 600; font-size: .95rem; line-height: 1;
  text-decoration: none;
  transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
}
.bb-btn--primary {
  background: var(--blog-accent, var(--fe-color-accent, #1f4e79));
  color: var(--fe-color-accent-contrast, #ffffff);
  box-shadow: 0 6px 14px rgba(15, 23, 42, 0.10);
}
.bb-btn--primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 20px rgba(15, 23, 42, 0.16);
  color: var(--fe-color-accent-contrast, #ffffff);
}
.bb-btn--secondary {
  background: transparent;
  color: var(--blog-accent, var(--fe-color-accent, #1f4e79));
  border: 1.5px solid currentColor;
}
.bb-btn--secondary:hover {
  background: color-mix(in srgb, var(--blog-accent, var(--fe-color-accent, #1f4e79)) 10%, transparent);
  transform: translateY(-1px);
}

.bb-list {
  margin: 0 0 1.4rem; padding-left: 1.6rem;
  line-height: 1.7;
}
.bb-list li { margin-bottom: .35rem; }
.bb-list li:last-child { margin-bottom: 0; }
.bb-list a { color: var(--blog-accent, var(--fe-color-accent, #1f4e79)); }

.bb-quote {
  margin: 1.8rem 0;
  padding: .25rem 0 .25rem 1.4rem;
  border-left: 4px solid var(--blog-accent, var(--fe-color-accent, #1f4e79));
  font-style: italic;
  color: var(--blog-ink-soft, var(--fe-color-muted, #4b5563));
}
.bb-quote p { margin: 0 0 .4rem; font-size: 1.1rem; line-height: 1.55; }
.bb-quote p:last-child { margin-bottom: 0; }
.bb-quote-cite {
  display: block; font-size: .9rem; font-style: normal;
  color: var(--blog-ink-soft, var(--fe-color-muted, #6b7280));
}

.bb-callout {
  display: flex; gap: 12px;
  margin: 1.6rem 0; padding: 14px 16px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--bb-callout-tone, #1f4e79) 8%, transparent);
  border-left: 4px solid var(--bb-callout-tone, #1f4e79);
}
.bb-callout--info    { --bb-callout-tone: var(--blog-accent, #1f4e79); }
.bb-callout--success { --bb-callout-tone: #047857; }
.bb-callout--warn    { --bb-callout-tone: #b45309; }
.bb-callout--danger  { --bb-callout-tone: #b91c1c; }
.bb-callout-icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--bb-callout-tone, #1f4e79);
  color: #ffffff; font-weight: 700;
}
.bb-callout-body { flex: 1 1 auto; min-width: 0; }
.bb-callout-body p:last-child { margin-bottom: 0; }
.bb-callout-title {
  font-weight: 700; margin-bottom: .35rem;
  color: var(--bb-callout-tone, #1f4e79);
}

.bb-separator {
  border: 0; border-top: 1px solid var(--blog-rule, var(--fe-color-border, #e5e7eb));
  margin: 2rem auto; max-width: 200px;
}

.bb-video { margin: 1.8rem 0; }
.bb-video-frame {
  position: relative; aspect-ratio: 16 / 9;
  background: #000; border-radius: 10px; overflow: hidden;
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.10);
}
.bb-video-frame iframe,
.bb-video-frame video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; border: 0;
}
.bb-video-caption {
  margin-top: .55rem; font-size: .88rem; text-align: center;
  color: var(--blog-ink-soft, var(--fe-color-muted, #6b7280));
}

.bb-code {
  margin: 1.6rem 0; padding: 1rem 1.1rem;
  background: var(--blog-bg, #f3f4f6);
  border: 1px solid var(--blog-rule, var(--fe-color-border, #e5e7eb));
  border-radius: 8px;
  overflow-x: auto;
  font-size: .92rem; line-height: 1.55;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.bb-code code { background: transparent; padding: 0; }

@media (max-width: 720px) {
  .bb-heading-h2 { font-size: 1.4rem; }
  .bb-quote p { font-size: 1rem; }
  .bb-image { max-width: 100% !important; }
}

/* Blog body Section block — wraps a stack of child blocks with admin-
   tuned top/bottom margins. The inline `style` from the renderer
   sets the spacing; the first/last-child resets keep the section's
   first heading or paragraph from doubling up with its own margin
   collapse, and the `display: block` is explicit so this stays a
   plain stacking context inside the blog body. */
.bb-section { display: block; }
.bb-section > *:first-child { margin-top: 0; }
.bb-section > *:last-child  { margin-bottom: 0; }
