
/* ============================================================
   GUIDES — v9.0 redesign — editorial / magazine, theme-driven
   ============================================================
   Two themes: midnight (default), oatmeal (warm beige).
   No light/paper theme — dropped per direction.

   Mobile-first; breakpoints expand the bento and reduce padding.
   ============================================================ */

/* ---------- Theme tokens ---------- */
:root[data-theme="midnight"], html[data-theme="midnight"] {
  --bg: #0a0a0a;
  --card: #141414;
  --card-elev: #1c1c1c;
  --text: #ffffff;
  --text-soft: #c5c5c5;
  --muted: #737373;
  --border: rgba(255,255,255,0.08);
  --border-strong: rgba(255,255,255,0.16);
  --pill-bg: rgba(255,255,255,0.05);
  --nav-bg: rgba(10,10,10,0.78);
  --gradient-overlay: linear-gradient(180deg, transparent 30%, rgba(10,10,10,0.85) 100%);
  --shadow-card: 0 1px 2px rgba(0,0,0,.3), 0 4px 12px rgba(0,0,0,.18);
  --on-image: #ffffff;
}
:root[data-theme="oatmeal"], html[data-theme="oatmeal"] {
  --bg: #f2eee9;
  --card: #ffffff;
  --card-elev: #faf7f3;
  --text: #1a1612;
  --text-soft: #3a322d;
  --muted: #5a5147;
  --border: rgba(26,22,18,0.10);
  --border-strong: rgba(26,22,18,0.22);
  --pill-bg: rgba(26,22,18,0.05);
  --nav-bg: rgba(242,238,233,0.82);
  --gradient-overlay: linear-gradient(180deg, transparent 30%, rgba(26,22,18,0.85) 100%);
  --shadow-card: 0 1px 2px rgba(26,22,18,.04), 0 4px 12px rgba(26,22,18,.05);
  --on-image: #ffffff;
}
/* Default theme without an attribute = midnight */
:root, html {
  --bg: #0a0a0a;
  --card: #141414;
  --card-elev: #1c1c1c;
  --text: #ffffff;
  --text-soft: #c5c5c5;
  --muted: #737373;
  --border: rgba(255,255,255,0.08);
  --border-strong: rgba(255,255,255,0.16);
  --pill-bg: rgba(255,255,255,0.05);
  --nav-bg: rgba(10,10,10,0.78);
  --gradient-overlay: linear-gradient(180deg, transparent 30%, rgba(10,10,10,0.85) 100%);
  --shadow-card: 0 1px 2px rgba(0,0,0,.3), 0 4px 12px rgba(0,0,0,.18);
  --on-image: #ffffff;
  /* v9.10.54 — Approximate sticky header height. The page header
   * (.site-header) is itself position:sticky at top:0; any other
   * sticky element below it needs to offset by this amount so it
   * pins below the header rather than overlapping. Adjusted
   * downward at the mobile breakpoint (see below). */
  --header-h: 3rem;
}
@media (max-width: 480px) {
  :root, html { --header-h: 2.6rem; }
}

/* ---------- Base reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
body {
  font-family: 'Outfit', -apple-system, system-ui, 'Segoe UI', sans-serif;
  font-weight: 400;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  transition: background-color .5s ease, color .5s ease;
  /* v9.10.54 — overflow-x: clip instead of hidden.
   *
   * overflow-x:hidden implicitly sets overflow-y to "auto" per
   * the CSS spec (when only one axis is hidden/scroll/auto, the
   * other becomes auto). That made body a scroll container,
   * which in turn broke position:sticky on descendants — sticky
   * was anchoring to the body's scroll instead of the viewport,
   * so the breadcrumb never pinned visually.
   *
   * overflow-x:clip clips horizontal overflow without setting up
   * a scroll container, so the viewport remains the scroll
   * context and sticky works as expected. Browser support is
   * solid (Chrome 90+, Safari 16+, Firefox 81+, all evergreen);
   * older browsers fall back to the unclipped behavior, which
   * may show a horizontal scrollbar in pathological cases but
   * won't break the page.
   *
   * Fallback overflow-x:hidden first so any non-clip-supporting
   * browser still gets the horizontal clip behavior; modern
   * browsers use clip from the second declaration. */
  overflow-x: hidden;
  overflow-x: clip;
  min-height: 100vh;
}
.serif { font-family: 'Playfair Display', Georgia, serif; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
img { display: block; max-width: 100%; }
::selection { background: var(--text); color: var(--bg); }
.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* ---------- Container ---------- */
.container {
  max-width: 1600px;
  margin: 0 auto;
  /* viewport-fit=cover (set in <meta>) extends the page under the
   * iPhone notch / Dynamic Island on landscape orientation. Without
   * safe-area padding, horizontal content gets clipped behind the
   * notch. max() so the safe area expands the padding only when the
   * env() value is larger than the design padding — on devices
   * without safe-area-inset (everything non-iOS), env() resolves to
   * 0 and the design padding stands. */
  padding: 0 max(1.5rem, env(safe-area-inset-right)) 0 max(1.5rem, env(safe-area-inset-left));
}
@media (max-width: 640px) {
  .container {
    padding: 0 max(1rem, env(safe-area-inset-right)) 0 max(1rem, env(safe-area-inset-left));
  }
}

/* ============================================================
   HEADER — sticky glass nav with theme switch + lang + sign-in
   ============================================================ */
.site-header {
  position: sticky; top: 0; z-index: 100;
  padding: .85rem 0;
  background: var(--nav-bg);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  border-bottom: 1px solid var(--border);
  transition: background .5s ease, border-color .5s ease;
}
.site-header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
}
.header-left { display: flex; align-items: center; gap: .35rem; min-width: 0; }
.site-logo {
  display: inline-flex; align-items: center;
  text-decoration: none;
  /* The wordmark provides its own typography; the anchor is just the
   * tappable wrapper so the brand becomes a single click target back
   * to the subapp home. */
}

/* ===== woow city wordmark =====
 *
 *   w  ●  ○  w  city
 *   |  |  |  |    |
 *   |  |  |  |    static word
 *   |  |  |  letter
 *   |  |  empty circle → switches to LIGHT (oatmeal) theme
 *   |  filled circle  → switches to DARK (midnight) theme
 *   letter
 *
 * The two os are theme-switch buttons styled as letterforms (filled
 * vs outline circle). The mapping is intuitive: the symbol matches
 * the theme it activates — filled = dark, empty = light.
 *
 * "city" comes after the wordmark in lighter weight to avoid
 * competing with the brand mark itself.
 */
.ww-wordmark {
  display: inline-flex; align-items: baseline;
  gap: 0;
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--text);
}
.ww-letter {
  display: inline-block;
  font-family: inherit;
  line-height: 1;
}
/* The "city" tail sits in lighter weight + slightly looser letter
 * spacing so it reads as a qualifier of the brand mark, not a
 * fifth letterform. Gap between the wordmark and "city" is a
 * padding-left on the tail span — using padding (not margin or
 * inline whitespace) keeps the space stable across flex's
 * whitespace-collapsing rules. ~0.3em ≈ a regular space at the
 * wordmark's font-size. */
.ww-tail {
  font-weight: 500;
  letter-spacing: 0;
  opacity: .85;
  padding-left: 0.3em;
}
.ww-o {
  /* Circle the size of a letter glyph. Two variants:
   *   - default (no extra class): FILLED circle = dark theme button
   *   - .ww-o-empty: hollow circle = light theme button
   * The base inherits text color so the filled variant blends with
   * the surrounding letters. The hollow variant uses a ring (border)
   * with a transparent center.
   *
   * Sized in em units so it scales with the wordmark font-size on
   * mobile breakpoints — no separate responsive rule needed. */
  width: 0.62em; height: 0.62em;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: currentColor;
  color: inherit;
  cursor: pointer;
  transition: transform .15s, opacity .15s, box-shadow .15s;
  /* Slight margin so circles read as letterforms with proper
   * letter spacing rather than touching their neighbors. */
  margin: 0 0.06em;
  /* Self-align so the circle's optical center sits where a lowercase
   * "o" would — slightly above baseline. */
  align-self: center;
  position: relative;
  top: -0.04em;
}
.ww-o-empty {
  background: transparent;
  /* Outline thickness scaled by font-size for crisp rendering at
   * any size. 0.13em ≈ 2.5px at 19px font (the wordmark's typical
   * rendered size). */
  box-shadow: inset 0 0 0 0.13em currentColor;
}
.ww-o:hover {
  /* Subtle scale-up on hover; doesn't shift surrounding letters. */
  transform: scale(1.12);
}
.ww-o.is-active {
  /* When THIS o represents the active theme, give it a focus ring
   * around it. Both variants get the same ring so the active state
   * reads consistently. The ring sits OUTSIDE the o so the disc
   * (or hollow shape) keeps its identity as a letterform. */
  box-shadow: 0 0 0 0.13em var(--text), inset 0 0 0 0.13em currentColor;
}
.ww-o-empty.is-active {
  /* Empty variant when active: ring outside (active marker) PLUS the
   * border inside (so the o stays hollow). Stack both shadows. */
  box-shadow: 0 0 0 0.13em var(--text), inset 0 0 0 0.13em currentColor;
}
.ww-o:not(.ww-o-empty).is-active {
  /* Filled variant when active: outer ring only — the disc stays
   * filled with currentColor, no inset needed. */
  box-shadow: 0 0 0 0.13em var(--text);
}

/* ===== Subapp picker dropdown =====
 *
 * Sits next to the wordmark, showing the current subapp's name. When
 * there's more than one configured subapp, opens a panel listing all
 * of them so users can hop between guides without going back to the
 * index page. Visually mirrors the language switcher: native
 * <details>, same popover styling. */
.subapp-switcher { position: relative; }
.subapp-switcher summary { list-style: none; cursor: pointer; }
.subapp-switcher summary::-webkit-details-marker { display: none; }
.subapp-switcher summary::marker { content: ''; }
.subapp-btn {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .35rem .65rem .35rem .55rem;
  /* No margin-left here — spacing between the wordmark and the
   * picker comes from .header-left's gap property. Pre-fix this
   * had its own .15rem margin layered on top, producing
   * inconsistent gaps on desktop vs the stacked mobile layout. */
  margin-left: 0;
  border-radius: 999px;
  font-size: .9rem;
  font-weight: 600;
  color: var(--text);
  background: transparent;
  transition: background .15s;
  white-space: nowrap;
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.subapp-btn:hover { background: var(--pill-bg); }
.subapp-btn .chev { width: 9px; height: 9px; opacity: .55; }
.subapp-switcher[open] .subapp-btn { background: var(--pill-bg); }
.subapp-btn-static {
  /* Used when only one subapp is configured — no chevron, no hover
   * affordance, just the name as a calm label. */
  cursor: default;
}
.subapp-btn-static:hover { background: transparent; }
.subapp-menu {
  position: absolute; top: calc(100% + .5rem); left: 0;
  min-width: 200px;
  padding: .35rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 12px 40px rgba(0,0,0,.25);
  z-index: 110;
}
.subapp-switcher:not([open]) .subapp-menu { display: none; }
.subapp-menu a {
  display: block;
  padding: .55rem .75rem;
  border-radius: 9px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  text-decoration: none;
  text-transform: none; letter-spacing: 0;
  transition: background .15s;
}
.subapp-menu a:hover { background: var(--pill-bg); }
.subapp-menu a.is-active {
  background: var(--pill-bg);
  font-weight: 600;
}

.site-nav { display: flex; align-items: center; gap: .65rem; }
/* v9.5 — Header row groups.
 *
 * Markup splits the right-side nav into two children: .site-nav-row1
 * (lang + sign-in) and .site-nav-row2 (trial pill + search icon).
 * On desktop both wrappers use display:contents so the inner items
 * flow into the parent flex row exactly as before — zero visual
 * change from the pre-v9.5 single-row layout.
 *
 * The mobile breakpoint (further down, max-width: 720px) overrides
 * both wrappers to display:flex and switches .site-nav to a column
 * so row1 and row2 stack vertically with the logo. */
.site-nav-row1, .site-nav-row2 { display: contents; }
.icon-btn {
  width: 38px; height: 38px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .2s;
  position: relative;
}
.icon-btn:hover { background: var(--pill-bg); }
.icon-btn svg { width: 18px; height: 18px; }

.lang-switcher { position: relative; }
.lang-switcher summary { list-style: none; cursor: pointer; }
.lang-switcher summary::-webkit-details-marker { display: none; }
.lang-switcher summary::marker { content: ''; }

.lang-btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .45rem .9rem;
  font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--text);
  border-radius: 999px;
  transition: background .2s;
  position: relative;
}
.lang-btn:hover { background: var(--pill-bg); }
.lang-btn .chev { width: 9px; height: 9px; opacity: .5; }
.lang-switcher[open] .lang-btn { background: var(--pill-bg); }

.signin-btn {
  display: inline-flex; align-items: center;
  padding: .55rem 1.4rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  transition: all .2s;
}
.signin-btn:hover {
  background: var(--text); color: var(--bg);
  border-color: var(--text);
}

/* ---------- Trial countdown pill (Turn __access13) ----------
 *
 * Compact non-interactive badge in the header showing time remaining
 * on the visitor's free trial. Hidden by default — client JS removes
 * .is-hidden after fetching /api/access-state, but only when the
 * visitor is in the trial window. Subscribed users, org-shared users,
 * and post-expiry users never see this pill.
 *
 * Two visual states:
 *   - default: neutral (matches signin-btn weight, calmer color)
 *   - .is-urgent: <10 min remaining, red accent to nudge upgrade
 *
 * The pill is read-only — clicking it doesn't do anything by design.
 * The visitor's call-to-action is the existing Sign in / paywall, not
 * a separate flow off the countdown. Keeping it a label avoids the
 * "every UI element is a button" trap where users wonder what each
 * thing does.
 */
.trial-pill {
  display: inline-flex; align-items: center;
  padding: .4rem .9rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-soft, var(--text));
  background: var(--pill-bg, transparent);
  border: 1px solid var(--border);
  border-radius: 999px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;  /* digits don't jitter as the count ticks */
}
.trial-pill.is-hidden { display: none; }
.trial-pill.is-urgent {
  color: #c0392b;
  border-color: #c0392b;
}
@media (max-width: 640px) {
  /* Mobile: shorten the label to save header space.
     The full label "Free trial · 42m" gets too crowded on narrow
     viewports; client JS shortens it but the CSS is here in case the
     font-size renders longer in some locales. */
  .trial-pill { padding: .35rem .7rem; font-size: 10px; letter-spacing: .08em; }
}

/* ---------- Lang menu (popover) ---------- */
.lang-menu {
  position: absolute; top: calc(100% + .5rem); right: 0;
  min-width: 220px; max-height: 60vh; overflow-y: auto;
  padding: .5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 12px 40px rgba(0,0,0,.25);
  z-index: 110;
}
.lang-switcher:not([open]) .lang-menu { display: none; }
.lang-menu a {
  display: flex; align-items: center; gap: .65rem;
  padding: .55rem .75rem;
  border-radius: 9px;
  font-size: 13px; font-weight: 500;
  color: var(--text);
  text-transform: none;
  letter-spacing: 0;
  transition: background .15s;
}
.lang-menu a:hover { background: var(--pill-bg); }
.lang-menu a.is-active { background: var(--pill-bg); font-weight: 600; }

/* ---------- User menu (profile dropdown) ---------- */
/* Shares popover styles with .lang-menu but has its own trigger:
 * a circular icon button (matching .icon-btn for the search icon),
 * with a small chevron stacked alongside the person silhouette so
 * the user reads it as "this opens a menu" rather than "click for
 * a single action". */
.user-menu { position: relative; }
.user-menu summary { list-style: none; cursor: pointer; }
.user-menu summary::-webkit-details-marker { display: none; }
.user-menu summary::marker { content: ''; }
.user-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border-radius: 50%;
  background: transparent;
  color: var(--text);
  transition: background .2s;
}
.user-btn svg { width: 18px; height: 18px; }
.user-btn:hover { background: var(--pill-bg); }
.user-menu[open] .user-btn { background: var(--pill-bg); }
.user-menu-panel {
  position: absolute; top: calc(100% + .5rem); right: 0;
  min-width: 240px;
  padding: .5rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 12px 40px rgba(0,0,0,.25);
  z-index: 110;
}
.user-menu:not([open]) .user-menu-panel { display: none; }
/* Header: shows the user's name + email at the top of the panel.
 * Non-interactive, hence the div, not an anchor. */
.user-menu-header {
  padding: .65rem .75rem .8rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: .35rem;
}
.user-menu-header .um-name {
  font-size: 13px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.user-menu-header .um-email {
  font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: .15rem;
}
.user-menu-panel a, .user-menu-panel button {
  display: flex; align-items: center; gap: .6rem;
  width: 100%;
  padding: .55rem .75rem;
  border-radius: 9px;
  font-size: 13px; font-weight: 500;
  color: var(--text);
  text-transform: none; letter-spacing: 0;
  background: transparent;
  border: none;
  text-align: left;
  cursor: pointer;
  transition: background .15s;
}
.user-menu-panel a:hover, .user-menu-panel button:hover {
  background: var(--pill-bg);
}
.user-menu-panel svg {
  width: 16px; height: 16px; opacity: .75; flex-shrink: 0;
}
.user-menu-divider {
  height: 1px; background: var(--border);
  margin: .35rem .25rem;
}

/* ---------- Header responsive ---------- */
@media (max-width: 720px) {
  .header-left {
    /* Stack the wordmark + subapp picker vertically on phones. The
     * single-row layout had everything competing for horizontal
     * space ("woow city" + "Montpellier" + search icon + lang
     * switcher + user menu = visually crowded on iPhone SE).
     * Stacking the brand block keeps each element legible without
     * shrinking type. The subapp picker sits left-aligned under the
     * wordmark and the right-side nav stays in its original row.
     *
     * align-items:flex-start so the wordmark sits flush left rather
     * than centering inside the stretched column. */
    flex-direction: column;
    align-items: flex-start;
    gap: .15rem;
  }
  /* Subapp picker on its own row — drop the small left margin we
   * added for inline alignment with the wordmark. The subapp name
   * also stays visible (we override the 480px hide rule below). */
  .subapp-btn {
    margin-left: 0;
    padding: .25rem .6rem .25rem .5rem;
    font-size: .82rem;
  }
  /* Dropdown panel: anchor under the new picker position. Same
   * default top offset, but left-aligned to the picker pill. */
  .subapp-menu {
    left: 0;
  }
  .ww-wordmark { font-size: 1.05rem; }
  .signin-btn { padding: .45rem 1rem; font-size: 10px; letter-spacing: .12em; }
  /* The header now occupies two visual rows on mobile, so it grows
   * vertically. Tighten its vertical padding (defined upstream as
   * .site-header) so the total height stays comfortable for one-
   * handed reach without feeling cramped. */
  .site-header-inner { padding-top: .45rem; padding-bottom: .45rem; }
  /* Header inner aligns to flex-start so the brand stack (logo +
   * Montpellier picker) and the nav stack (row 1 + row 2) sit on
   * the same top baseline. Without this they'd center against each
   * other and the nav rows would float vertically. */
  .site-header-inner { align-items: flex-start; }
  /* Right-side nav becomes a vertical stack of two rows on mobile.
   * v9.5 — Row 1 (lang + sign-in) sits inline with the logo at the
   * top; row 2 (trial pill + search icon) drops below. The wrappers
   * — display:contents on desktop — re-enable themselves here as
   * flex rows so each can lay out its own children with its own gap
   * and alignment.
   *
   * align-items:flex-end on .site-nav keeps both inner rows
   * right-aligned in the header column, which mirrors how the
   * previous single-row nav cluster behaved (right edge of header).
   *
   * Each row uses align-items:center so the trial pill (tall) and
   * the icon-button (38px round) sit visually balanced rather than
   * baseline-aligned. */
  .site-nav {
    flex-direction: column;
    align-items: flex-end;
    gap: .35rem;
    padding-top: .1rem;
  }
  .site-nav-row1, .site-nav-row2 {
    display: flex;
    align-items: center;
    gap: .5rem;
  }
}
@media (max-width: 480px) {
  /* On very narrow screens the subapp picker still shows its name —
   * unlike the previous behaviour where we hid it. With the stacked
   * layout there's room for the full label, and hiding it would
   * leave just a chevron pill that didn't tell the user which
   * guide they were in.
   *
   * Sign-in button: kept visible on mobile (previously hidden via
   * display:none which left no way to sign in from a phone). We
   * tighten its sizing on iPhone SE-width so it doesn't crowd the
   * search icon next to it. */
  .signin-btn {
    padding: .35rem .6rem;
    font-size: 9.5px;
    letter-spacing: .1em;
  }
}

/* ============================================================
   HERO HEADER — city name + tagline + chip strip
   ============================================================ */
.hero-section {
  padding: 3.5rem 0 2rem;
}
@media (max-width: 720px) { .hero-section { padding: 2rem 0 1.25rem; } }

.hero-eyebrow {
  display: flex; align-items: center; gap: .75rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .35em; text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1.25rem;
}
.hero-eyebrow::before {
  content: ''; width: 28px; height: 1px; background: currentColor;
}
.hero-title {
  font-family: 'Outfit', sans-serif;
  font-size: clamp(3rem, 11vw, 9rem);
  font-weight: 700;
  line-height: .82;
  letter-spacing: -0.04em;
  margin-bottom: 1.25rem;
  word-break: keep-all;
  hyphens: none;
}
.hero-title .accent {
  font-family: 'Playfair Display', serif;
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.02em;
}
/* v9.5 — "what's On in <City>" variant.
 *
 * The phrase is longer than a single city name, so the type ramps
 * down from the editorial clamp(3, 11vw, 9rem) to roughly half. The
 * surrounding "what's On in" reads in sans (Outfit, medium weight),
 * the city name carries the italic Playfair accent already wired up
 * via .hero-title .accent.
 *
 * .wow-letter highlights the "w" of "what's" and "O" of "On" — they
 * inherit the surrounding sans face but bump up to the brand
 * wordmark's weight (800) so they pop against the lighter
 * surrounding letters. The visual rhyme with the "wOOw city"
 * wordmark in the header is the whole point of the new title.
 *
 * line-height stays tighter than body copy but looser than the old
 * giant title — the new title can wrap onto two lines on narrow
 * viewports, and 1.05 keeps the wrapped lines from kissing.
 *
 * The .is-drill modifier is used by renderDrillHero for filtered
 * views. Same composition, just smaller.
 *
 * v9.7 — Sizes tuned down again after operator preference. The
 * editorial clamp pre-v9.7 was clamp(1.8rem, 5.5vw, 4rem), which on
 * a wide desktop hit the 4rem ceiling (64px) and read as oversized
 * relative to the rest of the page — the operator prefers a size
 * closer to the .is-drill cap. Editorial now caps at 2.8rem; drill
 * keeps its previous 2.6rem cap so there's still a small editorial
 * vs. drill visual distinction. */
.hero-title-whatson {
  font-size: clamp(1.5rem, 4vw, 2.8rem);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.015em;
}
.hero-title-whatson.is-drill {
  /* v9.10.1 — Drill title now matches editorial size exactly per
   * operator request ("font size of 'what's On in Montpellier' should
   * be the same on every page"). Pre-v9.10.1 the drill cap was
   * 2.6rem vs editorial 2.8rem; the operator finds the small step
   * change visually distracting when navigating between home and
   * atmosphere pages. Margin tweak below stays — drill view has
   * more chrome under the title (chip strips, breadcrumb context)
   * so a tighter bottom margin keeps the vertical rhythm. */
  margin-bottom: .5rem;
}
.hero-title-whatson .wow-letter {
  font-weight: 800;
  letter-spacing: -0.04em;
}
.hero-title-whatson .accent {
  /* The accent is already italic Playfair via the rule above. We
   * just bump the weight a notch so the city name doesn't read as
   * lighter than the sans surrounding it — italic Playfair at 400
   * has a much lighter optical weight than Outfit 500. */
  font-weight: 500;
}
.hero-subtitle {
  max-width: 38rem;
  font-size: 1.1rem;
  line-height: 1.55;
  color: var(--text-soft);
  border-left: 2px solid var(--border-strong);
  padding-left: 1.4rem;
  margin-bottom: 2.25rem;
}
@media (max-width: 720px) {
  .hero-subtitle { font-size: 1rem; padding-left: 1.1rem; margin-bottom: 1.75rem; }
}

/* ============================================================
   CHIP STRIP — atmosphere filters
   ============================================================ */
.chip-strip,
.chips {
  display: flex; align-items: center; gap: .45rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none;
  /* Edge fade so the user knows there's more */
  -webkit-mask-image: linear-gradient(to right, black 0, black calc(100% - 24px), transparent 100%);
          mask-image: linear-gradient(to right, black 0, black calc(100% - 24px), transparent 100%);
  padding-right: 1.5rem;
}
.chip-strip::-webkit-scrollbar,
.chips::-webkit-scrollbar { display: none; }
/* When .chips is the only thing in a page-context block, it doesn't
   need the top border (the page above it provides separation). */
.page-context .chips {
  border-top: 0; padding-top: 0;
}
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .55rem 1.1rem;
  font-size: 10px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  background: var(--pill-bg);
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 999px;
  white-space: nowrap;
  cursor: pointer;
  transition: all .2s;
  flex-shrink: 0;
}
.chip:hover {
  color: var(--text);
  border-color: var(--border-strong);
}
.chip.active,
.chip.is-active {
  background: var(--text); color: var(--bg);
  border-color: var(--text);
}
.chip-count {
  opacity: .6;
  margin-left: .15rem;
}
.chip.active .chip-count,
.chip.is-active .chip-count { opacity: .55; }
.chip-icon { display: inline-flex; align-items: center; }

/* ---------- Tag chip (secondary strip in drill) ---------- */
.tag-strip,
.tag-chips {
  display: flex; align-items: center; gap: .25rem;
  flex-wrap: wrap;
}

/* ---------- Sub-filter wrapper (Turn __access16) ----------
 *
 * Container for the tag and time-of-day chip strips. Both are
 * sub-filters of the active atmosphere — narrowing within whichever
 * atmosphere the user picked. Visual hierarchy:
 *
 *   ATMOSPHERE STRIP (top, full-width, bold pills)
 *     │
 *     ├── tag chips strip       ← inside .sub-filters
 *     │
 *     └── time-of-day chips     ← inside .sub-filters
 *
 *   Breadcrumb
 *
 * The wrapper carries:
 *   1. A left indent (1.5rem) — visually pushes the sub-strips
 *      "underneath" the atmosphere strip.
 *   2. A 1px vertical connector line in the indent gutter, drawn
 *      via ::before. The line is intentionally subtle (50% opacity)
 *      so it reads as a "structural hint" rather than a hard divider.
 *      It anchors the sub-strips under the parent atmosphere strip
 *      so the user sees they're sub-options, not peer filters.
 *
 * Pre-__access16, users reported the chip strips as "confusing in
 * terms of usage and display" — they read the three strips as a flat
 * list of equal-weight filters rather than a hierarchy.
 *
 * Mobile (<540px): the indent steals too much horizontal space, so we
 * drop it and convert the connector to a horizontal top rule. Same
 * hierarchy hint (separator between atmosphere strip and sub-strips),
 * appropriate for the narrower viewport.
 */
.sub-filters {
  position: relative;
  padding-left: 1.5rem;
  margin-top: .5rem;
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.sub-filters::before {
  content: '';
  position: absolute;
  left: .5rem;
  top: 0;
  bottom: .35rem;
  width: 1px;
  background: var(--border-strong, rgba(0,0,0,.18));
  opacity: .5;
  pointer-events: none;
}
@media (max-width: 540px) {
  .sub-filters {
    padding-left: 0;
    padding-top: .35rem;
    margin-top: .3rem;
  }
  .sub-filters::before {
    left: 0;
    right: 0;
    top: 0;
    bottom: auto;
    width: auto;
    height: 1px;
    opacity: .35;
  }
}

/* v9.10.49 — Tag chips are now the sole tag-filter affordance on
 * drill pages (the hero-filter accordion is suppressed when an
 * atmosphere is active). Previously they were a secondary surface
 * alongside the dropdown, so transparent-with-no-border kept them
 * understated. Now they need to read as "interactive filter pills"
 * at first glance.
 *
 * Changes vs pre-v9.10.49:
 *   - Subtle border (border vs transparent) so each chip has a clear
 *     boundary on the page background.
 *   - Slightly larger padding for an easier tap target on mobile.
 *   - Slightly stronger active state (filled-in dark text).
 *
 * Hover/active states preserved; the X-icon for "remove" still
 * appears on active chips. */
.tag-chip {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .4rem .85rem;
  font-size: 10px; font-weight: 600;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
  border-radius: 999px;
  cursor: pointer;
  transition: color .2s, background .2s, border-color .2s;
  border: 1px solid var(--border);
  background: var(--bg);
}
.tag-chip:hover {
  color: var(--text);
  border-color: var(--border-strong);
}
.tag-chip.is-active,
.tag-chip.active {
  background: var(--text);
  color: var(--bg);
  border-color: var(--text);
}
.tag-chip-icon { font-size: 12px; }
.tag-chip-count { opacity: .6; margin-left: .15rem; }

/* __access19 — Multi-select tag UI.
 *
 * X icon on active chips: signals the "click to remove" affordance.
 * Sized inline-flex with a small left margin so it sits after the
 * label and count without disrupting the pill shape. Stroke inherits
 * currentColor (the active chip's text color) so it's always visible
 * against the pill background regardless of theme. */
.tag-chip-x {
  margin-left: .15rem;
  flex-shrink: 0;
  opacity: .65;
  transition: opacity .15s;
}
.tag-chip.is-active:hover .tag-chip-x { opacity: 1; }

/* "Clear all" pill — appears at the top of the .sub-filters wrapper
 * when 2+ tags are active. Visually a peer to the tag-chip pills but
 * weighted differently: red-ish accent so it reads as "destructive
 * action" without shouting. Sits in its own row above the facet rows
 * (handled by the .tag-chips-clear-row wrapper). */
.tag-chips-clear-row {
  display: flex;
  margin-bottom: .15rem;
}
.tag-chip-clear-all {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .3rem .75rem .3rem .6rem;
  font-size: 10px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--text);
  background: var(--pill-bg);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: opacity .15s;
}
.tag-chip-clear-all:hover { opacity: .8; }
.tag-chip-clear-all .tag-chip-x {
  opacity: .85;
}
.tag-chip-clear-all .tag-chip-count {
  opacity: .55;
  font-weight: 500;
}

/* __access18: .tod-chips / .tod-chip styles removed.
 * Time-of-day chips render through .tag-chip via the faceted-tag system
 * (facet='time-of-day'). The shape inherits from .tag-chip — same pill
 * shape, smaller-than-atmosphere weight, sits inside .sub-filters. */

/* ---------- Drill breadcrumb ---------- */
.drill-breadcrumb {
  display: flex; align-items: center; gap: .55rem;
  margin: 1.25rem 0 .35rem;
  font-size: 12px;
  color: var(--muted);
  flex-wrap: wrap;
}
.drill-breadcrumb .accent-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent, currentColor);
}
.drill-breadcrumb .sep { color: var(--muted); opacity: .5; }
.drill-breadcrumb .current { color: var(--text); }
.drill-result-count {
  font-size: 12px; color: var(--muted);
  margin-bottom: 1.5rem;
}

/* ---------- Breadcrumb (legacy buildBreadcrumb output) ---------- */
.crumbs {
  display: flex; align-items: center; gap: .55rem;
  font-size: 12px;
  color: var(--muted);
  flex-wrap: wrap;
  /* Detail pages put the hero banner immediately under the
   * breadcrumb; without bottom spacing the crumb visually merges
   * into the banner edge. On home/drill views the page-context
   * wrapper already provides its own bottom margin, so this is
   * additive — small enough not to disrupt that layout. */
  margin-bottom: 1rem;
}

/* v9.10.54 — Full-width sticky wrapper for body-level breadcrumbs.
 *
 * The outer .crumbs-bar bears the background + sticky positioning;
 * the inner .crumbs is constrained to the content grid for text
 * alignment. This split makes the bar visually full-viewport-width
 * when stuck (the background extends edge-to-edge), while the
 * text content stays aligned with the rest of the page.
 *
 * Pre-v9.10.54 sticky was on .crumbs itself with max-width:1600px,
 * so the stuck bar was only as wide as the content — the wings on
 * either side showed scrolling content through. Wrapping fixes that.
 *
 * top: var(--header-h) — the page header is itself sticky at top:0;
 * we offset by its measured height (set by JS in guidesClientScript)
 * so the breadcrumb pins JUST BELOW the header. The fallback value
 * in :root is a rough static estimate; the JS measurement makes it
 * accurate to the actual rendered header (which varies by viewport
 * width because mobile has two rows of nav content).
 *
 * z-index 50: above page content but below modal overlays. */
.crumbs-bar {
  position: sticky;
  top: var(--header-h);
  z-index: 50;
  background: var(--bg);
  /* Edge-to-edge: cancel the parent's inline-padding by absolute-
   * width tricks isn't needed since .crumbs-bar IS a body-level
   * element. It naturally extends to 100% of its containing block,
   * which is <body> or <main> — both full viewport width. */
  width: 100%;
  /* Subtle hairline separator only visible against scrolling content. */
  box-shadow: 0 1px 0 0 color-mix(in srgb, var(--text) 6%, transparent);
}
.crumbs-bar .crumbs {
  /* Inside the bar, the crumbs element gets the max-width and
   * horizontal padding that .container would have provided. This
   * keeps the breadcrumb text aligned with the rest of the page
   * content grid even though the bar itself is edge-to-edge. */
  max-width: 1600px;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 0; /* spacing is owned by the bar's padding */
  padding-top: .7rem;
  padding-bottom: .5rem;
  padding-left: max(1.5rem, env(safe-area-inset-left));
  padding-right: max(1.5rem, env(safe-area-inset-right));
}
@media (max-width: 640px) {
  .crumbs-bar .crumbs {
    padding-left: max(1rem, env(safe-area-inset-left));
    padding-right: max(1rem, env(safe-area-inset-right));
  }
}
/* The bar itself needs a bottom margin so subsequent content (hero,
 * detail-hero, etc.) doesn't crowd the breadcrumb. Equivalent to
 * the old .crumbs margin-bottom: 1rem. */
.crumbs-bar { margin-bottom: 1rem; }
.crumbs a {
  color: var(--muted);
  transition: color .15s;
}
.crumbs a:hover { color: var(--text); }
.crumbs .crumb-current {
  color: var(--text);
  font-weight: 500;
}
.crumbs .sep {
  color: var(--muted); opacity: .4;
}
.crumbs .chip-icon {
  font-size: 14px;
  margin-right: .25rem;
}

/* ============================================================
   HERO FILTERS — the unified hero area combining heading +
   breadcrumb + atmosphere list + timeline + tags.
   ============================================================
 *
 * Layout responsibilities:
 *
 *   Desktop (≥720px wide): 3-column grid.
 *     ┌────────────────┬──────────┬──────────┐
 *     │ headline       │ timeline │ atmos    │
 *     │ + breadcrumb   │          │ + tags   │
 *     └────────────────┴──────────┴──────────┘
 *     <details> sections inside .hero-filters are always-expanded
 *     (chevron hidden, summary becomes a small section label).
 *
 *   Mobile (<720px): single column, accordion behavior.
 *     ┌────────────────┐
 *     │ headline       │
 *     │ + breadcrumb   │
 *     ├────────────────┤
 *     │ ▸ atmospheres  │  (collapsed by default; tap to expand)
 *     │ ▸ timeline     │
 *     │ ▸ tags         │
 *     └────────────────┘
 *     <details> sections behave natively. Summary shows icon +
 *     label + active selection + chevron. Tapping toggles.
 *
 * Why <details> as the building block:
 *   - Native accordion semantics → keyboard accessible out of the box
 *     (Enter/Space toggles, focus stays in DOM order).
 *   - Screen readers announce expansion state via role="region" w/
 *     aria-expanded automatically.
 *   - Zero JS for the toggle on mobile.
 *   - Easy to "force open" on desktop via CSS (we just hide the
 *     chevron + always show content; the open/closed state of the
 *     element is irrelevant because content stays visible regardless).
 *
 * A small inline script at page bottom strips the "open" attribute
 * on mobile load so the sections default-closed there. Pre-strip
 * markup ships with "open" set so the desktop default is correct
 * even before JS runs (progressive enhancement).
 */
.hero {
  padding: 1.25rem 0 0;
}
.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
  align-items: start;
}
.hero-headline {
  /* v9.10.51 — Outer slot for breadcrumb + heading + timeline.
   * Order: breadcrumb (top, for navigation context) → heading →
   * timeline. The flex gap handles spacing between siblings
   * uniformly regardless of order. Pre-v9.10.51 the crumb sat
   * below the timeline; operator preferred the crumb on top so
   * visitors see WHERE THEY ARE before reading editorial chrome. */
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.hero-headline .hero-section { padding: 0; }
.hero-headline .crumbs {
  /* Inside .hero-headline the flex gap handles spacing; we don't
   * want the sticky-bar's compensating margin-top to pull other
   * hero items up. Zero out both the margin override and the
   * compensating negative top to restore natural flex behavior.
   * The sticky behavior itself is preserved. */
  margin-bottom: 0;
  margin-top: 0;
  padding-top: .5rem;
}
.hero-filters {
  display: flex;
  flex-direction: column;
  gap: .75rem;
}

/* Each section is a <details> with consistent visual treatment.
 * Border + radius matches the .card aesthetic so the sections read
 * as "filter cards" in the page. */
.hero-filter {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  overflow: hidden;
}
.hero-filter > summary {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: 1rem 1.25rem;
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
}
/* Remove the default ▶ marker in webkit + firefox. */
.hero-filter > summary::-webkit-details-marker { display: none; }
.hero-filter > summary::marker { display: none; }

.hf-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  flex-shrink: 0;
  color: var(--text);
}
.hf-icon svg { width: 18px; height: 18px; }

.hf-label {
  /* The fixed "ATMOSPHERES" / "TIMELINE" / "TAGS" text. */
  color: var(--text);
  flex-shrink: 0;
}
.hf-summary {
  /* The dynamic "ALL ITEMS" / "13/05/2026 — 19/05/2026" / etc.
   * text. Shows the active selection so the user knows what's
   * currently filtered without expanding the section. */
  flex: 1;
  min-width: 0;
  color: var(--text);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: right;
  margin-left: auto;
}
.hf-chevron {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--muted);
  transition: transform .2s;
}
.hf-chevron svg { width: 14px; height: 14px; }
.hero-filter[open] > summary > .hf-chevron {
  transform: rotate(180deg);
}

.hf-content {
  padding: 0 1.25rem 1.25rem;
}

/* ----- Atmosphere list (vertical, count right-aligned, hairline
 *       between rows). Replaces the horizontal chip strip for the
 *       home hero context. */
.hf-atmo-list {
  display: flex;
  flex-direction: column;
}
.hf-atmo-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--border);
  text-decoration: none;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  transition: color .15s;
}
.hf-atmo-row:last-child { border-bottom: 0; }
.hf-atmo-row:hover { color: var(--text); }
.hf-atmo-row.is-active {
  color: var(--text);
  /* Heavy underline on the active row matches the image-3 desktop
   * mock — the active atmosphere reads as "you are here" with the
   * solid baseline emphasis. */
  border-bottom-color: var(--text);
}
.hf-atmo-row .hf-atmo-icon {
  font-size: 14px;
  margin-right: .35rem;
}
.hf-atmo-row .hf-atmo-count {
  color: var(--muted);
  font-weight: 500;
  letter-spacing: .05em;
}

/* ----- Timeline (range input + preset chips). */
.hf-tl-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: .55rem;
}
.hf-tl-range {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .75rem 1rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  margin-bottom: 1rem;
}
.hf-tl-range svg { width: 16px; height: 16px; color: var(--muted); flex-shrink: 0; }
.hf-tl-range input[type="date"] {
  border: 0;
  background: transparent;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  font-family: inherit;
  /* Native date input width is browser-flexible; this baseline
   * keeps both From and To inputs evenly sized inside the row. */
  width: 8em;
  min-width: 0;
  padding: 0;
}
.hf-tl-range input[type="date"]:focus { outline: 0; }
/* Hide the browser's native calendar icon — we already render
 * one calendar SVG outside the input as a row-level affordance,
 * and the native icon (rendered inside each input on the right)
 * was clipping the date value on narrow mobile widths (last
 * digit of the year hidden behind the icon, as seen in the
 * operator's screenshot).
 *
 * opacity:0 + width:100% on the indicator keeps it CLICKABLE
 * (so tapping anywhere on the input still opens the native
 * picker on iOS/Android) but invisible. position:absolute lets
 * us anchor it across the full input width so the entire field
 * remains a tap target.
 *
 * Firefox doesn't expose a pseudo-element for its picker
 * indicator and doesn't show one by default, so no equivalent
 * rule is needed there. */
.hf-tl-range input[type="date"]::-webkit-calendar-picker-indicator {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.hf-tl-range input[type="date"] {
  position: relative;
}
.hf-tl-range .hf-tl-sep { color: var(--muted); }
.hf-tl-presets {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}
.hf-tl-preset {
  display: inline-flex;
  align-items: center;
  padding: .55rem 1rem;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: background .15s, color .15s, border-color .15s;
}
.hf-tl-preset:hover {
  border-color: var(--border-strong);
}
.hf-tl-preset.is-active {
  background: var(--text);
  color: var(--bg);
  border-color: var(--text);
}

/* ----- Tag checkbox list. */
.hf-tag-list {
  display: flex;
  flex-direction: column;
  gap: .15rem;
}
.hf-tag-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .55rem 0;
  text-decoration: none;
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  transition: color .15s;
}
.hf-tag-row:hover { color: var(--text); }
.hf-tag-row.is-active { color: var(--text); }
.hf-tag-box {
  /* The checkbox-style indicator. 16x16 square; checked state shows
   * an accent-colored checkmark.  */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px; height: 16px;
  flex-shrink: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 3px;
  color: transparent;
  transition: border-color .15s, color .15s;
}
.hf-tag-row.is-active .hf-tag-box {
  /* Use the brand accent for the checked state. var(--accent) is
   * the same pink/rose used for active highlights elsewhere. */
  color: var(--accent, #e11d48);
  border-color: var(--accent, #e11d48);
}
.hf-tag-box svg { width: 12px; height: 12px; }
.hf-tag-row.is-active .hf-tag-icon {
  /* Reserved for tags that ship with a leading emoji (operator-set);
   * doesn't affect the box but inherits the active color. */
  color: var(--text);
}

/* Tag clear-all link, rendered when 2+ tags active. */
.hf-tag-clear {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin-top: .75rem;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
}
.hf-tag-clear:hover { color: var(--text); }
.hf-tag-clear svg { width: 11px; height: 11px; }

/* ----------------------------------------------------------
 * Desktop: 3-column layout, sections always expanded, chevron
 * hidden, summary acts as a quiet section label.
 * ---------------------------------------------------------- */
@media (min-width: 720px) {
  /* New structure (v9.8.1):
   *
   *   ┌─────────────────────────────────────────────┐
   *   │ headline (h1)                               │
   *   │ TIMELINE — date inputs + presets            │
   *   │ breadcrumb                                  │
   *   ├──────────────────────┬──────────────────────┤
   *   │ ATMOSPHERES          │ TAGS                 │
   *   │ (collapsible card)   │ (collapsible card)   │
   *   └──────────────────────┴──────────────────────┘
   *
   * Pre-fix the layout was a 3-col grid with headline left,
   * timeline middle, atmospheres+tags right. The operator noted
   * (a) atmospheres often produced a tall right column next to
   * a short timeline column (dead space), and (b) when filtered
   * to a non-events atmosphere the middle column went empty.
   * Moving timeline up into the headline block solves both: every
   * filter is at the top OR below in a tight 2-col row, no
   * gymnastics needed.
   *
   * The two cells flow naturally:
   *   - .hero-headline takes the full grid row 1 (grid-column 1 / -1)
   *   - .hero-filters takes grid row 2 with a nested 2-col grid for
   *     atmospheres + tags.
   */
  .hero-grid {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
  .hero-headline {
    grid-column: 1 / -1;
    grid-row: 1;
  }
  .hero-filters {
    grid-column: 1 / -1;
    grid-row: 2;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.5rem;
    align-items: start;
  }
  /* When tags section is absent, atmospheres takes the full width.
   * :has() handles this without a server-side class hint; older
   * browsers fall through to a half-width atmospheres + empty
   * right column — degraded but functional. */
  .hero-filters:not(:has(.hero-filter.tags)) {
    grid-template-columns: 1fr;
  }

  /* Headline block: heading on top, timeline below (compact
   * inline form), breadcrumb at the bottom. Generous gap between
   * heading and timeline so the date controls don't crowd the
   * "what's On" wordmark. */
  .hero-headline {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
  }
  /* Timeline inside the headline — borderless, just the inputs +
   * presets row. The whole thing reads as a "primary control bar"
   * under the heading, not as a card. Section label stays for
   * orientation; chevron + active-summary text are hidden because
   * the inputs themselves communicate the current state. */
  .hero-headline .hero-filter.timeline {
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
    margin: 0;
  }
  .hero-headline .hero-filter.timeline > summary {
    cursor: default;
    padding: 0 0 .5rem;
    font-size: 10px;
    letter-spacing: .25em;
    color: var(--muted);
    pointer-events: none;
  }
  .hero-headline .hero-filter.timeline > summary .hf-icon,
  .hero-headline .hero-filter.timeline > summary .hf-summary,
  .hero-headline .hero-filter.timeline > summary .hf-chevron {
    display: none;
  }
  .hero-headline .hero-filter.timeline .hf-content {
    padding: 0;
    /* Force expanded regardless of <details open> attribute. The
     * inputs and chips are the primary affordance; collapsing them
     * would hide the most-used filter behind a click. */
    display: block !important;
  }
  /* Lay out the timeline section as one horizontal row on desktop:
   * "SELECT RANGE" label | date inputs | preset chips. Tighter than
   * the mobile stacked form. */
  .hero-headline .hero-filter.timeline .hf-tl-label {
    display: none;
  }
  .hero-headline .hero-filter.timeline .hf-content {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
  }
  .hero-headline .hero-filter.timeline .hf-tl-range {
    margin-bottom: 0;
    flex: 0 0 auto;
  }
  .hero-headline .hero-filter.timeline .hf-tl-presets {
    flex: 0 0 auto;
  }

  /* Breadcrumb at the bottom of headline: sits below timeline. */
  .hero-headline .crumbs {
    margin-bottom: 0;
    margin-top: 0;
  }
}

/* Mobile: keep accordion behavior. Adjust the date inputs so two
 * fit comfortably on narrow screens; presets wrap. Also make the
 * timeline card hide its inline label on narrow widths because the
 * accordion summary already announces the section. */
@media (max-width: 540px) {
  .hf-tl-range { padding: .65rem .85rem; gap: .35rem; }
  .hf-tl-range input[type="date"] { width: 7.4em; font-size: 13px; }
  .hero-filter > summary { padding: .85rem 1rem; font-size: 12px; }
  .hf-content { padding: 0 1rem 1rem; }
}

/* ============================================================
   BENTO GRID — unfiltered home only. Editorial sizing.
   ============================================================ */
.bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 240px;
  gap: 1rem;
}
@media (max-width: 1100px) { .bento { grid-template-columns: repeat(3, 1fr); grid-auto-rows: 220px; } }
@media (max-width: 800px)  { .bento { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 200px; gap: .75rem; } }
@media (max-width: 480px)  { .bento { grid-template-columns: 1fr; grid-auto-rows: 220px; } }

.bento .span-2x2 { grid-column: span 2; grid-row: span 2; }
.bento .span-2x1 { grid-column: span 2; }
@media (max-width: 480px) {
  .bento .span-2x2 { grid-row: span 2; }
  .bento .span-2x1 { grid-row: span 1; }
  .bento .span-2x2, .bento .span-2x1 { grid-column: span 1; }
}

/* ---------- Uniform grid (drill view, search results, favorites) ---------- */
.uniform-grid {
  display: grid;
  /* Cards stay between 220–280px wide so a single result doesn't
     stretch full-row-wide. Wide viewports get more columns
     naturally; narrow ones get fewer. */
  grid-template-columns: repeat(auto-fill, minmax(220px, 280px));
  justify-content: start;
  gap: 1rem;
}
@media (max-width: 480px) {
  .uniform-grid { grid-template-columns: 1fr; gap: .75rem; }
}

/* ============================================================
   CARD — image-as-background + content overlay
   ============================================================ */
.card {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: var(--card);
  cursor: pointer;
  transition: transform .35s cubic-bezier(.16,1,.3,1);
  isolation: isolate;
  display: block;
  text-decoration: none;
  color: inherit;
}
/* Hover transform only on devices that actually have hover.
 * Without this guard, iOS Safari triggers :hover on touchend and
 * leaves the card translated up by 3px until the next tap elsewhere
 * — visually "stuck", confusing for touch users. */
@media (hover: hover) {
  .card:hover { transform: translateY(-3px); }
}

/* v9.3 — Cards get an aspect-ratio when placed in .uniform-grid
   (drill view, search, favorites). Without this, all .card-*
   children are position:absolute and the card collapses to 0
   height — invisible. The bento sets row heights explicitly via
   grid-auto-rows, so cards there don't need aspect-ratio. */
.uniform-grid > .card {
  aspect-ratio: 1 / 1;
}
@media (max-width: 480px) {
  .uniform-grid > .card { aspect-ratio: 16 / 10; }
}

.card-img {
  position: absolute; inset: 0;
  transition: transform .8s cubic-bezier(.16,1,.3,1);
  background: linear-gradient(135deg, var(--card-elev), var(--card));
}
@media (hover: hover) {
  .card:hover .card-img { transform: scale(1.04); }
}
.card-img img {
  width: 100%; height: 100%; object-fit: cover;
  opacity: .9;
}
.card-img-empty-icon {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 3rem; opacity: .25;
  pointer-events: none;
}
.card-overlay {
  position: absolute; inset: 0;
  background: var(--gradient-overlay);
  pointer-events: none;
}

/* atmosphere accent on a card — thin colored bottom bar */
.card[data-accent] {
  box-shadow: inset 0 -3px 0 var(--card-accent);
}

/* v9.10.26 / v9.10.29 — "No image" card variant. When an operator
   skips the cover image, the card falls back to the atmosphere's
   emoji (placeCard / tripCard). v9.10.26 also shortened the card
   aspect-ratio to make the variant visually distinct; v9.10.29
   reverts that — in a uniform grid, mixing aspect ratios makes the
   layout look broken (rows of different-height cards). Image cards
   and emoji cards now share the same outer dimensions; the only
   differences are inside the .card-img frame:
     - soft neutral background instead of a photo
     - large centered emoji at full opacity (not the .25 watermark
       used for the legacy "missing image" placeholder pattern)
     - de-emphasized overlay so the emoji isn't darkened.
   The accent ribbon and meta block keep their normal positions
   so the card still belongs to its atmosphere visually. */
.card.is-no-image .card-img {
  background: linear-gradient(135deg, var(--card) 0%, var(--card-elev) 100%);
}
.card.is-no-image .card-img-empty-icon {
  font-size: clamp(2.5rem, 7vw, 4rem);
  opacity: 1;
}
/* Slightly de-emphasize the overlay so the emoji isn't darkened. */
.card.is-no-image .card-overlay {
  background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.25) 100%);
}

/* card top row (badge + actions) */
.card-top {
  position: absolute; top: 1rem; left: 1rem; right: 1rem;
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: .5rem;
  z-index: 2;
  pointer-events: none;
}
.card-top > * { pointer-events: auto; }

.badge {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .3rem .6rem;
  background: rgba(255,255,255,.12);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 7px;
  font-size: 9px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--on-image);
}
.badge-live {
  background: #ef4444;
  color: white;
  padding: .15rem .4rem;
  border-radius: 4px;
  font-size: 9px; font-weight: 800;
  letter-spacing: .05em;
}
.badge-live::before {
  content: '';
  display: inline-block;
  width: 6px; height: 6px;
  background: white;
  border-radius: 50%;
  margin-right: .35rem;
  animation: pulse 1.6s ease-in-out infinite;
  vertical-align: 1px;
}
@keyframes pulse {
  0%,100% { opacity: 1; }
  50%     { opacity: .35; }
}

/* hover-reveal action buttons (favorite, share) */
.card-actions {
  display: flex; gap: .35rem;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity .25s, transform .25s;
}
.card:hover .card-actions,
.card:focus-within .card-actions {
  opacity: 1; transform: translateY(0);
}
@media (hover: none) {
  /* Touch devices: actions always visible (lighter weight) */
  .card-actions { opacity: 1; transform: none; }
}
.action-btn {
  width: 34px; height: 34px;
  border-radius: 50%;
  background: rgba(255,255,255,.12);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.16);
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  transition: all .2s;
}
.action-btn:hover { background: white; color: black; }
.action-btn svg { width: 14px; height: 14px; }
.action-btn[aria-pressed="true"] {
  background: #ef4444;
  border-color: #ef4444;
}
.action-btn[aria-pressed="true"] svg { fill: currentColor; }
.action-btn[aria-pressed="true"]:hover { background: white; color: #ef4444; }
.action-btn[aria-pressed="true"]:hover svg { fill: currentColor; }
/* Detail-page heart button (.btn rather than .action-btn) gets the
 * same filled treatment when active. Uses the same aria-pressed
 * attribute for consistency. */
.btn[aria-pressed="true"] svg { fill: currentColor; color: #ef4444; }

/* card bottom (text content) */
.card-bottom {
  position: absolute; bottom: 1.1rem; left: 1.25rem; right: 1.25rem;
  z-index: 2;
  color: var(--on-image);
}
.card-name {
  font-size: 1.35rem; font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
  margin-bottom: .25rem;
  display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.card-meta {
  font-size: 10px; font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase;
  color: rgba(255,255,255,.72);
  display: flex; align-items: center; gap: .35rem;
  flex-wrap: wrap;
}
.card-meta .dot { opacity: .5; }

/* v9.10.31 — Tag chips on placeCard. Sit below the subtype line so
   they read as "attributes of this place" (e.g. French, Rooftop)
   distinct from the subtype's identity classification (Bakery &
   Pastry, Restaurant). Visual style is a soft glass pill — same
   family as the top-row .badge but lower-contrast, mixed case, and
   sized smaller so they sit politely under the name rather than
   competing with it. */
.card-tags {
  display: flex; flex-wrap: wrap; gap: .25rem;
  margin-top: .4rem;
}
.card-tag {
  display: inline-flex; align-items: center; gap: .25rem;
  padding: .2rem .5rem;
  background: rgba(255,255,255,.14);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 999px;
  font-size: 10px; font-weight: 600;
  letter-spacing: .02em;
  color: rgba(255,255,255,.92);
  line-height: 1.2;
}
/* On no-image cards the background is a soft light gradient, so
   the glass-on-photo effect doesn't read. The bottom area is still
   darkened by .card-overlay (linear-gradient 0-25% black) so white
   text stays legible; we just give the pill a slightly stronger
   stroke and darker fill so it doesn't disappear into the gradient. */
.card.is-no-image .card-tag {
  background: rgba(0,0,0,.18);
  border-color: rgba(255,255,255,.22);
}

/* large card variant — bigger type */
.card.span-2x2 .card-name {
  font-size: clamp(1.65rem, 3vw, 2.4rem);
  letter-spacing: -0.025em;
  -webkit-line-clamp: 3;
}
.card.span-2x2 .card-bottom {
  bottom: 1.5rem; left: 1.65rem; right: 1.65rem;
}
.card.span-2x2 .card-meta { font-size: 11px; }

/* wide card */
.card.span-2x1 .card-name {
  font-size: 1.55rem;
}

/* trip metadata strip on the card */
.trip-meta {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: .65rem;
  font-size: 11px; font-weight: 500;
  letter-spacing: .04em;
  color: rgba(255,255,255,.85);
  margin-top: .25rem;
}
.trip-meta-item {
  display: inline-flex; align-items: center; gap: .25rem;
}

/* hosted event nested mini-card (Pavillon-style)
   v9.10.39 — Full-width mini-card (was max-width:65%). When a place
   hosts an upcoming event, that event is meaningful info — the
   visitor wants to read the event name without it being truncated.
   The pill carries the matching atmosphere's accent color (set
   inline via style="background:..." on the inner .hosted-event-icon)
   so the embedded event reads as part of its atmosphere visually. */
.hosted-event {
  position: absolute; bottom: 1.1rem; right: 1.25rem; left: 1.25rem;
  z-index: 3;
  background: rgba(255,255,255,.12);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 12px;
  padding: .55rem .75rem;
  display: flex; align-items: center; gap: .6rem;
  /* v9.5 — non-clickable visual span; the card itself is the link. */
  pointer-events: none;
  color: var(--on-image);
}
/* v9.10.39 — Pill is a 2-line stack (day on top, month abbreviation
   below) so a single 30×30 square carries both. Default background
   stays red for back-compat; the inline style overrides with the
   atmosphere accent when the event belongs to one. */
.hosted-event-icon {
  width: 32px; min-height: 32px;
  background: #ef4444;
  border-radius: 8px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  color: white;
  letter-spacing: .04em;
  flex-shrink: 0;
  padding: .15rem 0;
  line-height: 1;
}
.hosted-event-day {
  font-size: 13px; font-weight: 800;
  line-height: 1;
}
.hosted-event-month {
  font-size: 8px; font-weight: 700;
  letter-spacing: .1em;
  opacity: .92;
  margin-top: 1px;
}
/* "NOW" ongoing badge stays urgent-red even when an atmosphere
   accent is configured — temporal "happening right now" is a
   stronger signal than the atmosphere's visual identity. */
.hosted-event-icon-live {
  background: #ef4444 !important;
  font-size: 9px; font-weight: 800;
}
/* Single-glyph schedule badge (recurring events with only a
   weekday letter). Same 32×32 footprint as the day/month pill. */
.hosted-event-icon-sched {
  font-size: 13px; font-weight: 800;
}
.hosted-event-info {
  color: var(--on-image);
  min-width: 0;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
}
.hosted-event-info .label {
  font-size: 9px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  opacity: .85;
}
.hosted-event-info .name {
  font-size: 12px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* on a card with hosted event, push the card-bottom up so they don't overlap */
.card.has-hosted .card-bottom {
  bottom: 4.5rem;
}
@media (max-width: 480px) {
  .hosted-event { right: 1rem; left: 1rem; bottom: .95rem; }
  .card.has-hosted .card-bottom { bottom: 4.25rem; }
}

/* ============================================================
   ATMOSPHERE CARD — home grid entry points (v9.1)
   ============================================================
   Visually distinct from component cards:
     - Larger label type
     - Optional italic Playfair description below label
     - Count badge in top-right corner (number of components)
     - Accent block fills the card when no cover image (high color
       saturation — Shopping pink, Park green, etc.)
     - When cover image present, image fills with overlay; label
       sits at the bottom-left in white.
   The bento section .atmo-bento gives these cards generous heights
   so they read as editorial entry points, not list items.
*/
.atmo-bento {
  /* Override default bento row height for slightly taller atmosphere
     cards — they need more breathing room for the editorial label
     and description than component cards do. */
  grid-auto-rows: 280px;
}
@media (max-width: 1100px) { .atmo-bento { grid-auto-rows: 260px; } }
@media (max-width: 800px)  { .atmo-bento { grid-auto-rows: 240px; } }
@media (max-width: 480px)  { .atmo-bento { grid-auto-rows: 240px; } }

.atmo-card {
  position: relative;
  border-radius: 22px;
  overflow: hidden;
  background: var(--card);
  cursor: pointer;
  transition: transform .35s cubic-bezier(.16,1,.3,1);
  isolation: isolate;
  display: block;
  text-decoration: none;
  color: inherit;
}
@media (hover: hover) {
  .atmo-card:hover { transform: translateY(-3px); }
}
.atmo-card .card-img {
  position: absolute; inset: 0;
  transition: transform .8s cubic-bezier(.16,1,.3,1);
}
@media (hover: hover) {
  .atmo-card:hover .card-img { transform: scale(1.04); }
}
.atmo-card .card-img img {
  width: 100%; height: 100%; object-fit: cover;
}
.atmo-card .card-img-empty-icon {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.atmo-card .card-overlay {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, transparent 35%, rgba(0,0,0,0.55) 100%);
  pointer-events: none;
}

/* Top-right count badge */
.atmo-card-top {
  position: absolute; top: 1rem; right: 1rem;
  z-index: 2;
}
.atmo-count {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px; padding: 0 .55rem;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: white;
  border-radius: 999px;
  font-weight: 700; font-size: .85rem;
  letter-spacing: -0.01em;
}
.atmo-count.is-loading {
  animation: pulse 1.6s ease-in-out infinite;
}

/* Card body — label + optional description */
.atmo-card-body {
  position: absolute; bottom: 1.25rem; left: 1.5rem; right: 1.5rem;
  z-index: 2;
}
.atmo-card-label {
  font-family: 'Outfit', sans-serif;
  font-size: clamp(1.15rem, 2vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
  margin-bottom: .35rem;
}
.atmo-card-description {
  font-family: 'Playfair Display', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: .92rem;
  line-height: 1.45;
  opacity: .92;
  display: -webkit-box;
  -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Color modes: white text on image overlay, white on saturated accent block */
.atmo-card.on-image .atmo-card-label,
.atmo-card.on-image .atmo-card-description,
.atmo-card.on-accent .atmo-card-label,
.atmo-card.on-accent .atmo-card-description {
  color: #ffffff;
}
.atmo-card.on-accent .card-overlay {
  /* On accent cards (no cover image) we don't need the dark gradient —
     the accent itself provides text contrast. The .card-img already
     fills with the accent color via inline style. */
  display: none;
}

/* Span variants for bento layout — same names as component bento so
   the .bento grid rules drive sizing identically. */
.atmo-card.span-2x2 .atmo-card-label {
  font-size: clamp(1.65rem, 3vw, 2.5rem);
  letter-spacing: -0.025em;
}
.atmo-card.span-2x2 .atmo-card-description {
  font-size: 1rem;
  -webkit-line-clamp: 4;
}
.atmo-card.span-2x2 .atmo-card-body {
  bottom: 1.65rem; left: 1.85rem; right: 1.85rem;
}
.atmo-card.span-2x1 .atmo-card-label {
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
}

/* Touch — count badge stays the same; nothing to suppress */

/* ============================================================
   TIME CHIP STRIP — secondary filter for events (Today, Tomorrow…)
   ============================================================
   v9.2 — Visually downgraded vs. atmosphere chips so the user
   reads the hierarchy: atmosphere = primary filter (Event/Trip/etc),
   time = secondary refinement within events. Smaller, ghost-style,
   no border. The custom date range form is styled to match —
   no native input chrome leaks through.
*/
.chips-time {
  border-top: 0;
  padding-top: 0;
  /* No edge fade since the strip is tighter and rarely overflows */
  -webkit-mask-image: none;
          mask-image: none;
  gap: .15rem;
  font-size: 12px;
}
.chip-time {
  padding: .35rem .85rem;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: none;
  background: transparent;
  border: 0;
  color: var(--text-soft);
  border-radius: 999px;
}
.chip-time:hover {
  background: var(--pill-bg);
  color: var(--text);
  border: 0;
}
.chip-time.active,
.chip-time.is-active {
  background: var(--text);
  color: var(--bg);
}
.chip-time .chip-count {
  margin-left: .35rem;
  opacity: .55;
  font-weight: 600;
}

.chip-time-range {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .75rem;
  background: var(--pill-bg);
  border-radius: 999px;
  font-size: 11px;
  color: var(--text-soft);
  margin-left: .35rem;
}
.chip-time-range.active {
  background: var(--text);
  color: var(--bg);
}
.chip-time-range .chip-icon {
  font-size: 13px;
  opacity: .7;
  display: inline-flex;
}
.chip-date-input {
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  color: inherit;
  font-family: inherit;
  width: 5.5rem;
  cursor: pointer;
  outline: none;
}
/* v9.6 — Hide the native calendar icon so it doesn't compete with
   our 📅 emoji in the chip. Pre-v9.6 the indicator was stretched
   across the input via position:absolute + inset:0 with opacity:0
   — but absolute-positioned indicators on date inputs anchor to
   the body in some browsers, capturing clicks across the page and
   blocking access to the atmosphere chips above. The simpler
   "display: none" removes the indicator from layout entirely;
   tapping the input opens the picker via the input's own behavior. */
.chip-date-input::-webkit-calendar-picker-indicator {
  display: none;
}
.chip-date-input::-webkit-datetime-edit { color: inherit; padding: 0; }
.chip-date-input::-webkit-datetime-edit-fields-wrapper { padding: 0; }
.chip-date-input::-webkit-inner-spin-button { display: none; }
.chip-date-input::placeholder { color: var(--muted); }
.chip-time-range.active .chip-date-input { color: var(--bg); }
.chip-range-sep {
  opacity: .5;
  font-size: 10px;
}
.chip-time.chip-clear {
  font-size: 14px;
  font-weight: 600;
  padding: .35rem .65rem;
  line-height: 1;
}

/* Header label above the time chips */
.events-toolbar {
  display: flex; flex-direction: column; gap: .5rem;
  margin-bottom: 1.5rem;
}
.events-toolbar-row {
  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
}
.events-section-label {
  font-size: 10px; font-weight: 700;
  letter-spacing: .25em; text-transform: uppercase;
  color: var(--muted);
}

/* ============================================================
   EVENT CARD — date chip + name (one-off events in events row)
   ============================================================ */
/* v9.2 — Two presentations:
     - Image variant: uses .card scaffold; date sits in .event-date-badge
       pinned to the top-left corner (overlay glass pill).
     - Compact variant: side-by-side date chip + info + arrow row,
       used when there's no image and on the home detail's "What's on"
       cards (different selector — .whats-on-item).
*/
/* ============================================================
   EVENT CARD — __access20 visual differentiation (C1 + C2 + C3)
   ============================================================
   Two variants:
     - Image variant: .card.is-event scaffold; horizontal date pill at
       top-left, time pill in card body, ribbon along top edge.
     - Compact variant: .event-card grid: date column + info + arrow.
       Date column is the narrow vertical pill (kept for dense rows
       like the hosted-events strip on a venue's detail page). Same
       ribbon, same time-pill in the info block.

   C3 — atmosphere accent ribbon. Both variants use a ::before pseudo-
   element that paints a 4px stripe along the top edge of the card.
   The color comes from --card-accent, set by inline style only when
   the server attached _atmosphereAccent. Cards without an accent
   skip the ribbon (the pseudo's background is transparent without
   the variable).
*/

/* C1 — Horizontal date pill (image variant)
 * Glass-translucent pill, top-left of image card. Single horizontal
 * line carrying the relative or absolute label (TODAY / TOMORROW /
 * THU 14 MAY / 14 MAY). Tabular numerals so dates align across cards. */
.event-date-pill {
  display: inline-flex; align-items: center;
  padding: .35rem .65rem;
  background: rgba(0,0,0,.72);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: white;
  border-radius: 999px;
  font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  white-space: nowrap;
}
.event-date-pill.is-live {
  background: #ef4444;
}
.event-date-pill.is-today {
  background: rgba(239, 68, 68, .92);
}
.event-date-pill.is-tomorrow {
  background: rgba(0,0,0,.82);
}
.event-date-pill.is-recurring {
  background: rgba(0,0,0,.6);
  font-weight: 600;
}

/* C3 — Accent ribbon along the top edge.
 *
 * Painted via ::before only when the card has a [data-accent]
 * attribute AND .is-event class. Sits ABOVE the image (z-index 2)
 * so it shows over the card's overflow:hidden boundary. Height kept
 * minimal so it reads as a "this is an event" signal without
 * dominating the visual. The atmosphere-color tint reinforces the
 * card's relationship to its parent atmosphere (a Culture & Heritage
 * exhibition shows a culture-tinted ribbon).
 *
 * The image variant has the ribbon overlay the top of the image —
 * z-index above .card-img (which is the absolute-positioned image)
 * but below .card-top (where the date pill sits) so the pill stays
 * crisp above the ribbon.
 */
/* v9.10.43 — Ribbon now ALSO applies to every accented card on a
 * drill page (.atmo-drill-in .card[data-accent]). Pre-fix the ribbon
 * only attached to event cards; on an atmosphere drill page
 * (e.g. /evening-drinks/next-7-days) the place cards visually had no
 * tie to the atmosphere they were filtered by — visitor saw a strip
 * of generic-looking cards with no color signal saying "these all
 * belong to Evening Drinks". The drill-page-scoped rule adds the
 * top edge tint to all cards in the drill view while leaving the
 * home grid alone (place cards on the unfiltered home stay neutral —
 * they belong to many atmospheres and a tinted ribbon there would
 * be visually noisy).
 *
 * The .event-card selector is retained for safety but emits no
 * markup since v9.10.42 (the no-image event card uses .card.is-event
 * now). Will be dropped in a future cleanup pass. */
.card.is-event[data-accent]::before,
.atmo-drill-in .card[data-accent]::before,
.event-card[data-accent]::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4px;
  background: var(--card-accent, transparent);
  z-index: 2;
  pointer-events: none;
}
.card.is-event { position: relative; }
.event-card { position: relative; overflow: hidden; }

/* C2 — Time sub-line below the date pill.
 *
 * Operator design call (Turn __access20c): cluster time WITH the date
 * rather than scattering it into the card body. Pre-fix the time
 * rendered as a separate .event-time-pill in .card-bottom / .event-info,
 * which split temporal info into two zones of the card. Now: a
 * small line directly beneath the date pill, both wrapped in
 * .event-date-block so they read as one temporal cluster.
 *
 * Drops the clock icon — position-below-date conveys "this is a time"
 * without needing the symbol. Cleaner, less visual weight.
 *
 * Tabular numerals so times align vertically across cards in a row. */
.event-date-block {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .25rem;
  flex-shrink: 0;
}
/* v9.10.43 — Time sub-line rendered as a PILL (not muted text).
 *
 * Pre-v9.10.43 the time was a 10px 75%-opacity row of text sitting
 * below the date pill. With the date pill rendered as a bold colored
 * chip, the time read as an afterthought — operators reported "time
 * is not visible enough". v9.10.43 makes time mirror the date pill
 * exactly: same pill shape, same accent logic, same is-{kind}
 * modifiers. The two pills now read as a single temporal block.
 *
 * Color rules (mirroring .event-date-pill / .event-date):
 *   - Default (date/thisweek/recurring): atmosphere accent applied
 *     inline via style="background:..." from pillStyleAttr().
 *   - is-live  → urgent red (hardcoded, overrides accent)
 *   - is-today → urgent red (slightly translucent)
 *   - is-tomorrow → dark (kept neutral; tomorrow isn't urgent)
 *   - is-recurring → dark (no specific date to take accent from)
 */
.event-time-sub {
  display: inline-flex; align-items: center;
  padding: .2rem .55rem;
  background: rgba(0,0,0,.72);
  color: white;
  border-radius: 999px;
  font-size: 10px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.event-time-sub.is-live      { background: #ef4444; }
.event-time-sub.is-today     { background: rgba(239, 68, 68, .92); }
.event-time-sub.is-tomorrow  { background: rgba(0,0,0,.82); }
.event-time-sub.is-recurring { background: rgba(0,0,0,.6); font-weight: 600; }
/* v9.10.43 — Old overrides for .card.is-event .event-time-sub
 * (text-shadow, white color) and .event-card .event-time-sub (muted
 * color) removed: now the time-sub renders as a solid pill with white
 * text on its own colored background, identical treatment regardless
 * of whether the card has an image or not. No shadow needed since the
 * pill is opaque. The .event-card class itself isn't emitted anymore
 * (since v9.10.42 the no-image event card uses .card.is-event), so
 * .event-card-targeted rules are dead weight. */

/* Compact-variant date column (kept from pre-__access20 with relaxed
 * sizing — horizontal label needs more room than the old day/month
 * stack). Width grew from 56px → fluid (auto) so longer labels like
 * "THU 14 MAY" fit. */
.event-date {
  min-width: 56px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent, rgba(239, 68, 68, .92));
  color: white;
  border-radius: 12px;
  padding: .55rem .75rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
  white-space: nowrap;
}
.event-date.is-live      { background: #ef4444; }
.event-date.is-today     { background: rgba(239, 68, 68, .92); }
.event-date.is-tomorrow  { background: rgba(0,0,0,.82); }
.event-date.is-recurring { background: rgba(0,0,0,.55); font-weight: 600; }

.event-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem; align-items: center;
  padding: 1rem 1.15rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  cursor: pointer;
  transition: border-color .2s, transform .2s;
  text-decoration: none;
  color: var(--text);
}
.event-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
}
.event-info { min-width: 0; }
.event-info .name {
  font-size: 1rem; font-weight: 600;
  letter-spacing: -0.01em;
  margin-bottom: .15rem;
}
.event-info .venue {
  font-size: 11px;
  color: var(--muted);
}
.event-card .arrow-end {
  width: 30px; height: 30px;
  border-radius: 50%;
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  color: var(--muted);
  transition: all .2s;
  flex-shrink: 0;
}
.event-card:hover .arrow-end {
  background: var(--text); color: var(--bg); border-color: var(--text);
}

/* ============================================================
   DETAIL VIEW — full-bleed hero + 2-col body
   ============================================================ */
.detail-view {
  background: var(--bg);
  min-height: 100vh;
}
.detail-hero {
  position: relative;
  height: clamp(50vh, 65vh, 80vh);
  width: 100%;
  background: var(--card-elev);
}
.detail-hero img {
  width: 100%; height: 100%; object-fit: cover;
}
.detail-hero::after {
  content: '';
  position: absolute; inset: 0;
  /* v9.3 — Gradient is always dark, regardless of theme. The hero
     always renders white text on top; the gradient must darken the
     bottom of the image for legibility. Pre-v9.3 the gradient faded
     to var(--bg) which is cream in oatmeal theme — that left the
     title sitting on a cream/white blend, illegible.
     v9.10.37 — Three-stop gradient with darker max and earlier
     start. Some operator hero images have bright graphic content
     (poster art with white panels, light photography of facades)
     directly under where the title sits. The two-stop transparent→
     dark gradient left those bright zones bleeding through the
     bottom 50%, making the title hard to scan. The new shape ramps
     in earlier (20% from top) and reaches a deeper black (.85) at
     the bottom — still translucent enough to see the image, but
     reliably darker than any white-text overlay would need. Paired
     with a text-shadow on .detail-title + .detail-eyebrow below
     for belt-and-suspenders legibility on edge-case images. */
  background: linear-gradient(180deg,
    transparent 20%,
    rgba(0, 0, 0, 0.45) 55%,
    rgba(0, 0, 0, 0.85) 100%);
  pointer-events: none;
}
.detail-back {
  position: absolute; top: 1.25rem; left: 1.25rem;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.2);
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  z-index: 5;
  transition: all .2s;
}
.detail-back:hover { background: white; color: black; }
.detail-back svg { width: 20px; height: 20px; }

.detail-hero-content {
  position: absolute; bottom: 2.25rem; left: 2rem; right: 2rem;
  max-width: 64rem;
  color: white;
  z-index: 2;
}
@media (max-width: 720px) {
  .detail-hero-content { left: 1rem; right: 1rem; bottom: 1.5rem; }
}
.detail-eyebrow {
  display: flex; align-items: center; gap: .65rem;
  /* v9.10.41 — Eyebrow now sits BELOW the title (operator preference:
     name first, supporting metadata after). Margin flips from bottom
     to top accordingly so the gap stays the same visually. */
  margin-top: .85rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .25em; text-transform: uppercase;
  opacity: .9;
  flex-wrap: wrap;
  /* v9.10.37 — Soft text-shadow so even when the image has bright
     content directly behind the eyebrow text (operator-uploaded
     hero with white background, light architectural shots, etc.)
     the type stays readable. Pairs with the strengthened gradient
     on .detail-hero::after — gradient handles average images,
     shadow handles edge cases. The shadow is soft (16px blur) so
     it reads as a haze, not a hard outline. */
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
}
.detail-title {
  font-family: 'Outfit', sans-serif;
  font-size: clamp(2rem, 7vw, 5.5rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: .92;
  /* v9.10.37 — Larger shadow on the title (the eyebrow is small
     enough that 8px is enough; the title's 3-5rem letters need a
     wider halo to stay legible against bright image patches). */
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}

.detail-body {
  max-width: 1200px;
  margin: 0 auto;
  padding: 4rem 2rem 3rem;
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 4rem;
}
@media (max-width: 900px) {
  .detail-body { grid-template-columns: 1fr; gap: 2.5rem; padding: 2.5rem 1rem; }
}
.detail-section { margin-bottom: 2.5rem; }
.detail-section:last-child { margin-bottom: 0; }
.detail-section.bordered {
  border-top: 1px solid var(--border);
  padding-top: 2.5rem;
}
.detail-h4 {
  font-family: 'Playfair Display', serif;
  font-style: italic;
  font-size: 1.5rem; font-weight: 600;
  margin-bottom: 1rem;
}
.detail-section-label {
  font-size: 11px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1rem;
}
.detail-description {
  font-size: 1.1rem;
  color: var(--text-soft);
  line-height: 1.65;
  margin-bottom: 1.5rem;
}

/* v9.10.30 — Margin-bottom matches .detail-description so the action
   row + heading + description triplet has consistent vertical rhythm
   when actions sit above the heading. Pre-v9.10.30 actions were at
   the BOTTOM of the description block (after paragraphs), so no
   bottom margin was needed; now they're at the TOP and need room
   before the .detail-h4 heading. */
.detail-actions { display: flex; flex-wrap: wrap; gap: .65rem; margin-bottom: 1.5rem; }

/* ============================================================
   OPENING HOURS — v9.10.6 sidebar dropdown (Google-Maps style)
   ============================================================ */
/* Renders inside the sidebar card as a <details> dropdown. Summary
   row (always visible) shows today's status: "Open today · 10:00–20:00"
   or "Closed today · Opens Wed". Expanded view shows all 7 days
   with today highlighted. Inherits the small label/value layout of
   neighboring sidebar rows (Best Time, Address, etc). */
.oh-details {
  margin-top: .15rem;
}
.oh-summary-row {
  /* Suppress browser default disclosure triangle so we can use a
     custom chevron that matches the sidebar's quiet aesthetic. */
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: .4rem;
  font-size: .92rem;
  color: var(--text);
  padding: .1rem 0;
}
.oh-summary-row::-webkit-details-marker { display: none; }
.oh-summary-text { flex: 1; }
.oh-chevron {
  font-size: .85rem;
  color: var(--muted);
  transition: transform .15s ease;
  display: inline-block;
}
.oh-details[open] .oh-chevron { transform: rotate(180deg); }
.oh-status { font-weight: 600; }
.oh-status-open { color: #16a34a; }
.oh-status-closed { color: #c43; }
.oh-times, .oh-next { color: var(--text-soft); }
/* v9.10.45 — Active seasonal period label, shown next to "Open today"
 * status when the place is in a defined period (Summer, Winter, etc.).
 * Italic + muted so it reads as supplementary context — visitors see
 * the operating hours first, then notice the seasonal flag. */
.oh-period {
  font-style: italic;
  color: var(--text-soft);
  font-weight: 400;
  font-size: .92em;
}
.oh-week-list {
  margin-top: .5rem;
  padding-top: .4rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: .12rem;
}
.oh-day-row {
  display: flex;
  justify-content: space-between;
  gap: .75rem;
  padding: .2rem .35rem;
  border-radius: 4px;
  font-size: .85rem;
}
.oh-day-row .oh-day-name {
  text-transform: capitalize;
  color: var(--text-soft);
}
.oh-day-row .oh-day-hours { color: var(--text); }
.oh-day-row-highlight {
  background: color-mix(in srgb, var(--accent, #4f46e5) 10%, transparent);
  font-weight: 600;
}
.oh-day-row-highlight .oh-day-name,
.oh-day-row-highlight .oh-day-hours { color: var(--text); }
.oh-day-row .oh-day-hours em {
  color: var(--muted);
  font-style: italic;
}
.oh-exception-note {
  margin: .5rem 0;
  padding: .4rem .55rem;
  background: color-mix(in srgb, #d97706 12%, transparent);
  border-radius: 6px;
  font-size: .8rem;
  color: var(--text);
}

/* Budget tier display — sidebar row showing $ / $$ / $$$ */
.budget-tier {
  font-weight: 600;
  letter-spacing: .05em;
}
.budget-tier .budget-active { color: var(--text); }

/* v9.10.11 — Free tier renders as a small green pill so it doesn't
 * look like a $-ladder string. Visual cue that this venue/event
 * has NO admission cost is more useful than a Stylized "Free" word. */
.budget-tier-free {
  /* Override the letter-spacing inherited from .budget-tier so the
   * word "Free" doesn't read as F R E E. */
  letter-spacing: 0;
  font-weight: 600;
}
.budget-tier-free .budget-active {
  display: inline-block;
  padding: .15rem .55rem;
  border-radius: 999px;
  background: color-mix(in srgb, #16a34a 14%, transparent);
  color: #16a34a;
  font-size: .8rem;
}

/* ============================================================
   LINK PILLS — v9.10.10 sidebar social/external links
   ============================================================ */
/* Icon-only round pills, unified color. Each pill is a tap target
 * containing a single brand SVG that inherits the surrounding text
 * color (via fill="currentColor"). Hover/focus state lights up with
 * the accent color. Sized for comfortable touch interaction (40×40
 * minimum tap target on mobile per Apple HIG).
 */
.link-pills {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: .35rem;
}
.link-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--card, #f5f5f0);
  border: 1px solid var(--border, #e3e3dc);
  color: var(--text-soft, var(--text));
  text-decoration: none;
  transition: background .15s ease, border-color .15s ease, color .15s ease, transform .12s ease;
  line-height: 1;
}
.link-pill:hover,
.link-pill:focus-visible {
  background: color-mix(in srgb, var(--accent, #4f46e5) 10%, var(--card, #f5f5f0));
  border-color: color-mix(in srgb, var(--accent, #4f46e5) 40%, var(--border, #e3e3dc));
  color: var(--text);
  transform: translateY(-1px);
}
/* Icon-only variant: a uniform square pill that holds just the SVG.
 * 36×36 reads as a solid touch target without looking heavy in the
 * sidebar. Border-radius slightly smaller than half-width gives a
 * "soft square" rather than a perfect circle — feels less stamp-like.
 */
.link-pill-icon-only {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  padding: 0;
}
.link-pill-icon-only svg {
  display: block;
  /* fill is set to currentColor on the SVG itself; this just keeps
   * the path crisp at the rendered size. */
  width: 18px;
  height: 18px;
}

/* v9.10.46 / v9.10.47 / v9.10.48 — Providers section.
 *
 * Inline accordion list grouped by subtype on the consumer's detail
 * page. Each subtype gets its own small heading (BIKE RENTAL,
 * FITNESS CENTER, etc.); each provider under that heading is a
 * <details> block with the name + city always visible and the
 * description + links revealed on click. Native <details>/<summary>
 * handles the show/hide — no JS needed.
 *
 * Visual design:
 *   - Section root has top/bottom margin to separate from neighbors
 *     (Lineup above, What's On below).
 *   - Each subtype group has its own heading (10px tracked caps,
 *     muted) — matches other detail-page section labels so
 *     subtype groups feel like peers of Gallery / Lineup / What's On.
 *   - Group lists share thin top/bottom borders so multiple groups
 *     stacked together still read as distinct clusters.
 *   - Per-provider rows: name in regular weight, city as middle-dot
 *     suffix in muted color (so name dominates the eye).
 *   - Chevron rotates 180° on open.
 */
.providers-section {
  margin: 2rem 0;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.provider-group-heading {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 .6rem;
}
.providers-list {
  border-top: 1px solid var(--border);
}
.provider-block {
  border-bottom: 1px solid var(--border);
}
.provider-block[open] .provider-chevron {
  transform: rotate(180deg);
}
.provider-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .9rem 0;
  cursor: pointer;
  list-style: none;
  /* Hide the default disclosure triangle (Safari + Firefox) so the
   * custom chevron on the right is the only indicator. */
}
.provider-summary::-webkit-details-marker {
  display: none;
}
.provider-summary-text {
  display: flex;
  align-items: baseline;
  gap: 0;
  min-width: 0;
  flex: 1;
  flex-wrap: wrap;
}
.provider-name {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.005em;
}
.provider-city {
  font-size: .9rem;
  color: var(--muted);
  font-weight: 400;
}
.provider-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  transition: transform .2s ease;
  flex-shrink: 0;
}
.provider-content {
  padding: 0 0 1rem;
  display: flex;
  flex-direction: column;
  gap: .85rem;
}
.provider-description {
  font-size: .9rem;
  line-height: 1.5;
  color: var(--text-soft, var(--text));
  white-space: pre-wrap;
}
/* Provider links cluster reuses .link-pill / .link-pill-icon-only
 * styles defined above. The container just gives them flex layout
 * matching the sidebar's .link-pills. */
.provider-links {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}

/* ============================================================
   LINEUP — v9.10.9 main-column artist grid (festivals, concerts)
   ============================================================ */
/* Auto-fill grid: 3-4 columns on desktop, 2 on tablet, 1 on phone.
 * Each item is a name (bold) + optional role (small + muted).
 * Festivals with 50+ artists wrap naturally without overflow.
 */
.lineup-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: .75rem 1.25rem;
  margin-top: .5rem;
}
.lineup-item {
  padding: .15rem 0;
}
.lineup-name {
  font-weight: 600;
  font-size: .95rem;
  color: var(--text);
}
.lineup-role {
  font-size: .8rem;
  color: var(--text-soft, var(--muted));
  margin-top: .1rem;
}

/* ============================================================
   ARTICLE CONTENT — v9.10.12 long-form rich text section
   ============================================================ */
/* Renders below the description. Editorial typography — slightly
 * larger leading, generous heading spacing, pulled-quote treatment
 * for blockquotes. Inherits the page's text color so light/dark
 * themes work automatically.
 */
.article-content {
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text);
}
.article-content h2 {
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 1.75rem 0 .65rem;
  color: var(--text);
}
.article-content h3 {
  font-size: 1.2rem;
  font-weight: 600;
  margin: 1.25rem 0 .5rem;
  color: var(--text);
}
.article-content h4 {
  font-size: 1.05rem;
  font-weight: 600;
  margin: 1rem 0 .35rem;
  color: var(--text);
}
.article-content p { margin: .65rem 0; }
.article-content ul,
.article-content ol {
  margin: .75rem 0;
  padding-left: 1.6rem;
}
.article-content li { margin: .25rem 0; }
.article-content a {
  color: var(--accent, #4f46e5);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.article-content a:hover { text-decoration: none; }
.article-content blockquote {
  margin: 1.25rem 0;
  padding: .5rem 0 .5rem 1.1rem;
  border-left: 3px solid var(--accent, #4f46e5);
  color: var(--text-soft, var(--muted));
  font-style: italic;
  font-size: 1.05rem;
}
.article-content hr {
  border: 0;
  border-top: 1px solid var(--border, #e3e3dc);
  margin: 1.5rem 0;
}
.article-content b, .article-content strong { font-weight: 600; }
.article-content u { text-decoration-thickness: 1px; text-underline-offset: 3px; }
.article-content s, .article-content strike {
  text-decoration: line-through;
  color: var(--text-soft, var(--muted));
}
.article-content h2:first-child,
.article-content h3:first-child,
.article-content h4:first-child,
.article-content p:first-child { margin-top: 0; }
.article-content > *:last-child { margin-bottom: 0; }

/* ============================================================
   BUTTONS
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .5rem;
  padding: .85rem 1.65rem;
  font-size: 11px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  border-radius: 999px;
  transition: all .2s;
  cursor: pointer;
  border: 0;
  text-decoration: none;
  white-space: nowrap;
}
.btn-primary { background: var(--text); color: var(--bg); }
.btn-primary:hover { opacity: .88; }
.btn-secondary {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text);
}
.btn-secondary:hover { background: var(--text); color: var(--bg); border-color: var(--text); }
.btn-ghost { padding: .65rem 1.25rem; font-size: 10px; }
.btn-sm { padding: .5rem 1rem; font-size: 10px; letter-spacing: .12em; }
.btn-block { display: flex; width: 100%; }
.btn svg { width: 14px; height: 14px; }

/* ============================================================
   "WHAT'S ON" — hosted events list in detail view
   ============================================================ */
.whats-on { display: flex; flex-direction: column; gap: .75rem; }
.whats-on-item {
  display: flex; align-items: center; gap: 1.1rem;
  padding: 1.1rem 1.3rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  transition: border-color .2s, transform .2s;
  cursor: pointer;
  text-decoration: none;
  color: var(--text);
}
.whats-on-item:hover {
  border-color: var(--border-strong);
  transform: translateX(2px);
}
.whats-on-item.upcoming { opacity: .75; }
.whats-on-bar {
  width: 5px; align-self: stretch;
  border-radius: 999px;
  background: #ef4444;
  flex-shrink: 0;
}
.whats-on-bar.upcoming { background: var(--muted); }
.whats-on-info { flex: 1; min-width: 0; }
.whats-on-info .label {
  font-size: 9px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: #ef4444;
  margin-bottom: .25rem;
}
.whats-on-info .label.upcoming { color: var(--muted); }
.whats-on-info .name {
  font-size: 1rem; font-weight: 600;
  letter-spacing: -0.005em;
  margin-bottom: .15rem;
}
.whats-on-info .date {
  font-size: 12px; color: var(--muted);
}
.whats-on-arrow {
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  transition: all .2s;
  flex-shrink: 0;
}
.whats-on-item:hover .whats-on-arrow {
  background: var(--text); color: var(--bg);
  border-color: var(--text);
}
.whats-on-arrow svg { width: 14px; height: 14px; }

/* v9.10.55 — Per-row upcoming events disclosure on listing pages.
 *
 * Each member row in a listing can have an attached <details> block
 * showing the events hosted at that venue. The disclosure sits as a
 * sibling of the .listing-row anchor inside a .listing-row-wrap
 * grouping (HTML disallows nested interactives so <details> can't
 * live inside the <a>).
 *
 * Visual treatment: the disclosure is visually attached to the row
 * above via shared rounded corners and the same card surface. The
 * row gets flat-bottom corners when its sibling disclosure is
 * present, so the two read as one card. The disclosure-summary
 * line is a thin band with the count and toggle chevron.
 *
 * Hide native disclosure marker so we render our own chevron icon
 * for consistent styling across browsers. */
.listing-row-wrap {
  /* The grouping element. No box of its own — children paint the
   * card. Vertical stack via the parent .listing-rows flex. */
}
.listing-row-wrap .listing-row-events {
  margin: 0;
  /* Visually attach to the row above: remove the gap, share the
   * card surface, drop the top-border, round only the bottom. */
  margin-top: -1px; /* overlap the row's bottom border */
  background: var(--card);
  border: 1px solid var(--border);
  border-top: 1px dashed var(--border);
  border-radius: 0 0 18px 18px;
  overflow: hidden;
}
/* When a row is followed by an events disclosure, flatten its
 * bottom corners so the two pieces merge into a single card. */
.listing-row-wrap .listing-row-events ~ * { display: none; } /* defensive: only one disclosure per wrap */
.listing-row-wrap:has(.listing-row-events) .listing-row {
  border-radius: 18px 18px 0 0;
}

.listing-row-events summary { list-style: none; cursor: pointer; }
.listing-row-events summary::-webkit-details-marker { display: none; }
.listing-row-events summary::marker { display: none; content: ''; }

.listing-row-events-summary {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .7rem 1rem;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  transition: color .15s, background .15s;
}
.listing-row-events-summary:hover {
  color: var(--text);
  background: color-mix(in srgb, var(--text) 3%, transparent);
}
.listing-row-events-summary .listing-row-events-label {
  /* The text portion of the summary line. The count pill and
   * toggle chevron sit to its right. */
}
.listing-row-events-summary .listing-count {
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
  font-weight: 600;
}
.listing-row-events-summary .listing-row-events-toggle {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  color: var(--muted);
  transition: transform .2s ease, color .2s ease;
}
.listing-row-events[open] .listing-row-events-toggle {
  transform: rotate(180deg);
}
.listing-row-events-summary:hover .listing-row-events-toggle {
  color: var(--text);
}

.listing-row-events-list {
  /* The events themselves. Indent slightly from the disclosure's
   * edge so they read as nested under the row above, and add
   * vertical breathing room before the first card and after the
   * last. The .whats-on parent flex gap handles inter-card spacing. */
  padding: .25rem 1rem 1rem;
}

/* ============================================================
   LISTING MEMBER ROWS (v9.10.53)
   ============================================================
   Vertical-stack horizontal-row layout for listing detail
   pages. Each row is a wide entry with thumbnail left, name +
   subtype + description in the middle, and a chevron on the
   right. Visually distinct from atmosphere drill cards so
   visitors immediately read the page as "curated list" not
   "another category page".
   ============================================================ */
.listing-members-section { margin-top: 1.5rem; }
.listing-members-section .detail-section-label {
  /* Section label gets the same treatment as Gallery / What's On
   * section labels, plus a small count pill to make the list
   * feel quantified ("Places in this list (5)"). */
  margin-bottom: .85rem;
}
.listing-count {
  color: var(--muted);
  font-weight: 600;
}

.listing-rows {
  display: flex;
  flex-direction: column;
  gap: .85rem;
}
.listing-row {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 18px;
  transition: border-color .2s, transform .2s;
  cursor: pointer;
  text-decoration: none;
  color: var(--text);
}
.listing-row:hover {
  border-color: var(--border-strong);
  transform: translateX(2px);
}

/* Thumbnail: fixed-width square on desktop (130px), shrinks on
 * mobile to keep the row from dominating narrow viewports.
 * object-fit:cover crops cleanly regardless of source aspect. */
.listing-row-thumb {
  flex-shrink: 0;
  width: 130px;
  height: 130px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg);
  position: relative;
}
.listing-row-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.listing-row-thumb-placeholder {
  /* Soft fallback when no cover image is set on the member —
   * displays the generic place glyph centered in the thumb slot.
   * Same visual weight as image thumbnails so the row layout
   * stays uniform across members with and without images. */
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.5rem;
  opacity: .35;
}

.listing-row-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .25rem;
}
.listing-row-kicker {
  /* Subtype kicker: small uppercase tracked label above the
   * name. Matches the .whats-on-info .label treatment but in
   * a neutral muted color (no red event-ness). */
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .15em;
  text-transform: uppercase;
  color: var(--muted);
}
.listing-row-name {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  color: var(--text);
  line-height: 1.25;
}
.listing-row-desc {
  /* Description trimmed to 2 lines via line-clamp so verbose
   * operator copy doesn't bloat the row. The full text shows
   * on the member's own detail page. line-clamp is widely
   * supported (Chrome, Safari, Firefox); on the rare unsupported
   * browser the description still renders, just full-length. */
  font-size: .9rem;
  color: var(--text-soft, var(--muted));
  line-height: 1.4;
  margin-top: .15rem;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.listing-row-arrow {
  flex-shrink: 0;
  color: var(--muted);
  transition: color .2s, transform .2s;
}
.listing-row:hover .listing-row-arrow {
  color: var(--text);
  transform: translateX(2px);
}

/* Mobile: shrink the thumb so the body text gets the room it
 * needs. Keep the row horizontal — vertical stack would lose
 * the "guide list" visual signature. */
@media (max-width: 480px) {
  .listing-row { padding: .75rem; gap: .85rem; }
  .listing-row-thumb { width: 88px; height: 88px; }
  .listing-row-name { font-size: 1rem; }
  .listing-row-desc { font-size: .85rem; }
  .listing-row-arrow { display: none; }
}

/* ============================================================
   SIDEBAR CARD — location + hours in detail view
   ============================================================ */
.sidebar-card {
  padding: 1.65rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 22px;
  margin-bottom: 1rem;
  display: block;
  color: var(--text);
}
/* When the address row is clickable, wrap it in <a class="sidebar-card-row sidebar-card-row-link">.
 * The row gets the hover/cursor styling; the parent card stays passive
 * so hovering an info row (hours, time-of-day) doesn't suggest a click
 * that doesn't exist. Fixes a bug where the entire card was a single
 * <a>, making any-row hover light up the card and any-row click open
 * directions — confusing for rows that have nothing to do with maps.
 */
.sidebar-card-row-link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  border-radius: 12px;
  margin: -.4rem -.6rem;
  padding: .4rem .6rem;
  transition: background .2s;
}
.sidebar-card-row-link:hover {
  background: var(--bg);
}
.sidebar-card-row {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 1rem;
}
.sidebar-card-row + .sidebar-card-row {
  margin-top: 1.4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--border);
}
.sidebar-card-label {
  font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted);
  margin-bottom: .55rem;
}
.sidebar-card-value {
  font-size: 1rem; font-weight: 500;
  line-height: 1.4;
}
.sidebar-card-value-soft {
  font-size: 13px; color: var(--muted); margin-top: .15rem;
}
.open-now {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: 10px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: #16a34a;
  margin-top: .35rem;
}
.open-now::before {
  content: ''; width: 7px; height: 7px;
  border-radius: 50%; background: currentColor;
}
.closed-now {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: 10px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--muted);
  margin-top: .35rem;
}
.sidebar-arrow {
  width: 36px; height: 36px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: all .2s;
  color: var(--text);
}
.sidebar-card-row-link:hover .sidebar-arrow {
  background: var(--text); color: var(--bg);
  border-color: var(--text);
}
.sidebar-arrow svg { width: 14px; height: 14px; }

/* ============================================================
   SEARCH OVERLAY
   ============================================================ */
#guides-search-overlay {
  position: fixed; inset: 0;
  z-index: 200;
  background: var(--bg);
  display: none;
  flex-direction: column;
  padding: 1rem;
}
#guides-search-overlay[aria-hidden="false"],
#guides-search-overlay.open {
  display: flex;
}
#guides-search-overlay-backdrop {
  position: absolute; inset: 0;
  background: var(--bg);
}
#guides-search-overlay-body {
  position: relative;
  max-width: 760px;
  width: 100%;
  margin: 0 auto;
  padding: 1.25rem 1.5rem 0;
}
.guides-search-form {
  display: flex; align-items: center; gap: .85rem;
  border-bottom: 1px solid var(--border-strong);
  padding: .35rem 0 .25rem;
  /* v9.5 — Right padding so the input doesn't run under the
   * floating close button in the top-right corner of the overlay
   * body. 56px = 44 (button width) + 12 (.75rem from-edge offset). */
  padding-right: 56px;
}
.guides-search-icon { color: var(--muted); display: inline-flex; }
.guides-search-form input {
  flex: 1;
  font-size: 1.5rem; font-weight: 500;
  background: transparent;
  border: 0;
  padding: .85rem 0;
  color: var(--text);
  font-family: inherit;
}
.guides-search-form input:focus { outline: none; }
.guides-search-form input::placeholder { color: var(--muted); }
.guides-search-close {
  /* v9.5 — Floating top-right close button.
   *
   * Pre-v9.5 the close button sat inline at the end of the search
   * form, after the input. On narrow mobile viewports users
   * reported "can't close search" — even though the button was
   * technically there with min 44x44 reach, it was visually muddy
   * (text color against same-color background) and the inline
   * layout pushed it tight against the right edge where it could
   * brush the viewport edge and feel un-tappable.
   *
   * Floating it to the absolute top-right of the overlay body
   * decouples its position from the form layout entirely. The
   * circular background gives it consistent contrast against any
   * theme, and the fixed corner position matches the pattern most
   * users expect for "close this modal" on mobile (iOS share
   * sheets, Android modals, etc.).
   *
   * inset works in this context because the parent
   * #guides-search-overlay-body is position:relative. */
  position: absolute;
  top: .75rem;
  right: .75rem;
  width: 44px; height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--pill-bg);
  color: var(--text);
  padding: 0;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: background .15s, transform .15s, border-color .15s;
  /* z-index above the input so the button receives taps even if a
   * stray autofill chip or browser-supplied affordance overlaps the
   * top-right corner of the form. */
  z-index: 2;
}
.guides-search-close:hover {
  background: var(--bg-soft, var(--pill-bg));
  border-color: var(--border-strong);
  transform: scale(1.04);
}
.guides-search-close svg {
  width: 20px; height: 20px;
  /* The stroked X reads well at 20px; smaller would be hard to tap-
   * target precisely on a 44px button. */
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer {
  margin-top: 6rem;
  padding: 4rem 0;
  border-top: 1px solid var(--border);
}
.site-footer-inner {
  display: flex; flex-wrap: wrap; gap: 2rem;
  justify-content: space-between; align-items: center;
}
.site-footer-logo {
  font-family: 'Playfair Display', serif;
  font-style: italic; font-size: 1.5rem; font-weight: 600;
  opacity: .85;
}
.site-footer-links {
  display: flex; gap: 2.5rem; flex-wrap: wrap;
  font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--muted);
}
.site-footer-links a:hover { color: var(--text); }
.site-footer-meta {
  font-size: 10px;
  letter-spacing: .15em;
  color: var(--muted);
}
@media (max-width: 640px) {
  .site-footer { margin-top: 3rem; padding: 3rem 0; }
  .site-footer-inner { gap: 1.5rem; }
}

/* ============================================================
   PANEL (slide-in detail viewer — kept from old design)
   ============================================================ */
#guides-panel {
  position: fixed; inset: 0;
  z-index: 200;
  pointer-events: none;
}
#guides-panel[aria-hidden="false"] { pointer-events: auto; }
#guides-panel-backdrop {
  position: absolute; inset: 0;
  background: rgba(0,0,0,.5);
  opacity: 0;
  transition: opacity .3s;
}
#guides-panel[aria-hidden="false"] #guides-panel-backdrop { opacity: 1; }
#guides-panel-body {
  position: absolute; top: 0; right: 0;
  width: min(100%, 920px);
  height: 100%;
  background: var(--bg);
  transform: translateX(100%);
  transition: transform .35s cubic-bezier(.16,1,.3,1);
  overflow-y: auto;
  border-left: 1px solid var(--border);
}
#guides-panel[aria-hidden="false"] #guides-panel-body {
  transform: translateX(0);
}
#guides-panel-close {
  position: absolute; top: 1.25rem; left: 1.25rem;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: rgba(0,0,0,.4);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255,255,255,.2);
  color: white;
  display: inline-flex; align-items: center; justify-content: center;
  z-index: 10;
}
@media (max-width: 720px) {
  #guides-panel-body { width: 100%; }
}

/* ============================================================
   FORMS — signin / signup
   ============================================================ */
.form-page {
  max-width: 420px; margin: 4rem auto;
  padding: 2.5rem 1.5rem;
}
.form-page h1 {
  font-family: 'Outfit', sans-serif;
  font-size: 2.5rem; font-weight: 700;
  letter-spacing: -0.025em;
  margin-bottom: .5rem;
}
.form-page p.lede {
  color: var(--muted);
  font-size: 1rem;
  margin-bottom: 2.25rem;
}
.form-group { margin-bottom: 1rem; }
.form-group label {
  display: block;
  font-size: 11px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--muted);
  margin-bottom: .55rem;
}
.form-input {
  width: 100%;
  padding: .85rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  transition: border-color .2s;
}
.form-input:focus {
  outline: none;
  border-color: var(--text);
}
.form-error {
  background: rgba(239,68,68,.08);
  border: 1px solid rgba(239,68,68,.3);
  color: #ef4444;
  padding: .85rem 1.1rem;
  border-radius: 12px;
  font-size: 13px;
  margin-bottom: 1rem;
}
.form-footer {
  margin-top: 1.5rem;
  font-size: 13px;
  color: var(--muted);
  text-align: center;
}
.form-footer a { color: var(--text); text-decoration: underline; }

/* ============================================================
   EMPTY STATES
   ============================================================ */
.empty {
  text-align: center;
  padding: 4rem 1.5rem;
  color: var(--muted);
}
.empty .emoji { font-size: 2.5rem; display: block; margin-bottom: 1rem; }
.empty p { font-size: 1rem; margin-bottom: 1rem; }

/* ============================================================
   ANIMATION HELPERS
   ============================================================ */
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
.anim-in { opacity: 0; animation: fadeUp .65s cubic-bezier(.16,1,.3,1) forwards; }

/* ============================================================
   SECTION VISIBILITY — atmoDrill SPA-style transitions
   ============================================================
   The home renders both the bento section and the drill section
   into the DOM at all times; .is-hidden toggles visibility. The
   atmoDrill JS module animates between them by flipping these
   classes plus .is-fading / .is-entering for the cross-fade.
*/
.is-hidden { display: none !important; }
.is-fading { opacity: 0; transform: scale(.97); transition: opacity .25s, transform .25s; }
.is-entering { opacity: 0; transform: translateY(8px); }
.atmo-grid-section, .atmo-drill-in {
  transition: opacity .35s cubic-bezier(.16,1,.3,1), transform .35s cubic-bezier(.16,1,.3,1);
}

/* ============================================================
   ACCESS-GATING — paywall, banners, account extensions (Turn C)
   ============================================================ */

/* Subscribe page hero — large, calm, marketing-page feel.
 * Most of the geometry is set on container + plan-grid below;
 * .subscribe-hero is just the headline block. */
.subscribe-page { padding: 3rem 0; }
.subscribe-hero { text-align: center; margin-bottom: 2.5rem; }
.subscribe-h1 {
  font-family: 'Playfair Display', serif;
  font-weight: 700;
  font-size: clamp(2rem, 5vw, 3rem);
  margin: 0 0 0.5rem;
}
.subscribe-subhead {
  font-size: 1.05rem; color: var(--text-soft);
  margin: 0 auto 2rem; max-width: 36rem;
}
/* Billing-interval toggle. Two pill-buttons inside a flex row, one
 * is-active at a time. The "save ~30%" badge nests inside the
 * Yearly button to reinforce the discount. */
.subscribe-toggle {
  display: inline-flex; gap: .25rem;
  padding: .25rem; background: var(--pill-bg); border-radius: 999px;
}
.toggle-btn {
  border: 0; background: transparent; cursor: pointer;
  padding: .5rem 1.25rem; border-radius: 999px;
  font-size: .9rem; font-weight: 600; color: var(--text-soft);
  transition: background .15s, color .15s;
}
.toggle-btn.is-active {
  background: var(--bg); color: var(--text);
  box-shadow: 0 1px 4px rgba(0,0,0,.08);
}
.toggle-save {
  display: inline-block;
  margin-left: .35rem;
  padding: .1rem .4rem;
  background: var(--accent, #6b8d57); color: #fff;
  border-radius: 999px; font-size: .7rem; font-weight: 600;
}

/* Plan grid: 2-col on desktop, stacked on mobile. */
.plan-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  max-width: 56rem; margin: 0 auto;
}
.plan-card {
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 1.75rem 1.5rem;
  background: var(--card);
  display: flex; flex-direction: column;
  transition: transform .15s, box-shadow .15s, border-color .15s;
}
.plan-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0,0,0,.06);
  border-color: var(--text-soft);
}
.plan-name {
  font-size: 1.25rem; font-weight: 700;
  margin: 0 0 0.75rem;
}
.plan-price { margin-bottom: 1.25rem; }
.plan-price-amount {
  font-family: 'Playfair Display', serif;
  font-size: 2.2rem; font-weight: 700;
}
.plan-price-period {
  font-size: .95rem; color: var(--text-soft);
  margin-left: .25rem;
}
.plan-features {
  list-style: none; padding: 0; margin: 0 0 1.5rem;
  flex: 1;
}
.plan-features li {
  padding-left: 1.5rem; position: relative;
  margin-bottom: .55rem; font-size: .95rem; color: var(--text);
}
.plan-features li::before {
  content: '✓';
  position: absolute; left: 0;
  color: var(--accent, #6b8d57); font-weight: 700;
}
.subscribe-btn { margin-top: auto; }
.subscribe-anon-note {
  text-align: center; color: var(--text-soft);
  margin: 2rem auto 0; max-width: 36rem;
  font-size: .95rem;
}
.subscribe-anon-note a {
  color: var(--text); text-decoration: underline;
  font-weight: 600;
}

/* FAQ block under the plan grid */
.subscribe-faq {
  max-width: 42rem; margin: 4rem auto 0;
}
.subscribe-faq-h2 {
  font-family: 'Playfair Display', serif;
  font-weight: 700; font-size: 1.5rem;
  margin: 0 0 1.5rem; text-align: center;
}
.subscribe-faq dl { margin: 0; }
.subscribe-faq dt {
  font-weight: 600; margin: 1rem 0 .25rem;
}
.subscribe-faq dd {
  margin: 0 0 1rem; color: var(--text-soft);
  font-size: .95rem; line-height: 1.5;
}

/* ---------- Share banner ----------
 * Renders above the header for anonymous visitors who arrived via
 * an org share URL. Compact strip with the org name + a CTA to
 * sign up. Theme-aware: inherits theme colors instead of a fixed
 * accent so it doesn't fight the brand mark. */
.share-banner {
  background: var(--text); color: var(--bg);
  padding: .55rem 1rem;
  font-size: .85rem; line-height: 1.3;
  text-align: center;
}
.share-banner a {
  color: inherit; text-decoration: underline;
  font-weight: 600; margin-left: .5rem;
}

/* ---------- Trial countdown ----------
 * Small pill shown in the user menu or account page when access
 * state is 'trial'. Counts down in minutes once below an hour,
 * gentle warning tone (no alarming red unless < 5 min). */
.trial-countdown {
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .25rem .75rem; border-radius: 999px;
  background: var(--pill-bg); color: var(--text);
  font-size: .75rem; font-weight: 600;
}
.trial-countdown.is-urgent {
  background: #fff3cd; color: #856404;
}

/* ---------- Account page access section ---------- */
.account-access { margin: 2rem 0; }
.account-access h2 {
  font-family: 'Playfair Display', serif; font-size: 1.4rem;
  font-weight: 700; margin: 0 0 1rem;
}
.access-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: .75rem 0; border-bottom: 1px solid var(--border);
  gap: 1rem;
}
.access-row:last-child { border-bottom: 0; }
.access-row-label { color: var(--text-soft); font-size: .9rem; }
.access-row-value {
  font-weight: 600; font-size: .95rem;
  word-break: break-all;
}
.share-url-row {
  flex-direction: column; align-items: stretch;
  gap: .5rem;
}
.share-url-input {
  display: flex; gap: .5rem; align-items: center;
}
.share-url-input input {
  flex: 1; padding: .55rem .75rem; font-size: .85rem;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--pill-bg); color: var(--text);
  font-family: 'Courier New', monospace;
}
.share-url-input button {
  white-space: nowrap;
}


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