/* =====================================================================
   Public events site — homepage + event detail page
   ---------------------------------------------------------------------
   Page-specific rules for templates/public/*.html, built on top of the
   shared design system in static/css/style.css (tokens, .btn/.card/.badge/
   .chip-opt/.mega-panel/.search-overlay/.public-header/.site-footer/
   @keyframes heroBlobFloat) rather than redefining any of it. Carries its
   own PESIMSR-inspired identity — Playfair Display for headings/display
   text and Noto Sans for body copy (the "PES Support" dashboard system is
   Inter/Sora only) — and its own navy/orange colour tokens, defined below. */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,600;0,700;0,800;0,900;1,600;1,700&family=Noto+Sans:wght@400;500;600;700;800&display=swap');
/* Department.icon/Tag.icon store a Bootstrap Icons class name (e.g. "bi
   bi-building", "bi bi-hash" — see the help_text on DepartmentForm/TagForm
   in app/forms.py) — nothing in the project loaded the actual icon font
   before now, since every other icon on the site is a hand-drawn inline
   <svg>. Only pulled in here (not a project-wide dependency) because these
   two per-item icons are the only place that reads a Bootstrap Icons class
   from the database instead of hardcoding its own SVG. */
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css');

/* ---------------------------- PESIMSR theme tokens ----------------------- */
/* Re-declares the shared design-system's colour variables for public pages
   only — this stylesheet is exclusively linked by templates/public/base.html
   (see public/base.html's extra_css block), so none of this reaches the
   staff dashboard, which never loads this file and keeps its original
   palette untouched. Deliberately leaves --warning/--info/--danger/--success
   alone — those feed the tickets' own per-card accent variety (theme-a/b/c,
   trend-pattern-*, status pills), which keeps its existing look;
   only the surrounding site chrome (header, hero, buttons, section
   headings, footer) moves to navy + orange. --accent/--accent-100 fill in
   fallback tokens the shared stylesheet already reaches for (see
   .mega-item-icon in style.css) but never previously defined. */
:root{
  --primary:#00264d;
  --primary-600:#001830;
  --primary-100:#e9eff6;
  --primary-text:#ef7e1a;
  --accent:#ef7e1a;
  --accent-100:#fef1e2;
  /* Text colour for the pale-blue "upcoming" status pill (.event-status-pill,
     .up-status) — a separate token from --primary rather than reusing it
     directly, since --primary stays navy in dark mode while --primary-100's
     pill background goes dark there, which would otherwise sink navy text
     into near-invisibility against it. */
  --upcoming-text:var(--primary);
  /* Guest-pages-only gold accent — differentiates a guest's own "ID badge"/
     boarding-pass stub from an event ticket's navy one, same navy/orange
     tokens everywhere else. */
  --gold:#b8892b;
  --gold-light:#d9a53f;
  --gold-pale:#fbf3e2;
  /* Calendar-page-only academic-calendar accents (AcademicCalendarItem.type)
     — deliberately distinct from --danger/--warning, which already mean
     "live"/"upcoming" event on this same calendar, so a mandatory holiday
     pill never reads as a live event by sharing its colour. */
  --hol-mandatory:#a5314f;
  --hol-mandatory-pale:#f7e3ea;
  --hol-festival:#b5790f;
  --hol-festival-pale:#fbf0dc;
  --acad-exam:#453a86;
  --acad-exam-pale:#eae8f7;
  --acad-other:#1f6e68;
  --acad-other-pale:#e2f2f0;
  --display:"Playfair Display","Times New Roman",serif; --body:"Noto Sans",-apple-system,sans-serif;
}
html[data-bs-theme="dark"]{
  --hol-mandatory-pale:#341620;
  --hol-festival-pale:#332810;
  --acad-exam-pale:#211c38;
  --acad-other-pale:#122622;
  --primary-100:#1b2a3d;
  --primary-text:#ff9a45;
  --accent:#ff9a45;
  --accent-100:#2a1f14;
  --upcoming-text:#7fb0ff;
  --gold-pale:#2e2410;
}
/* Higher-specificity than style.css's plain `body`/`h1..h4` rules (an extra
   :has() pseudo-class), so it wins regardless of stylesheet load order —
   swaps the dashboard's Inter/Sora pairing for Noto Sans/Playfair Display
   across every public page without touching the shared selectors themselves. */
body:has(.public-header){ font-family:'Noto Sans',sans-serif; }
body:has(.public-header) h1,
body:has(.public-header) h2,
body:has(.public-header) h3,
body:has(.public-header) h4{ font-family:'Playfair Display',serif; }
/* PESIMSR's signature fully-pill buttons, applied only within public pages. */
body:has(.public-header) .btn{ border-radius:999px; }

/* ---------------------------- header additions -------------------------- */
.public-brand-word{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:var(--text); white-space:nowrap; }
.public-brand-word b{ color:var(--primary-text); font-weight:800; }
.mega-panel-inner.no-visual{ grid-template-columns:1fr; }
.tag-pill-row{ display:flex; flex-wrap:wrap; gap:10px; }
.tag-pill-row.lg .chip-opt{ padding:9px 18px; font-size:13.5px; }
/* Round shapes for the header's own icon cluster (search/menu toggle, the
   light/dark switch, and — for logged-in staff viewing the public site —
   the quick-actions/notifications/profile buttons from _topbar_actions.html)
   — scoped to .public-header so the dashboard's own square-ish versions of
   these same shared classes (style.css) are completely untouched; this
   stylesheet never loads there anyway, but the extra scope also keeps it
   from reaching for .icon-btn/.avatar on any other public page context. */
.public-header .icon-btn{ border-radius:50%; }
.public-header .theme-toggle{ border-radius:999px; }
.public-header .theme-toggle button{ border-radius:999px; }
.public-header .user-chip{ border-radius:999px; }

/* ------------------------ header search trigger (icon-only) --------------- */
/* Plain icon circle, not a text pill — still opens the exact same
   #publicSearchOverlay (see initPublicSearchToggle in scripts.js, untouched;
   Ctrl K still opens it too, just no visible kbd hint on the button itself
   any more — moved into the title="" tooltip instead). Sized to match
   .icon-btn's own two sizes exactly (style.css: 40x40 desktop, 32x32 at
   <=640px) since it sits directly beside those buttons for logged-in staff
   (quick actions / notifications) — a fixed 32x32 at every width used to
   look right only when those weren't present (anonymous visitors), then
   read as noticeably undersized once they were. */
.nav-search-trigger{
  display:flex; align-items:center; justify-content:center; width:40px; height:40px;
  background:#f6f8fb; border:1px solid #e2e8f0; border-radius:50%; color:#5c6b80; cursor:pointer; flex-shrink:0;
  font-family:inherit; transition:background .15s ease, border-color .15s ease;
}
.nav-search-trigger:hover{ background:#fff; border-color:#ef7e1a; color:#0a1626; }
.nav-search-trigger svg{ width:16px; height:16px; flex-shrink:0; }
@media (max-width:640px){ .nav-search-trigger{ width:32px; height:32px; } .nav-search-trigger svg{ width:15px; height:15px; } }

/* ------------------------ single-button theme toggle ---------------------- */
/* .theme-toggle is shared with the staff dashboard (style.css) — its two
   light/dark buttons sit side by side there in one pill, which is exactly
   the "two buttons" look the public header no longer wants. Only the public
   header's copy carries .compact (set via compact_theme_toggle=True on the
   _topbar_actions.html include — the dashboard's own header.html include
   doesn't pass that flag), so scoping every override to .compact here keeps
   the dashboard's version completely untouched.

   Both [data-theme-set] buttons stay in the DOM and initTheme()/setTheme()
   in scripts.js are untouched — that's still what's flipping data-bs-theme
   sitewide and toggling each button's own .active class ("this button's
   own theme is the CURRENT one") on click. Only the CSS changes: both
   buttons are stacked in the exact same circle (position:absolute;inset:0),
   sized to match .icon-btn's own two sizes exactly (style.css: 40x40
   desktop, 32x32 at <=640px, same reasoning as .nav-search-trigger right
   above this) instead of a fixed 32x32 at every width — that read as too
   small next to quick-actions/notifications' full 40px .icon-btn circles
   for logged-in staff, even though it matched the search trigger fine for
   anonymous visitors (nothing else 40px sat next to it there).

   Deliberately show the *inactive* button, not .active — a single-icon
   toggle's icon is the destination you'd switch TO, not the current state
   (sun visible while in dark mode, moon visible while in light mode). This
   isn't just cosmetic: showing .active instead would mean the one visible/
   clickable button always re-applies the theme already active, and the
   other theme could never be reached, since setTheme() never repositions
   which element carries .active independent of this. */
.theme-toggle.compact{
  position:relative; width:40px; height:40px; border-radius:50%; flex-shrink:0;
  background:#f6f8fb; border:1px solid #e2e8f0; padding:0; gap:0;
}
.theme-toggle.compact button{
  position:absolute; inset:0; width:100%; height:100%; border:none; background:none; border-radius:50%;
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:#5c6b80;
  opacity:0; visibility:hidden; transition:opacity .15s ease, color .15s ease;
}
.theme-toggle.compact button svg{ width:18px; height:18px; }
.theme-toggle.compact button:not(.active){ opacity:1; visibility:visible; }
.theme-toggle.compact:hover{ border-color:#cbd6e3; }
.theme-toggle.compact:hover button:not(.active){ color:#ef7e1a; }
@media (max-width:640px){
  .theme-toggle.compact{ width:32px; height:32px; }
  .theme-toggle.compact button svg{ width:16px; height:16px; }
}

/* --------------------------- mega-menu columns ---------------------------- */
/* Matches the mockup's own .mega-inner/.mega-grid/.mega-col/.mega-link/
   .mega-link-sm/.mega-cta-panel/.mega-foot verbatim (class names included),
   literal hex throughout for the same reason as .nav-search-trigger above.
   Only the CONTENT inside .mega-panel changed; the panel shell itself
   (.mega-panel/.mega-panel-inner, style.css) and its open/close/position
   JS (initMegaMenus, scripts.js) are untouched, so nothing here risks the
   dashboard, which never renders a .mega-panel at all. Campus's markup is
   hand-written (see base.html); Department/Tags loop over the real
   nav_departments*/
/* 1280px, not the mockup's own 1180px — matches .public-header-inner's own
   container (style.css) so the panel's columns line up exactly under the
   header/logo above them instead of drifting off to a narrower center. */
.mega-inner{ max-width:1280px; margin:0 auto; padding:30px 40px 32px; }
.mega-grid{ display:grid; grid-template-columns:1.3fr 1fr 1fr 1.1fr; gap:0; }
.mega-col{ padding-right:36px; border-right:1px solid #e2e8f0; }
.mega-col:nth-child(2){ padding-left:36px; padding-right:36px; }
.mega-col:nth-child(3){ padding-left:36px; border-right:none; }
.mega-col-title{ font-size:12px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:#5c6b80; margin-bottom:8px; }
.mega-link{ display:block; padding:11px 0; }
.mega-link .ml-name{ display:block; font-family:'Playfair Display',serif; font-size:15.5px; font-weight:700; color:#00264d; }
.mega-link .ml-desc{ display:block; font-size:11.5px; color:#5c6b80; margin-top:2px; }
.mega-link:hover .ml-name{ color:#ef7e1a; }
.mega-link-sm{ display:block; padding:9px 0; font-size:13.5px; font-weight:600; color:#0a1626; }
.mega-link-sm:hover{ color:#ef7e1a; }
.mega-link-sm span{ color:#5c6b80; }
.mega-cta{ padding-left:36px; }
.mega-cta-panel{
  background:linear-gradient(160deg, #00264d 0%, #001830 100%); border-radius:18px; padding:24px 22px;
  color:#fff; position:relative; overflow:hidden;
}
.mega-cta-panel::before{ content:""; position:absolute; inset:0; background:radial-gradient(ellipse 180px 140px at 100% 0%, rgba(239,126,26,.3), transparent 60%); }
.mega-cta-ic{ width:38px; height:38px; border-radius:11px; background:rgba(255,255,255,.12); display:flex; align-items:center; justify-content:center; margin-bottom:14px; position:relative; }
.mega-cta-ic svg{ width:18px; height:18px; color:#ff9a45; }
.mega-cta-panel h4{ position:relative; font-family:'Playfair Display',serif; font-weight:700; font-size:16px; color:#fff; margin:0 0 6px; }
.mega-cta-panel p{ position:relative; font-size:12.5px; color:rgba(255,255,255,.72); line-height:1.55; margin:0 0 16px; }
.mega-cta-link{ position:relative; display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:700; color:#fff; }
.mega-cta-link svg{ width:13px; height:13px; flex-shrink:0; }
.mega-cta-link:hover{ color:#ff9a45; }
/* .mega-foot is a grid item of .mega-panel-inner.no-visual's 1fr column
   (display:grid) — a non-auto margin on a grid item opts it out of the
   default stretch, so `margin:0 auto` here was shrinking it to hug its own
   two short links (max-content width) and centering that small block
   instead of spanning the row, which collapsed "All Campuses"/the phone
   number (and, on the other two mega-panels, "All Departments"/"Browse by
   Tag" and "All Tags"/"Featured Events") into the middle instead of
   opposite edges. width:100% forces it back to the grid area's full width
   so justify-content:space-between has the whole row to work with, same as
   .mega-inner above already gets (its own wide 4-column .mega-grid content
   happens to fill the row on its own, which is why that sibling never
   showed this bug). */
.mega-foot{
  width:100%; max-width:1280px; margin:0 auto; padding:18px 40px 26px; border-top:1px solid #e2e8f0;
  display:flex; gap:28px; flex-wrap:wrap; justify-content:space-between;
}
/* .mega-foot's "Browse by Tag" is a <button> (it triggers the tags panel
   via JS, not a plain navigation link like its "All Departments" sibling),
   so unlike that <a> it still carries the browser's native button chrome
   (border/background/padding) unless explicitly reset — this rule only set
   layout/type properties, leaving it looking like a bordered pill instead
   of matching the plain icon+text link right next to it. */
.mega-foot a, .mega-foot button{
  display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:600; color:#5c6b80; font-family:inherit;
  border:none; background:none; padding:0; appearance:none; cursor:pointer;
}
.mega-foot a:hover, .mega-foot button:hover{ color:#ef7e1a; }
.mega-foot a svg, .mega-foot button svg{ width:13px; height:13px; flex-shrink:0; }

/* Dark page-dimming overlay behind an open mega-panel — toggled alongside
   .mega-panel.show by initMegaMenus (scripts.js). It DOES already sit above
   and intercept clicks on everything behind it (confirmed via
   elementFromPoint — a click anywhere behind the panel, e.g. on the browse
   page's own filter bar, hits this backdrop and closes the menu rather than
   reaching the control underneath), so the "I can still interact with the
   rest of the page" read was a visual problem, not a functional one: at
   only 30% opacity with no blur, bright content behind it (a white
   .filter-panel especially) barely looked dimmed at all, so the mega-panel
   read as a loose popup floating over a still-fully-active page instead of
   a focused overlay state. Matches .search-overlay-backdrop's own (much
   more convincing) treatment elsewhere on this same site now. */
.mega-backdrop{
  position:fixed; inset:0; background:rgba(4,12,28,.55); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); z-index:290;
  opacity:0; visibility:hidden; transition:opacity .22s ease, visibility .22s ease;
}
.mega-backdrop.show{ opacity:1; visibility:visible; }
@media (max-width:980px){ .mega-backdrop{ display:none; } }

/* Off-canvas drawer (mobile, <980px) — mega-panels render as an inline
   accordion there (see style.css's own .public-nav .mega-panel rules), so
   the multi-column layout above has to collapse to one stacked column and
   the CTA card needs to actually fit an ~250px-wide drawer. */
@media (max-width:980px){
  .mega-inner{ padding:4px 0 10px; }
  .mega-grid{ grid-template-columns:1fr; }
  .mega-col{ padding-right:0!important; padding-left:0!important; border-right:none; }
  .mega-col + .mega-col{ margin-top:18px; }
  .mega-cta{ padding-left:0; margin-top:18px; }
  .mega-foot{ padding:14px 0 6px; }
}
.public-header .avatar{ border-radius:50%; }

/* -------------------------- full-screen mobile menu ----------------------- */
/* Replaces the old .public-nav off-canvas drawer below 980px (that drawer's
   own trigger id was renamed off publicNavToggleBtn in base.html, so
   initPublicNav in scripts.js can no longer find it and permanently no-ops
   there) — matches the mockup's own #mobileMenu/.mm-* exactly, literal hex
   for the same reason as the mega-menu/search-trigger above. Opened/closed
   by initMobileMenu in public.js via a plain .show class (kept out of
   view with visibility+opacity, not display:none, so its own open/close
   transition can run). */
#mobileMenu{
  position:fixed; inset:0; z-index:200; background:#fff; display:flex; flex-direction:column;
  opacity:0; visibility:hidden; transform:translateY(-12px);
  transition:opacity .22s ease, transform .22s ease, visibility 0s linear .22s;
}
#mobileMenu.show{ opacity:1; visibility:visible; transform:none; transition:opacity .22s ease, transform .22s ease; }
@media (min-width:981px){ #mobileMenu{ display:none; } }

.mm-top{
  display:flex; align-items:center; justify-content:space-between; gap:12px; flex-shrink:0;
  padding:18px 20px; border-bottom:1px solid #e2e8f0;
}
.mm-brand{ display:flex; align-items:center; }
.mm-brand img{ height:30px; width:auto; }
.mm-close{
  width:38px; height:38px; flex-shrink:0; display:flex; align-items:center; justify-content:center;
  border-radius:50%; background:#f6f8fb; border:1px solid #e2e8f0; color:#0a1626; cursor:pointer;
}
.mm-close:hover{ background:#fef1e2; border-color:#ef7e1a; color:#ef7e1a; }

.mm-body{ flex:1; overflow-y:auto; padding:8px 20px 20px; }
.mm-link{
  display:block; padding:18px 2px; font-size:23px; font-weight:700; color:#00264d;
  border-bottom:1px solid #e2e8f0;
  font-family:var(--display)
}
.mm-link:hover{ color:#ef7e1a; }

.mm-accordion{ border-bottom:1px solid #e2e8f0; }
.mm-acc-trigger{
  width:100%; display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:18px 2px; background:none; border:none; font-family:var(--display); font-size:23px; font-weight:700;
  color:#00264d; cursor:pointer; text-align:left;
}
.mm-caret{
  width:9px; height:9px; flex-shrink:0; border-right:2px solid #5c6b80; border-bottom:2px solid #5c6b80;
  transform:rotate(45deg); transition:transform .2s ease; margin-top:-3px;
}
.mm-acc-trigger.is-open .mm-caret{ transform:rotate(-135deg); margin-top:3px; }
.mm-acc-panel{ display:grid; grid-template-rows:0fr; transition:grid-template-rows .22s ease; }
.mm-acc-trigger.is-open + .mm-acc-panel{ grid-template-rows:1fr; }
.mm-acc-panel-inner{ overflow:hidden; }
.mm-acc-group{ padding:2px 0 14px; }
.mm-acc-label{
  font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:#5c6b80;
  margin-bottom:8px 2px 4px;
}
.mm-acc-group a{
  display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 2px;
  font-size:15.5px; font-weight:600; color:#0a1626; font-family: "Noto Sans",-apple-system,sans-serif;
}
.mm-acc-group a:hover{ color:#ef7e1a; }
.mm-acc-group a span{ font-size:12px; color:#5c6b80; font-weight:600; flex-shrink:0; }
.mm-acc-all{
  display:flex; align-items:center; gap:6px; padding:10px 2px 18px; font-size:14px; font-weight:700; color:#ef7e1a;
}
.mm-acc-all svg{ width:13px; height:13px; flex-shrink:0; }

.mm-utility{
  display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:6px; padding:16px 2px 4px;
  border-top:1px solid #e2e8f0; font-size:12.5px; font-weight:600; color:#5c6b80;
}
.mm-utility a:hover{ color:#ef7e1a; }

.mm-foot{ flex-shrink:0; display:flex; gap:10px; padding:16px 20px 22px; border-top:1px solid #e2e8f0; }
.mm-foot .btn{ flex:1; justify-content: center;}
.btn-navy-outline{
  display:flex; align-items:center; justify-content:center; gap:8px; padding:12px 20px;
  border-radius:999px; border:1.5px solid #00264d; color:#00264d; font-size:14px; font-weight:700;
}
.btn-navy-outline:hover{ background:#00264d; color:#fff; }
.btn-block{ width:100%; }

/* ------------------------------ scroll reveal ---------------------------- */
.reveal{ opacity:0; transform:translateY(28px); transition:opacity .6s ease, transform .6s ease; transition-delay:var(--reveal-delay,0s); }
.reveal.in-view{ opacity:1; transform:none; }
@media (prefers-reduced-motion:reduce){ .reveal{ transition:none; opacity:1; transform:none; } }

/* ---------------------------------- hero --------------------------------- */
.hero{
  position:relative; overflow:hidden; isolation:isolate;
  background:var(--primary); color:#fff; padding:64px 0 84px;
}
/* Rotating background photos (static/images/slider/*.jpg) — plain CSS
   cross-fade via staggered animation-delay on each <img>, no JS needed to
   cycle them. 20s loop / 4 photos / 5s stagger tiles exactly: each image's
   own 0%→100% timeline (fade in 0-5%, hold 5-20%, fade out 20-25%, hidden
   the rest) is a 5s "turn" that hands off to the next photo's delayed
   start with no gap or overlap. ::after's navy gradient sits on top of
   them for the same reason any photo hero needs one — the white hero copy
   and hero ticket need to stay legible over whatever photo happens to
   be showing. */
.hero-bg{ position:absolute; inset:0; z-index:0; }
.hero-bg img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0;
  animation:heroBgFade 20s infinite;
}
.hero-bg img:nth-child(1){ animation-delay:0s; }
.hero-bg img:nth-child(2){ animation-delay:5s; }
.hero-bg img:nth-child(3){ animation-delay:10s; }
.hero-bg img:nth-child(4){ animation-delay:15s; }
@keyframes heroBgFade{ 0%{ opacity:0; } 5%{ opacity:1; } 20%{ opacity:1; } 25%{ opacity:0; } 100%{ opacity:0; } }
.hero-bg::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(120deg, rgba(0,38,77,.94) 0%, rgba(0,38,77,.86) 55%, rgba(0,64,128,.72) 100%);
}
@media (prefers-reduced-motion:reduce){
  .hero-bg img{ animation:none; opacity:0; }
  .hero-bg img:first-child{ opacity:1; }
}
.hero::before{
  content:""; position:absolute; inset:0; z-index:1; opacity:.4;
  background-image:radial-gradient(circle at 1px 1px, rgba(255,255,255,.12) 1px, transparent 0);
  background-size:22px 22px;
}
.hero-blob{ position:absolute; border-radius:50%; filter:blur(90px); opacity:.32; z-index:1; animation:heroBlobFloat 20s ease-in-out infinite alternate; }
.hero-blob.h1{ width:420px; height:420px; background:#ef7e1a; top:-160px; left:-100px; }
.hero-blob.h2{ width:380px; height:380px; background:#004080; bottom:-180px; right:-80px; animation-duration:24s; animation-delay:-8s; }
@media (prefers-reduced-motion:reduce){ .hero-blob{ animation:none; } }

.hero-inner{
  position:relative; z-index:2; max-width:1280px; margin:0 auto; padding:0 40px;
  display:grid; grid-template-columns:1.15fr .85fr; gap:48px; align-items:center;
}
.hero-eyebrow{
  display:inline-flex; align-items:center; gap:9px; font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase;
  color:#ff9a45; margin-bottom:18px;
}
.hero-eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:currentColor; flex-shrink:0; }
.hero-copy h1{ font-family:'Playfair Display',serif; font-size:clamp(36px,4.6vw,56px); line-height:1.1; letter-spacing:-.01em; font-weight:700; margin:16px 0 20px; color:#fff; }
.hero-copy h1 em{ font-style:italic; color:#ff9a45; }
.hero-desc{ font-size:16.5px; line-height:1.65; color:rgba(255,255,255,.78); max-width:52ch; margin:0 0 32px; }
.hero-actions{ display:flex; gap:12px; flex-wrap:wrap; margin-bottom:44px; }
.btn-lg{ padding:13px 24px; font-size:14.5px; border-radius:12px; }
.hero-stats{ display:flex; gap:0; flex-wrap:wrap; border-top:1px solid rgba(255,255,255,.16); padding-top:26px; }
.hero-stat{ flex:1; min-width:90px; padding-right:24px; }
.hero-stat-value{ display:block; font-family:'Playfair Display',serif; font-size:clamp(24px,2.4vw,32px); font-weight:700; color:#fff; }
.hero-stat-label{
  display:block; font-size:12.5px; color:rgba(255,255,255,.62); font-weight:600;
  text-transform:uppercase; letter-spacing:.08em; margin-top:4px;
}
.btn-white{ background:#fff; color:var(--primary-600); }

/* events_list.html's embedded search & filter card, sitting inside the
   navy hero — without this, the raw <input>/<select> elements each get
   their own individual pill via the site-wide input/select base rule
   (style.css), but nothing unifies them into one panel, so they end up
   floating directly on the navy background with no shared card behind
   them at all. */
.filter-panel{ background:var(--surface); border-radius:18px; padding:22px; box-shadow:var(--shadow-lg); }
/* .filter-search normally sits INSIDE a .filter-row as one flex item
   alongside the selects (its flex:1 1 220px in style.css is sized for
   that) — .filter-panel instead puts it in its own full-width row above a
   separate .filter-row of selects/buttons, a layout style.css's own rules
   don't add any gap for. Scoped to the adjacent-sibling case specifically
   so it can't affect the shared class's original nested usage elsewhere
   (e.g. guests.html's filter form). */
.filter-search + .filter-row{ margin-top:14px; }
.btn-white:hover{ background:#f0f2fa; }

/* ------------------- hero live ticket deck (boarding pass) ---------------- */
/* Up to 3 real live events (index.html's hero_live_events / the JSON twin
   hero_live_events_data it embeds via json_script) cycle through ONE front
   .hero-ticket, driven by initHeroDeck in public.js (fade the swappable
   bits out, swap their text, fade back in, "shuffle" the two decorative
   cards behind it). .deck-peek.peek-1/peek-2 carry no real per-event data
   at all (just placeholder pill/line/stub shapes) — same as the reference
   mockup — so they render unconditionally as pure "there's a stack here"
   decoration; only the dots (one per real event) and the second peek card
   are gated on there being more than one event to page through. */
.hero-deck{ position:relative; min-width:0; }
.deck-stage{ position:relative; }
.deck-peek{
  position:absolute; inset:0; border-radius:18px; box-shadow:var(--shadow); overflow:hidden; background:var(--surface);
  transition:transform .5s cubic-bezier(.22,.9,.3,1);
}
.deck-peek.peek-1{ transform:rotate(-5deg) translate(-7px,9px); z-index:1; }
.deck-peek.peek-2{ transform:rotate(6.5deg) translate(9px,15px); z-index:0; opacity:.88; }
.hero-deck.is-shuffling .deck-peek.peek-1{ transform:rotate(-8deg) translate(-10px,4px); }
.hero-deck.is-shuffling .deck-peek.peek-2{ transform:rotate(9deg) translate(13px,9px); }
.peek-pill{ position:absolute; left:22px; top:22px; width:58px; height:14px; border-radius:999px; }
.peek-pill.orange{ background:var(--accent-100, var(--primary-100)); }
.peek-pill.navy{ background:var(--primary-100); }
.peek-line{ position:absolute; left:22px; border-radius:5px; background:var(--primary-100); }
.peek-line.title{ top:52px; width:52%; height:12px; }
.peek-line.meta{ top:74px; width:34%; height:8px; background:var(--bg); border:1px solid var(--border); }
.peek-stub{ position:absolute; right:0; top:0; bottom:0; width:78px; display:flex; align-items:center; justify-content:center; }
.peek-stub::before{ content:""; position:absolute; left:0; top:10px; bottom:10px; width:0; border-left:2px dashed rgba(255,255,255,.35); }
.peek-stub.navy{ background:linear-gradient(165deg, var(--primary-600), var(--primary)); }
.peek-stub.orange{ background:linear-gradient(165deg, var(--accent), color-mix(in srgb, var(--accent) 100%, black 20%)); }
.peek-vert{ writing-mode:vertical-rl; transform:rotate(180deg); font-size:9.5px; letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.65); font-weight:700; font-style:normal; }

.hero-ticket{
  position:relative; z-index:3; display:flex; background:var(--surface); border-radius:18px; box-shadow:var(--shadow-lg);
  transition:transform .25s ease, box-shadow .25s ease; overflow:hidden;
}
.hero-ticket:hover{ transform:rotate(0deg) translateY(-4px); box-shadow:0 30px 50px rgba(0,10,30,.4); }
.ht-main{ flex:1; min-width:0; padding:24px 22px; }
.ht-live{
  display:inline-flex; align-items:center; gap:6px; background:var(--accent-100, var(--primary-100)); color:var(--accent, var(--primary-text));
  font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; padding:5px 10px; border-radius:999px; margin-bottom:14px;
}
/* hero_live_events can now be padded out with upcoming events (see
   app.views.home), so the front card isn't always genuinely live — same
   pale-navy look as .event-status-pill.status-upcoming elsewhere, no dot. */
.ht-live.upcoming{ background:var(--primary-100); color:var(--upcoming-text, var(--primary)); }
/* A genuine glowing halo (pulsing box-shadow blur), not the plain opacity/
   scale flicker livePulse gives every other "live" dot on the site — a
   dedicated keyframe (liveDotGlow), reused below on every other "live"
   indicator on the site too (event-status-pill, up-status, .live-dot, the
   page-scoped .status-pill .dot on browse/event-detail). position:static +
   border:none neutralise style.css's OWN unrelated ".dot" class (the
   header's notification-unread badge, position:absolute + its own
   border) — .dot here is a plain inline dot, not a corner badge, but
   without resetting those two properties the sitewide rule (lower
   specificity, but never overridden since this rule didn't mention them)
   was still pulling it out of flow and hiding it off in the card's
   corner instead of next to "Live now". */
.ht-live .dot{
  position:static; width:6px; height:6px; border-radius:50%; border:none; background:var(--accent, var(--primary-text));
  
  animation: pulse 1.6s infinite;
}
@keyframes liveDotGlow{
  0%,100%{  }
  50%{ box-shadow:0 0 0 7px rgba(239,126,26,.08), 0 0 16px 5px rgba(239,126,26,.85); }
}
/* initHeroDeck fades these out/in around each swap (title/meta/qr/date) —
   translateY(5px) while faded matches the reference mockup's own settle-
   in motion instead of a flat opacity crossfade. */
.ht-fade{ transition:opacity .28s ease, transform .28s ease; }
.ht-fade.is-swapping{ opacity:0; transform:translateY(5px); }
/* Wraps across lines like the regular carousel's own .ticket-body h3
   below — white-space:nowrap here previously forced the card to grow as
   WIDE as a long title's full single line instead of wrapping it, because
   a plain `fr` grid column (.hero-inner) has an implied automatic minimum
   sized off its content's min-content, and nowrap text's min-content is
   its whole-line width. Letting it wrap keeps the card at its intended
   narrow width and grows its height instead, same as every other
   ticket-style card on this page. */
.ht-main h3{
  font-family:'Playfair Display',serif; font-weight:700; font-size:21px; color:var(--text); margin:0 0 8px; line-height:1.3;
}
.ht-meta{ font-size:13px; color:var(--muted); line-height:1.7; }
.ht-meta b{ color:var(--text); }
/* Stub — same punched-notch boarding-pass language as .ticket-stub in the
   carousel below and .ticket-card on the event-detail page. The notch
   circles are a fixed dark navy (not var(--bg)/var(--surface), which flip
   with the light/dark toggle) because they need to "cut into" the hero's
   own always-dark gradient behind this card, not the page's background. */
.ht-stub{
  width:100px; flex-shrink:0; background:var(--primary); color:#fff; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:10px; position:relative; padding:16px 8px;
  border-left:2px dashed rgba(255,255,255,.28);
}
.ht-stub::before, .ht-stub::after{
  content:""; position:absolute; left:-9px; width:18px; height:18px; border-radius:50%; background:var(--primary-600);
}
.ht-stub::before{ top:-9px; }
.ht-stub::after{ bottom:-9px; }
.hero-ticket-qr{ display:grid; grid-template-columns:repeat(5,4px); grid-template-rows:repeat(5,4px); gap:2px; }
.hero-ticket-qr i{ background:rgba(255,255,255,.85); border-radius:1px; }
.hero-ticket-qr i.o{ background:transparent; }
.hero-ticket-date{ font-family:'Playfair Display',serif; font-weight:700; font-size:13px; text-align:center; line-height:1.3; }
/* When the current event has a poster, the stub shows it edge-to-edge (same
   width, so the card doesn't jump between deck slides) instead of the QR +
   date block — mirrors .bt-stub.has-image on the event-detail big ticket. */
.ht-featured-img{ display:none; position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.ht-stub.has-image{ padding:0; background:none; border-left-color:rgba(255,255,255,.5); }
.ht-stub.has-image .hero-ticket-qr,
.ht-stub.has-image .hero-ticket-date{ display:none; }
.ht-stub.has-image .ht-featured-img{ display:block; }
.ht-stub.has-image::before,
.ht-stub.has-image::after{ z-index:1; }

.deck-dots{ display:flex; justify-content:center; gap:7px; margin-top:20px; position:relative; z-index:3; }
.deck-dots button{ width:6px; height:6px; border-radius:50%; background:rgba(255,255,255,.32); transition:.25s ease; padding:0; border:none; cursor:pointer; }
.deck-dots button.is-active{ background:var(--accent); width:18px; border-radius:4px; }

@media (max-width:620px){
  .hero-deck{ max-width:420px; }
  .hero-ticket{ transform:none; }
  .deck-peek{ display:none; }
}
@media (prefers-reduced-motion:reduce){
  .deck-peek, .ht-fade{ transition:none; }
}

/* --------------------- live events ticket carousel ------------------------ */
/* Horizontally-scrolling row of boarding-pass-style ticket cards, one per
   live_fan_events entry (real live events first, padded out with the
   soonest upcoming ones only when there are fewer than 5 live — same data
   the old single-card spotlight used, just every card visible/scrollable
   at once instead of one at a time behind prev/next). */
.ticket-carousel{ position:relative; }
.ticket-row{ display:flex; gap:22px; overflow-x:auto; padding:4px 4px 16px; scroll-snap-type:x proximity; scroll-behavior:smooth; }
.ticket-row::-webkit-scrollbar{ height:6px; }
.ticket-row::-webkit-scrollbar-thumb{ background:var(--border); border-radius:99px; }
.ticket-carousel-nav-row{ display:flex; gap:10px; }
.carousel-arrow{
  width:36px; height:36px; border-radius:50%; background:var(--surface); border:1px solid var(--border); color:var(--text);
  display:flex; align-items:center; justify-content:center; cursor:pointer; box-shadow:var(--shadow); flex-shrink:0;
  transition:background .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}
.carousel-arrow:hover{ background:var(--primary); border-color:var(--primary); color:#fff; box-shadow:var(--shadow-lg); }
.carousel-arrow svg{ width:16px; height:16px; }
/* Hidden below 980px for every .ticket-carousel's own arrows (live events,
   VIP guests, related guests) — their nav buttons live in
   .public-section-head (a sibling of .ticket-carousel, not a descendant of
   it), so :has() is what actually scopes this to sections that contain
   one; .ticket-row's native overflow-x:auto makes touch-scroll a working
   fallback there. .carousel-arrow is reused as-is (unscoped) for the
   department carousel further down this file, which is a JS transform
   carousel with no native scroll fallback — hiding its only nav control on
   mobile would leave it with no way to advance at all, so that one keeps
   its arrows at every width. */
@media (max-width:980px){ .public-section:has(.ticket-carousel) .carousel-arrow{ display:none; } }

/* Each .ticket is a plain div (not an <a>) carrying data-href, exactly like
   the .hero-spotlight it replaces — several of its variants nest a genuine
   link of their own inside (.stub-cta's "View", the like button, etc.),
   and nesting an <a> inside another <a> is invalid HTML browsers handle
   inconsistently. initLiveTicketCarousel/initTicketGridClicks (public.js)
   navigate on click UNLESS the click landed on one of those real links. */
.ticket{
  flex:0 0 320px; scroll-snap-align:start; display:flex; background:var(--surface);
  border-radius:18px; border:1px solid var(--border); overflow:hidden; box-shadow:var(--shadow-sm); cursor:pointer; transition:transform .2s ease, box-shadow .2s ease;
}
.ticket:hover{ transform:translateY(-4px); box-shadow:var(--shadow-md); }
.ticket-body{ position:relative; flex:1; min-width:0; padding:22px 20px; display:flex; flex-direction:column; }
/* Floating Instagram-style heart button — a second, icon-only [data-like-btn]
   alongside the existing "N likes" text stat (not a replacement for it),
   pinned to a card's own corner rather than sitting inline with the other
   facts. Shares the same data-event-id as the text-stat like button on the
   same card, so initLikeButtons() (public.js) keeps both — plus any other
   copy of this same event's like controls elsewhere on the page — in sync
   on click. */
/* .event-status-pill has no margin of its own everywhere else it's reused
   (event-detail's ticket-hero, the hero ticket's live badge, etc. each
   control their own spacing around it already) — inside a ticket card specifically it's
   always immediately followed by .tags-inline or the title, so it needs
   its own bottom margin here or the two sit flush with only their line-
   height separating them. align-self:flex-start undoes the stretch that
   .ticket-body's column flex layout otherwise applies to its direct
   children by default, which was forcing every pill to the card's full
   width instead of hugging its own text ("LIVE NOW" vs "UPCOMING" ending
   up the same width). */
.ticket-body .event-status-pill{ margin-bottom:10px; align-self:flex-start; }
.tags-inline{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:10px; }
/* var(--text) (not a static navy hex) keeps this legible in dark mode too,
   where --primary-100's pale pill background goes dark and a static navy
   text would disappear into it. */
.tags-inline span{ font-size:11px; font-weight:700; color:var(--text); background:var(--primary-100); padding:3px 9px; border-radius:999px; }
.ticket-body h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:18px; color:var(--text); margin:0 0 8px; line-height:1.3; }
.ticket-body p{ font-size:13.5px; color:var(--muted); line-height:1.55; margin:0 0 14px; }
.ticket-facts{ font-size:12.5px; color:var(--muted); display:flex; flex-direction:column; gap:4px; margin-top:auto; padding-top:10px; border-top:1px dashed var(--border); }
.ticket-facts b{ color:var(--text); }
/* [data-like-btn] only shows up in one place now — event_detail.html's own
   .ticket-hero-stat (views + like, on the event's own page). Every other
   card's like/view display was pulled back out again. Reset back to
   looking like plain text (a real <button>, for a11y/semantics, not a
   <span>) — display:inline-flex + align-items:center keeps the icon and
   count on one line, which a bare <button> doesn't do on its own the way
   an inline <span> would. .is-liked (added client-side by
   initLikeButtons() in public.js, both on click and from a localStorage
   "already liked this browser" check on load) fills the heart and tints
   it red instead of leaving it an outline. */
[data-like-btn]{
  display:inline-flex; align-items:center; gap:4px; appearance:none; background:none; border:none;
  padding:0; margin:0; font:inherit; color:inherit; cursor:pointer; white-space:nowrap;
}
[data-like-btn]:disabled{ cursor:default; }
[data-like-btn].is-liked{ color:var(--danger); }
[data-like-btn].is-liked svg{ fill:var(--danger); }
/* .like-pop (public.js's initLikeButtons) — only added on the actual click
   that triggers a like, never the page-load pass that re-marks already-
   liked buttons from localStorage, so this plays once as real feedback
   instead of every heart silently "popping" on every page load. */
@keyframes likePop{
  0%{ transform:scale(1); }
  35%{ transform:scale(1.4); }
  60%{ transform:scale(.92); }
  100%{ transform:scale(1); }
}
[data-like-btn].like-pop svg{ animation:likePop .4s ease; }
.ticket-stub{
  width:88px; flex-shrink:0; background:var(--primary); color:#fff; display:flex; flex-direction:column;
  align-items:center; justify-content:space-between; padding:18px 8px; position:relative;
  border-left:2px dashed rgba(255,255,255,.25); border-radius:0 18px 18px 0;
}
/* z-index:2 keeps these "hole punch" notches — half of each circle sits
   inside .ticket-stub's own box — above .ticket-stub-img below rather than
   being partly covered by it (its z-index:auto position/DOM order would
   otherwise paint before these, since ::before/::after count as this
   element's very first/last "children" for stacking purposes). */
.ticket-stub::before, .ticket-stub::after{ content:""; position:absolute; left:-9px; width:18px; height:18px; border-radius:50%; background:var(--bg); z-index:2; }
.ticket-stub::before{ top:-9px; }
.ticket-stub::after{ bottom:-9px; }
/* z-index:1 on both — without it, these plain (position:static) flex items
   painted *behind* .ticket-stub-img/-tint below (auto-z-index positioned
   descendants sort above non-positioned ones regardless of DOM order),
   leaving the arrow fully hidden under the tint and only the vert text
   dimly showing through it by accident of being light text on a dark
   overlay. z-index promotes both onto the same explicit stacking level as
   the notches above, which already needed this for the same reason. */
.ticket-stub .vert{ position:relative; z-index:1; writing-mode:vertical-rl; transform:rotate(180deg); font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.6); font-weight:700; }
.ticket-stub-arrow{ position:relative; z-index:1; width:26px; height:26px; border-radius:50%; background:#ef7e1a; color:#fff; display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s ease; }
.ticket-stub-arrow svg{ width:13px; height:13px; }
.ticket:hover .ticket-stub-arrow{ background:#ff9a45; }

/* When an event has a featured_image, this pair of elements (added first,
   right after the opening .ticket-stub tag — see index.html/event_detail.
   html/_events_results.html) replaces the flat navy/grey background with
   that photo: the <img> fills the box, and .ticket-stub-tint darkens it
   just enough to keep .vert's light text and the arrow/stub-cta legible.
   Unscoped (not .page-events-browse-prefixed like most of .ticket-stub's
   own rules) since it only adds a positioned overlay — it doesn't touch
   the background colour those page-specific rules exist to vary, so the
   exact same pair works unmodified inside that page's own .ticket-stub. */
.ticket-stub-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; border-radius:0 18px 18px 0; }
.ticket-stub-tint{ position:absolute; inset:0; background:linear-gradient(180deg, rgba(0,20,45,.4), rgba(0,20,45,.75)); border-radius:0 18px 18px 0; }
.ticket-stub.is-past .ticket-stub-tint{ background:linear-gradient(180deg, rgba(45,52,63,.5), rgba(45,52,63,.8)); }

/* Past-events ticket carousel (homepage "Past events" section) — same
   .ticket/.ticket-stub shell as Live/Upcoming, just recoloured to read as
   "already happened" instead of "come to this": a muted card body and a
   grey (not brand-navy) stub. */
.ticket.is-past{ background:var(--bg); }
.ticket.is-past .ticket-body{ opacity:.92; }
.ticket-stub.is-past{ background:linear-gradient(180deg,#6d7889,#4b5563); }

/* ------------------------------ status pill ------------------------------ */
/* One consistent soft/pale treatment everywhere this pill shows up (ticket
   cards, event-detail's hero + related-events, the trending row, the
   calendar's .up-status) — no more per-context solid-colour/translucent-
   white variants. */
.event-status-pill{
  display:inline-flex; align-items:center; gap:6px; padding:5px 13px; border-radius:999px; font-size:11px; font-weight:800;
  letter-spacing:.3px; text-transform:uppercase; background:var(--bg); color:var(--muted);
}
.event-status-pill.status-live{ background:var(--accent-100, var(--primary-100)); color:var(--accent, var(--primary-text)); }
.event-status-pill.status-live::before{
  content:""; width:6px; height:6px; border-radius:50%; background:currentColor; flex-shrink:0;
  
  animation: pulse 1.6s infinite;
}
.event-status-pill.status-upcoming{ background:var(--primary-100); color:var(--upcoming-text, var(--primary)); }
.event-status-pill.status-closed{ background:var(--bg); color:var(--muted); }
/* .status-live's own pulsing dot, mirrored for "already happened" — a
   small checkmark bubble instead of a glowing dot. */
.event-status-pill.status-closed::before{
  content:"\2713"; display:inline-flex; align-items:center; justify-content:center;
  width:12px; height:12px; border-radius:50%; background:var(--muted); color:var(--surface);
  font-size:8px; font-weight:800; flex-shrink:0;
}
.event-status-pill.lg{ padding:8px 18px; font-size:12.5px; }

/* --------------------------- generic section shell ------------------------ */
/* .public-section is a CONTENT wrapper only (max-width + padding, centred) —
   it has no width of its own to paint a background across, so alternating
   a section's background full-bleed (the way every reference mockup does
   it) needs a separate, wider parent: .section-pale/.section-white are that
   parent, a plain full-width band with nothing but a background colour,
   wrapping an ordinary untouched .public-section inside it. Keeping
   .public-section itself completely unchanged means it still picks up the
   exact same responsive gutters from the consistent-page-container rules
   at the end of this file regardless of which band it's sitting in.

   Every reference mockup's body is pure white with alternating bands one
   shade darker (.pale) — this app's own body background token (--bg) is
   already that slightly-off-white "pale" tone (it was designed for the
   dashboard, not a marketing page), NOT true white, so a section left
   unbanded here reads as pale, not white, the reverse of the mockups.
   .section-white is therefore the one that needs an explicit background
   (var(--surface), true white in light mode) while .section-pale can just
   restate var(--bg) for clarity even though that's already the page
   default — both stay dark-mode-safe since --surface is always the more
   "elevated/lighter" of the two tokens in either theme. */
.section-pale{ background:var(--bg); }
.section-white{ background:var(--surface); }
.public-section{ max-width:1280px; margin:0 auto; padding:56px 40px; }
.public-section-head{ display:flex; align-items:flex-end; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:26px; }
/* PESIMSR-style eyebrow dot — a small solid circle before the label,
   matching that reference site's `.eyebrow::before`. Skipped on
   .live-eyebrow (below), which already carries its own red pulsing
   .live-dot as a status indicator; a second dot there would just double up. */
.public-section-eyebrow{
  display:inline-flex; align-items:center; gap:9px;
  font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--primary-text);
}
.public-section-eyebrow::before{
  content:""; width:7px; height:7px; border-radius:50%; background:var(--accent, var(--primary-text)); flex-shrink:0;
}
.live-eyebrow::before{ display:none; }
.public-section h2{ font-family:'Playfair Display',serif; font-size:clamp(26px,3vw,38px); font-weight:800; margin:6px 0 0; line-height:1.15; }

/* ---------------------------------- live now ------------------------------- */
.live-eyebrow{ display:inline-flex; align-items:center; gap:7px; }
/* Orange (not --danger red — every other "live" indicator on the site is
   the brand orange, this was the one leftover red one) with the same
   glowing halo as .ht-live's dot above. */
.live-dot{
  width:8px; height:8px; border-radius:50%; background:var(--accent, var(--primary-text)); display:inline-block;
  
  animation: pulse 1.6s infinite;
}
@keyframes livePulse{ 0%,100%{ opacity:1; transform:scale(1); } 50%{ opacity:.4; transform:scale(1.3); } }
@media (prefers-reduced-motion:reduce){ .live-dot, .live-badge-dot{ animation:none; } }
.live-strip{ display:flex; gap:18px; overflow-x:auto; padding:2px 2px 10px; scroll-snap-type:x proximity; }
.live-strip::-webkit-scrollbar{ height:6px; }
.live-strip::-webkit-scrollbar-thumb{ background:var(--border); border-radius:10px; }
.live-card{
  flex:0 0 280px; scroll-snap-align:start; background:linear-gradient(160deg,#2a0f24,#4a0f2e); border-radius:16px; padding:20px;
  color:#fff; box-shadow:var(--shadow); border:1px solid rgba(255,90,90,.3); transition:transform .2s ease;
}
.live-card:hover{ transform:translateY(-4px); }
.live-badge{
  display:inline-flex; align-items:center; gap:6px; background:var(--danger); color:#fff; font-size:10.5px; font-weight:800;
  letter-spacing:.4px; padding:4px 10px; border-radius:20px; margin-bottom:14px;
}
.live-badge-dot{ width:6px; height:6px; border-radius:50%; background:#fff; animation:livePulse 1.2s ease-in-out infinite; }
.live-card h3{ font-family:'Playfair Display',serif; font-size:16px; font-weight:800; margin:0 0 10px; color:#fff; }
.live-card-row{ display:flex; align-items:center; gap:7px; font-size:12.5px; color:rgba(255,255,255,.75); margin-bottom:5px; }
.live-card-row svg{ width:14px; height:14px; flex-shrink:0; }
.live-card-cta{ display:inline-flex; align-items:center; gap:5px; margin-top:10px; font-size:12.5px; font-weight:700; color:#ff9d9d; }

/* ---------------------------------- upcoming --------------------------------- */
/* Homepage's "Upcoming events" teaser — one full-width dark "big ticket"
   card per event (same shell/shape as the event-detail page's own hero
   ticket: navy gradient + dot pattern, dashed-seam stub on the right),
   stacked instead of a grid since each card is full-width. The stub shows
   the event's featured_image (.ubc-featured-img) when it has one, falling
   back to the QR/date/add-to-calendar content (.has-image toggles which
   renders — see index.html) otherwise. The inside of the card is
   deliberately simple — title, a short line of description, a couple of
   tag pills, one plain date line — not a miniature copy of the detail
   page's own denser fact-chip/campus layout.
   New, homepage-only class names (not the .page-event-detail-scoped
   .big-ticket/.bt-* — those only exist inside that page's own wrapper) so
   nothing here can leak onto or be leaked onto by the event-detail page's
   similarly-shelled but page-scoped version. */
/* A horizontally-scrolling row (initTicketCarousel wires the arrow
   buttons + click-through, same as the Live events/VIP carousels) when
   there's more than one event — .single (added server-side off
   upcoming_curated's own length, not a media query) drops back to one
   plain full-width block instead, since a lone card has nothing to
   scroll to and a fixed flex-basis would otherwise leave empty space
   beside it. */
.upcoming-big-list{ display:flex; gap:22px; overflow-x:auto; padding-bottom:4px; scroll-snap-type:x proximity; scroll-behavior:smooth; }
.upcoming-big-list::-webkit-scrollbar{ height:6px; }
.upcoming-big-list::-webkit-scrollbar-thumb{ background:var(--border); border-radius:99px; }
.upcoming-big-list.single{ display:block; overflow-x:visible; }
.upcoming-big-card{
  position:relative; flex:0 0 min(720px,88vw); scroll-snap-align:start; display:flex; overflow:hidden; border-radius:26px; cursor:pointer;
  background:linear-gradient(150deg, var(--primary) 0%, var(--primary-600) 120%);
  transition:transform .2s ease, box-shadow .2s ease;
}
.upcoming-big-list.single .upcoming-big-card{ width:100%; }
.upcoming-big-card:hover{ transform:translateY(-3px); box-shadow:0 26px 60px rgba(0,10,30,.3); }
.upcoming-big-card::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.07) 1.4px, transparent 1.4px); background-size:22px 22px;
}
.ubc-main{ position:relative; z-index:1; flex:1; min-width:0; padding:32px 36px; color:#fff; }
.ubc-main h3{ font-family:'Playfair Display',serif; font-size:24px; font-weight:800; color:#fff; margin:0 0 8px; line-height:1.3; }
.ubc-sub{ font-size:14px; color:rgba(255,255,255,.72); line-height:1.55; margin:0 0 14px; max-width:64ch; }
.ubc-tags{ display:flex; gap:8px; flex-wrap:wrap; margin-bottom:18px; }
.ubc-tags span{ font-size:11px; font-weight:700; color:#fff; background:rgba(255,255,255,.14); padding:4px 10px; border-radius:999px; }
.ubc-date-row{ display:flex; align-items:center; gap:8px; font-size:13px; font-weight:600; color:rgba(255,255,255,.75); }
.ubc-date-row svg{ color:#ff9a45; flex-shrink:0; }

/* A fixed, generous width rather than a computed 1:1 square — same
   reasoning as .page-event-detail .bt-stub: a dynamic square sized off
   this row's own content-driven height fights with .ubc-main's flex:1
   sibling (the browser derives this item's cross size before the row's
   final height — which depends on how much width .ubc-main gets, i.e.
   how much this item takes — is actually known), so it lands on a stale,
   too-small estimate. 260px reads as roughly square against this card's
   typical rendered height while sidestepping that circularity entirely. */
.ubc-stub{
  position:relative; z-index:1; width:260px; flex-shrink:0; background:rgba(0,0,0,.16); color:#fff;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px; padding:24px 16px;
  border-left:2px dashed rgba(255,255,255,.28); overflow:hidden;
}
.ubc-stub.has-image{ padding:0; background:none; border-left-color:rgba(255,255,255,.5); }
.ubc-featured-img{ width:100%; height:100%; object-fit:cover; display:block; }
.ubc-stub::before, .ubc-stub::after{ content:""; position:absolute; left:-13px; width:26px; height:26px; border-radius:50%; background:var(--bg); }
.ubc-stub::before{ top:-13px; }
.ubc-stub::after{ bottom:-13px; }
.ubc-stub .admit{ font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:rgba(255,255,255,.6); font-weight:700; }
.ubc-qr{ display:grid; grid-template-columns:repeat(7,4px); grid-template-rows:repeat(7,4px); gap:2px; background:#fff; padding:7px; border-radius:8px; }
.ubc-qr i{ background:var(--primary); border-radius:1px; }
.ubc-qr i.o{ background:transparent; }
.ubc-date{ font-family:'Playfair Display',serif; font-weight:700; font-size:14.5px; text-align:center; line-height:1.3; color:#fff; }
.ubc-cal-link{ display:inline-flex; align-items:center; gap:5px; font-size:11px; font-weight:700; color:rgba(255,255,255,.6); }
.ubc-cal-link:hover{ color:#fff; }

@media (max-width:760px){
  .upcoming-big-card{ flex-direction:column; }
  .ubc-stub{ width:100%; flex-direction:row; flex-wrap:wrap; border-left:none; border-top:2px dashed rgba(255,255,255,.28); padding:18px 22px; }
  .ubc-stub.has-image{ aspect-ratio:16/9; padding:0; border-top-color:rgba(255,255,255,.5); }
  .ubc-stub::before, .ubc-stub::after{ display:none; }
  .ubc-date br{ display:none; }
}

/* ---------------------------------- trending -------------------------------- */
.event-status-pill.sm{ padding:3px 9px; font-size:9.5px; flex-shrink:0; }

/* Vertical "ticket stub" cards, each with its own geometric pattern panel
   (see .trend-pattern-* — flat CSS patterns, never a photo, per the
   no-cover-photos rule). Hovering doesn't move the small trigger card
   itself — that would jitter, since the enlarged element would reposition
   out from under the cursor — instead each ticket already carries a
   fixed-position popup child (invisible/inert at rest) that scales and
   rotates in from the trigger's own vertical orientation to a full
   horizontal ticket centred on the viewport, while a page-wide backdrop
   blurs everything else via :has(), same technique as the related-events
   collage hover elsewhere on this page. */
.trend-tickets-wrap{ position:relative; }
.trend-tickets-backdrop{
  position:fixed; inset:0; z-index:150; opacity:0; pointer-events:none;
  background:color-mix(in srgb, var(--bg) 55%, transparent); backdrop-filter:blur(14px);
  transition:opacity .35s ease;
}
.trend-tickets-wrap:has(.trend-ticket:hover) .trend-tickets-backdrop{ opacity:1; }
/* 6 trending events (app.views.home), not 5 — divides evenly at every
   breakpoint below (3x2 on desktop, 2x3 on tablet, 2x3 on mobile) instead
   of leaving a lone card dangling in the last row. */
.trend-tickets{ display:grid; grid-template-columns:repeat(6, 1fr); gap:20px; padding:8px 2px 16px; }
@media (max-width:980px){ .trend-tickets{ grid-template-columns:repeat(3, 1fr); } }
@media (max-width:640px){ .trend-tickets{ grid-template-columns:repeat(2, 1fr); } }

.trend-ticket{ position:relative; }

/* One single unified ticket shape (art + facts in one card, one shadow,
   one hover lift) rather than two separate boxes stacked with a gap —
   overflow stays visible on the whole card so the lanyard notch at the
   top edge and the tear-line punches at the art/facts seam can actually
   poke past the card's own edge instead of being clipped; each zone's own
   background is still confined to its own border-radius regardless. */
.trend-ticket-card{
  position:relative; border-radius:16px; box-shadow:0 12px 26px rgba(0,10,30,.16);
  display:flex; flex-direction:column; transition:transform .3s ease, box-shadow .3s ease;
}
.trend-ticket:hover .trend-ticket-card{ transform:translateY(-6px); box-shadow:0 20px 38px rgba(0,10,30,.24); }
/* backdrop-filter:blur does nothing useful here — the art zones are flat
   solid colour now (no texture), so "blurring" a uniform patch produces
   the same uniform patch back; all it did was tint every notch the same
   washed grey regardless of the card colour behind it. A plain opaque
   var(--bg) fill is simpler and reads correctly as a genuine punched hole
   in both the small-card (plain page behind) and popup (matches the
   backdrop's own base tone closely enough) contexts. */
.trend-ticket-notch-top{
  position:absolute; top:-9px; left:50%; transform:translateX(-50%); width:26px; height:18px;
  border-radius:0 0 999px 999px; background:var(--bg); z-index:4;
}

/* Fixed pixel height + overflow:hidden — deterministic, unlike
   aspect-ratio which can still be forced larger by an intrinsic-sized
   child (that's what was inflating the card whenever a title/chip needed
   more room than the ratio implied). Nothing here needs to poke past this
   box's own edge any more, so it's safe to actually clip. */
.trend-ticket-top{
  position:relative; height:320px; color:#fff; display:flex; border-radius:16px 16px 0 0; overflow:hidden;
}
.trend-ticket-rank{
  position:absolute; top:12px; right:12px; font-family:'Playfair Display',serif; font-size:12.5px; font-weight:800;
  background:rgba(0,0,0,.3); padding:3px 10px; border-radius:20px; letter-spacing:.5px; z-index:2; text-align:center;
}
.trend-ticket-status{ position:absolute; top:46px; right:12px; z-index:2; }

/* The event's identity (title/description/campus/tag) is set as genuine
   vertical type — writing-mode does the actual text flow, one shared
   transform:rotate(180deg) on the outer block flips the whole already-
   vertical subtree the right way up to read bottom-to-top, exactly like a
   printed ticket stub's sideways title. Deliberately plain block stacking
   (h3/p/div as ordinary children), not nested flex — a flex column inside
   a writing-mode container makes each child negotiate its own cross-size
   independently, and one of them (the title, here) can lose that
   negotiation and get squeezed to zero/clipped. Plain blocks just stack
   along the block axis in document order with predictable spacing. */
.trend-ticket-vertical{
  position:relative; z-index:1; margin-right:auto; flex-shrink:0; height:100%; padding:20px 14px 18px;
  border-radius:0 16px 16px 0; writing-mode:vertical-rl; transform:rotate(180deg); overflow:hidden;
}
/* No white-space:nowrap on any of these — an unbreakable run forces the
   text's vertical extent past whatever height the aspect-ratio gave the
   card, inflating the whole ticket regardless of overflow:hidden here
   (min-content still wins). Letting each wrap into more "columns" instead
   keeps the card at its intended height. */
.trend-ticket-vertical h3{ font-size:18px; font-weight:800; color:#fff; margin:0 0 16px; line-height:1.25; }
.trend-ticket-vertical p{ font-size:10.5px; font-weight:500; color:rgba(255,255,255,.78); margin:0 0 16px; line-height:1.4; }
.trend-ticket-chips{ display:flex; flex-direction:row; gap:6px; margin:0 0 16px; }
.trend-ticket-chip{
  font-size:8.5px; font-weight:700; text-transform:uppercase; letter-spacing:.3px; color:#fff;
  background:rgba(255,255,255,.2); border:1px solid rgba(255,255,255,.3); border-radius:999px; padding:6px 4px;
}
.trend-ticket-venue{
  display:flex; flex-direction:row; align-items:center; gap:5px; font-size:10px; font-weight:700; color:#fff; margin:0;
}
.trend-ticket-venue svg{ width:12px; height:12px; flex-shrink:0; }

/* Horizontal tear line — the classic ticket-stub cut — between the art
   zone and the facts strip below, with punches biting the card's own
   left/right edges at that seam. */
.trend-ticket-tear{ position:relative; z-index:2; border-top:2px dashed #000; }
.trend-ticket-tear::before,
.trend-ticket-tear::after{
  content:""; position:absolute; top:50%; transform:translateY(-50%); width:22px; height:22px;
  border-radius:50%; background:var(--bg); z-index:5;
}
.trend-ticket-tear::before{ left:-11px; }
.trend-ticket-tear::after{ right:-11px; }

/* Date/time/venue/views/likes — the facts strip forming the bottom of the
   same single card (plain surface colour, matching a ticket's barcode
   footer), not a separate boxed component. Fixed height + overflow:hidden
   (same deterministic approach as .trend-ticket-top above) — how many of
   these wrap onto a second line varies per card (view/like counts are
   different digit-lengths), which otherwise made cards in the same row
   different total heights. */
.trend-ticket-facts{
  display:flex; flex-wrap:wrap; align-content:flex-start; gap:8px 14px; padding:12px 14px 12px; height:92px; overflow:hidden;
  background:var(--surface); border-radius:0 0 16px 16px;
  font-size:10.5px; font-weight:600; color:var(--muted); box-sizing:border-box;
}
.trend-ticket-facts span{ display:flex; align-items:center; gap:5px; white-space:nowrap; }
.trend-ticket-facts svg{ width:12px; height:12px; flex-shrink:0; }
.trend-ticket-barcode{
  flex-basis:100%; height:13px; margin-top:4px; opacity:.55;
  background-image:repeating-linear-gradient(90deg, var(--muted) 0 2px, transparent 2px 5px);
}

/* Popup on hover is the exact same ticket card (same markup/classes,
   nested as .trend-ticket-card-lg below) just bigger and centred on the
   viewport — not a redesigned layout — since it's the trigger card
   "opening up": rotate(-90deg)→rotate(0) is the same vertical-to-
   horizontal turn the small card itself represents. */
.trend-ticket-popup{
  position:fixed; top:50%; left:50%; z-index:200; text-decoration:none;
  width:640px; max-width:calc(100vw - 48px);
  opacity:0; visibility:hidden; pointer-events:none;
  transform:translate(-50%,-50%) rotate(-90deg) scale(.55);
  transition:transform .5s cubic-bezier(.2,.8,.2,1), opacity .35s ease, visibility 0s linear .35s;
}
.trend-ticket:hover .trend-ticket-popup{
  opacity:1; visibility:visible; pointer-events:auto; transform:translate(-50%,-50%) rotate(0deg) scale(1);
  transition:transform .5s cubic-bezier(.2,.8,.2,1), opacity .35s ease, visibility 0s linear 0s;
}
@media (prefers-reduced-motion:reduce){ .trend-ticket-popup{ transition:opacity .2s ease; transform:translate(-50%,-50%) scale(1); } }

.trend-ticket-card-lg{ box-shadow:0 40px 90px rgba(0,10,30,.35); }
.trend-ticket-card-lg .trend-ticket-top{ height:220px; }
/* The popped-open popup shows the event's own featured_image here in place
   of the small card's flat trend-pattern-* colour (see index.html — only
   .trend-ticket-card-lg gets the .has-image class + inline background-
   image, never the small closed .trend-ticket-card). The trend-pattern-*
   class stays on the element regardless; it just sits unseen behind the
   opaque cover-sized photo. Text stays legible thanks to the existing
   .trend-ticket-vertical gradient below, not an extra tint here. */
.trend-ticket-card-lg .trend-ticket-top.has-image{ background-size:cover; background-position:center; }
/* The small card's title/description/tags/venue read sideways because the
   card itself is still "closed" (vertical). Once popped open full-size,
   that's no longer true — reset writing-mode so the same content reads
   normally, anchored to the bottom of the art zone like a typical event
   card caption. */
.trend-ticket-card-lg .trend-ticket-vertical{
  writing-mode:horizontal-tb; transform:none; align-self:flex-end; width:100%; margin:0;
  padding:20px 24px; display:flex; flex-direction:column; align-items:flex-start; gap:10px;
  background:linear-gradient(0deg, rgba(0,0,0,.65) 45%, transparent);
}
.trend-ticket-card-lg .trend-ticket-vertical h3{ font-family:'Playfair Display',serif; font-size:24px; margin:0; }
.trend-ticket-card-lg .trend-ticket-vertical p{ font-size:13.5px; margin:0; max-width:480px; }
.trend-ticket-card-lg .trend-ticket-chips{ margin:0; }
.trend-ticket-card-lg .trend-ticket-venue{ flex-direction:row; font-size:12px; }
.trend-ticket-card-lg .trend-ticket-facts{ font-size:12.5px; padding:16px 20px 14px; height:auto; overflow:visible; }
@media (max-width:720px){
  .trend-ticket-popup{ width:calc(100vw - 32px); }
  .trend-ticket-card-lg .trend-ticket-top{ height:180px; }
}

/* Four flat solid colours (no photos, no texture) standing in for the
   reference's ticket artwork panels — each mixed with black for a
   deeper, richer tone rather than the raw bright accent colour, so it
   reads as one deliberate dark palette next to the solid-black text zone
   rather than a bright patch butting against a black patch. */
.trend-pattern-a{ background-color:color-mix(in srgb, var(--primary) 65%, black); }
.trend-pattern-b{ background-color:color-mix(in srgb, var(--info) 65%, black); }
.trend-pattern-c{ background-color:color-mix(in srgb, var(--warning) 65%, black); }
.trend-pattern-d{ background-color:color-mix(in srgb, var(--success) 65%, black); }

/* ----------------------------------- past ----------------------------------- */
.past-timeline{ position:relative; display:flex; flex-direction:column; padding-left:26px; }
.past-timeline::before{ content:""; position:absolute; left:5px; top:6px; bottom:6px; width:2px; background:var(--border); }
.past-timeline-item{
  position:relative; display:flex; align-items:center; gap:14px; flex-wrap:wrap; padding:14px 0; border-bottom:1px solid var(--border);
  color:var(--text);
}
.past-timeline-item:last-child{ border-bottom:none; }
.past-timeline-dot{
  position:absolute; left:-26px; top:50%; transform:translateY(-50%); width:12px; height:12px; border-radius:50%;
  background:var(--surface); border:2px solid var(--muted-2);
}
.past-timeline-date{ font-size:12px; font-weight:700; color:var(--muted); flex-shrink:0; width:110px; }
.past-timeline-title{ flex:1; font-size:14px; font-weight:700; min-width:200px; }
.past-timeline-cta{ display:inline-flex; align-items:center; gap:5px; font-size:12.5px; font-weight:700; color:var(--primary-text); flex-shrink:0; }

/* ------------------------------- campus tiles ------------------------------ */
/* Pixel-matched to the pes-events-redesign.html mockup's own .campus-grid/
   .campus-tile (literal hex, same as the mega-menu/search-trigger rules
   above) rather than the app's --primary/--primary-600 tokens, so it stays
   identical to the mockup regardless of dark-mode token overrides. */
.campus-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:22px; }
@media (max-width:980px){ .campus-grid{ grid-template-columns:repeat(2, 1fr); } }
@media (max-width:620px){ .campus-grid{ grid-template-columns:1fr; } }
.campus-tile{
  border-radius:18px; padding:32px 26px; color:#fff; background:linear-gradient(150deg,#00264d,#004080);
  min-height:180px; display:flex; flex-direction:column; justify-content:space-between; position:relative; overflow:hidden;
}
.campus-tile::before{
  content:""; position:absolute; inset:0; z-index:0;
  background-image:radial-gradient(rgba(255,255,255,.08) 1.2px, transparent 1.2px); background-size:20px 20px;
}
/* Campus photo (Campus.image, index.html) — a full tile-height panel flush
   against the right edge, its width driven off that same height via
   aspect-ratio:1/1 so a square source is never cropped or distorted (only
   ever fit into a box that's genuinely square, same reasoning as before —
   just sized to the tile's full height now instead of a small fixed chip).
   Flush right:0/top:0/bottom:0 means .campus-tile's own overflow:hidden +
   border-radius clips its corners to match the card automatically, so it
   doesn't need (or fight) its own border-radius. The left edge still fades
   into the navy dot field via mask-image rather than a hard-edged cutout. */
.campus-tile-photo{
  position:absolute; z-index:0; top:0; right:0; bottom:0; height:100%; aspect-ratio:1/1;
  background-size:cover; background-position:center;
  -webkit-mask-image:linear-gradient(90deg, transparent 0%, rgba(0,0,0,.5) 32%, #000 58%);
  mask-image:linear-gradient(90deg, transparent 0%, rgba(0,0,0,.5) 32%, #000 58%);
}
.campus-tile .name{ font-family:'Playfair Display',serif; font-weight:700; font-size:21px; position:relative; z-index:1; }
.campus-tile .link{ font-size:13px; font-weight:700; color:#ff9a45; display:inline-flex; align-items:center; gap:6px; position:relative; z-index:1; }

/* -------------------------------- department cards -------------------------- */
.dept-grid{ display:grid; grid-template-columns:repeat(3, 1fr); gap:16px; }
@media (max-width:980px){ .dept-grid{ grid-template-columns:repeat(2, 1fr); } }
@media (max-width:620px){ .dept-grid{ grid-template-columns:1fr; } }
.dept-card{
  display:flex; align-items:center; gap:14px; background:#fff; border:1px solid #e2e8f0; border-radius:14px;
  padding:16px 18px; transition:.2s;
}
.dept-card:hover{ border-color:#ef7e1a; box-shadow:0 2px 10px rgba(0,38,77,.06); }
.dept-badge{
  width:44px; height:44px; border-radius:50%; background:#00264d; color:#fff; font-family:'Playfair Display',serif;
  font-weight:700; font-size:15px; display:flex; align-items:center; justify-content:center; flex-shrink:0;
}
.dept-card .name{ font-weight:700; font-size:14.5px; color:#0a1626; }
.dept-card .cnt{ font-size:12px; color:#5c6b80; }
.dept-card .go{ margin-left:auto; color:#ef7e1a; font-weight:700; font-size:13px; }

/* ---------------------------------- tag cloud -------------------------------- */
/* home-tag-pill (not .tag-pill) — guests.html's own industry-chip filter
   also uses an unscoped .tag-pill class further down this file; both used
   to share the plain name with neither page-scoped, so their :hover rules
   partially collided (this rule's own background:#ef7e1a hover here + the
   guests one's later color:var(--accent) hover there both won for
   different properties on the SAME element), landing guests.html on
   orange-on-orange — invisible hover text. Renaming this one (index.html's
   own tag cloud, literal-hex like the rest of that mockup-matched section)
   is simpler than retrofitting a page-wide wrapper div around index.html's
   content just to scope it the usual .page-* way. */
.tag-cloud{ display:flex; flex-wrap:wrap; gap:10px; }
.home-tag-pill{
  padding:9px 16px; border-radius:999px; background:#fff; color:#ef7e1a; font-weight:700; font-size:13px;
  border:1px solid #cbd6e3; transition:.2s;
}
.home-tag-pill:hover{ background:#ef7e1a; color:#fff; }
.home-tag-pill .n{ opacity:.65; font-weight:600; margin-left:4px; }

/* -------------------------------- guest stack ------------------------------ */
/* Homepage teaser only — an Instagram-story-style stack of overlapping
   circular avatars, each later one drawn over the previous (natural DOM
   paint order + negative margin, no z-index juggling needed at rest).
   No VIP ribbon/designation/categories/LinkedIn here — those live on the
   full "View all" guests directory instead; this is just a face, or the
   guest's name as a text fallback when there's no photo. All 10 always
   fit in one row with no scrolling — sized (with the breakpoints below)
   to stay inside the .public-section's own max-width at each viewport,
   rather than letting content overflow into a horizontal scrollbar. */
.guest-stack{ display:flex; align-items:center; justify-content:center; padding:6px 2px 14px; }
.guest-stack-avatar{
  position:relative; z-index:1; width:150px; height:150px; flex-shrink:0; margin-left:-50px;
  border-radius:50%; overflow:hidden; border:5px solid var(--bg); background:var(--surface-2);
  box-shadow:var(--shadow); display:flex; align-items:center; justify-content:center;
  transition:transform .2s ease;
}
.guest-stack-avatar:first-child{ margin-left:0; }
.guest-stack-avatar:hover{ transform:translateY(-8px) scale(1.05); z-index:2; }
.guest-stack-avatar img{ width:100%; height:100%; object-fit:cover; }
.guest-stack-fallback{ font-family:'Playfair Display',serif; font-size:36px; font-weight:800; color:#fff; letter-spacing:1px; }
/* Muted/desaturated ("matte") takes on the accent palette instead of the
   raw glossy/saturated colours used elsewhere — mixed with a flat neutral
   grey rather than hardcoding a whole new unrelated palette. */
.guest-stack-color-primary{ background:color-mix(in srgb, var(--primary) 55%, #8a8f98); }
.guest-stack-color-info{ background:color-mix(in srgb, var(--info) 55%, #8a8f98); }
.guest-stack-color-warning{ background:color-mix(in srgb, var(--warning) 55%, #8a8f98); }
.guest-stack-color-success{ background:color-mix(in srgb, var(--success) 55%, #8a8f98); }
.guest-stack-color-danger{ background:color-mix(in srgb, var(--danger) 55%, #8a8f98); }
@media (max-width:900px){ .guest-stack-avatar{ width:110px; height:110px; margin-left:-35px; border-width:4px; } .guest-stack-fallback{ font-size:26px; } }
@media (max-width:560px){ .guest-stack-avatar{ width:70px; height:70px; margin-left:-22px; border-width:3px; } .guest-stack-fallback{ font-size:17px; } }
@media (max-width:400px){ .guest-stack-avatar{ width:46px; height:46px; margin-left:-15px; border-width:2px; } .guest-stack-fallback{ font-size:11px; } }

/* -------------------------------- guest cards ------------------------------ */
/* Horizontal avatar+text row (not the previous centered/stacked card),
   matching the reference mockup's .guest-card exactly. Fixed 2 columns
   (not auto-fill) — same as the mockup — collapsing to 1 on narrow screens
   alongside .event-body-layout's own breakpoint. */
.guest-card-grid{ display:grid; grid-template-columns:repeat(2,1fr); gap:16px; }
.guest-card{
  position:relative; background:var(--surface); border:1px solid var(--border); border-radius:16px; padding:22px;
  display:flex; gap:16px; align-items:flex-start; transition:box-shadow .2s ease, border-color .2s ease; cursor:pointer;
}
.guest-card:hover{ box-shadow:var(--shadow-sm); border-color:var(--border-strong, var(--border)); }
.guest-card-vip{
  position:absolute; top:14px; right:14px; display:inline-flex; align-items:center; gap:4px;
  background:linear-gradient(135deg, var(--vip), #d9a53a); color:#fff; font-size:9.5px; font-weight:800; padding:4px 9px 4px 7px;
  border-radius:20px; letter-spacing:.4px; box-shadow:0 4px 10px rgba(184,134,11,.35);
}
.guest-avatar-ring{ width:54px; height:54px; border-radius:50%; flex-shrink:0; }
.guest-card-photo{ width:100%; height:100%; border-radius:50%; object-fit:cover; display:block; }
.guest-card-initials{
  display:flex; align-items:center; justify-content:center; background:var(--primary); color:#fff; font-weight:700;
  font-size:17px; font-family:'Playfair Display',serif;
}
.guest-card-name{ font-family:'Playfair Display',serif; font-size:15.5px; font-weight:700; color:var(--text); }
.guest-card-role{ font-size:12.5px; color:var(--muted); margin-top:3px; line-height:1.4; }
.guest-card-cats{ display:flex; flex-wrap:wrap; gap:5px; margin-top:10px; }
.guest-card-linkedin{
  display:inline-flex; align-items:center; gap:5px; margin-top:12px; font-size:11.5px; font-weight:700; color:#0a66c2;
  padding:5px 12px; border-radius:20px; background:rgba(10,102,194,.08); border:1px solid rgba(10,102,194,.18);
}
.guest-card-linkedin:hover{ background:rgba(10,102,194,.16); }
@media (max-width:640px){ .guest-card-grid{ grid-template-columns:1fr; } }

/* --------------------------------- closing CTA ----------------------------- */
/* Pixel-matched to the mockup's own full-bleed .cta-band (literal hex, same
   reasoning as .campus-tile above). .cta-band/.btn-solid are also used
   (independently, with their own values) by other public pages' own
   verbatim mockup ports — each scoped under ITS OWN .page-* wrapper
   (.page-guests .cta-band, .page-event-detail .btn-solid, etc.) further
   down this file. .page-home does the same here so neither side's rules
   can leak into the other. .public-section is reused for its max-width/
   centring only; its own vertical padding is zeroed out here so
   .cta-band's 90px 0 is the only vertical padding in play. */
.page-home.cta-band{ background:#00264d; color:#fff; text-align:center; padding:90px 0; position:relative; overflow:hidden; }
.page-home.cta-band::before{
  content:""; position:absolute; inset:0;
  background-image:radial-gradient(rgba(255,255,255,.07) 1.4px, transparent 1.4px); background-size:26px 26px;
}
.page-home.cta-band .public-section{ padding:0 40px; position:relative; }
.page-home.cta-band .public-section-eyebrow{ justify-content:center; }
.page-home.cta-band .public-section-eyebrow.on-dark{ color:#ff9a45; }
.page-home.cta-band h2{
  font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(28px,3.6vw,44px); max-width:20ch;
  margin:16px auto 24px; line-height:1.18; position:relative; color:#fff;
}
.page-home .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-home .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }

/* ----------------------------- event ticket page --------------------------- */
/* Reproduces the event.html mockup's own CSS verbatim (colours, sizes,
   spacing), scoped under .page-event-detail — a full stacked layout
   (back-bar > event-hero > big-ticket-wrap > body section), not the
   previous overlapping-card design, so no JS-measured overlap/seam-notch
   trick is needed any more (initTicketCardOverlap() in public.js was
   removed; initEventStickyBar() now watches .big-ticket instead of the
   old .ticket-card). views/like row (.ticket-hero-stats/.ticket-hero-like,
   further below, unscoped — this page only) and the gallery/brochures/
   multi-contact/extra-socials/sticky-bar/lightbox sections are additive —
   the mockup has no equivalent, so they keep their own established look. */
.page-event-detail{ font-family:'Noto Sans',-apple-system,sans-serif; color:#0a1626; }
.page-event-detail .wrap{ max-width:1280px; margin:0 auto; padding:0 40px; }
.page-event-detail section{ padding:72px 0; }

.page-event-detail .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-event-detail .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }
.page-event-detail .btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.page-event-detail .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-event-detail .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
.page-event-detail .btn-outline{ border:1.5px solid #cbd6e3; color:#00264d; }
.page-event-detail .btn-outline:hover{ background:#e9eff6; }
.page-event-detail .btn-sm{ padding:9px 18px; font-size:13px; }
.page-event-detail .btn-block{ width:100%; }

.page-event-detail .back-bar{ background:#f6f8fb; border-bottom:1px solid #e2e8f0; }
/* padding-top/-bottom only (not the shorthand "padding:13px 0") — a full
   shorthand here would reset left/right to 0, wiping out the 40px side
   inset .wrap (same element, same .page-event-detail specificity, but
   declared earlier in the file) is supposed to contribute, which is
   exactly what was pulling this breadcrumb's text flush to the edge
   instead of lining up with the header/ticket above and below it. */
.page-event-detail .back-bar-inner{ display:flex; align-items:center; gap:8px; padding-top:13px; padding-bottom:13px; font-size:13px; font-weight:700; color:#5c6b80; flex-wrap:wrap; }
/* :not(.admin-edit-link) — that link already gets its own white-on-navy
   hover state from the shared .admin-edit-link:hover rule; without this
   exclusion, this rule's higher specificity (3 class-level selectors vs.
   .admin-edit-link:hover's 2) was winning and repainting its text navy
   on top of its own navy hover background, making it unreadable. */
.page-event-detail .back-bar-inner a:not(.admin-edit-link):hover{ color:#00264d; }
.page-event-detail .back-bar-inner .sep{ color:#cbd6e3; }
.page-event-detail .back-bar-inner .current{ color:#0a1626; font-weight:700; }
/* Staff-only "Edit event"/"Edit guest" shortcut — {% if request.user|can:
   'change_event' %} / 'change_guest' in event_detail.html/guest_detail.html
   gates it the same way the actual dashboard edit view itself is gated
   (accounts.utils.check_requested_user_permissions), so it never shows for
   a visitor who'd just hit access-denied clicking it. Unscoped (not
   .page-event-detail/.page-guest-detail prefixed) since both pages' own
   .back-bar-inner share the exact same breadcrumb-row look already —
   margin-left:auto pushes it to the row's right edge without needing
   justify-content:space-between on the shared flex row itself (which would
   also shove the breadcrumb's own first link away from the left edge on
   pages with no edit link to show). */
.admin-edit-link{
  display:inline-flex; align-items:center; gap:6px; margin-left:auto; padding:6px 12px;
  border-radius:999px; border:1px solid #cbd6e3; background:#fff; color:#00264d; font-size:12.5px; font-weight:700;
  transition:background .15s ease, border-color .15s ease, color .15s ease;
}
.admin-edit-link:hover{ background:#00264d; border-color:#00264d; color:#fff; }
.admin-edit-link svg{ width:14px; height:14px; flex-shrink:0; }
/* On narrow viewports the breadcrumb text alone can already fill the row,
   so .back-bar-inner's flex-wrap:wrap drops this onto its own line — but
   margin-left:auto (needed to pin it to the row's right edge above) also
   pins it to the RIGHT edge of that wrapped line instead of sitting under
   the breadcrumb text like a normal wrapped item would. */
@media(max-width:620px){
  /* .back-bar-inner's own gap:8px (shared row-gap/column-gap in a wrapping
     flex row) reads as plenty of breathing room between inline breadcrumb
     words, but far too tight once this button drops to its own line below
     a full line of text — margin-top gives that wrapped line extra space
     without touching the gap the breadcrumb links themselves rely on. */
  .admin-edit-link{ margin-left:0; margin-top:6px; }
}

/* ---------- event hero (light, dot pattern) ---------- */
/* Bottom padding must stay >= half the ticket's own rendered height — it
   sits at bottom:0 + translateY(50%) below, so it always straddles this
   hero's bottom edge exactly in half — otherwise the ticket's top half
   reaches past the empty padding and covers real text (status pill/
   description) instead of just the blank space under it. The ticket's
   height varies a fair bit with how many facts an event has (hosted-by,
   campus/department block, second date/time row, etc.), so 240px is sized
   for a fairly full ticket (~480px tall) — a bare-minimum ticket just gets
   extra breathing room instead of ever risking an overlap.
   overflow:visible (not hidden) — the ticket is a child of this section now
   (see .big-ticket-wrap below), and its bottom half is meant to extend
   past this section's own edge into the content below; overflow:hidden
   would clip that half clean off. */
.page-event-detail .event-hero{ position:relative; background:#f6f8fb; overflow:visible; padding:64px 0 270px; }
.page-event-detail .event-hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(0,38,77,.10) 1.5px,transparent 1.5px); background-size:24px 24px; pointer-events:none; }
/* Whatever renders right after this section (.event-tags-row if the event
   has tags, otherwise #eventBody directly) needs the matching clearance on
   its side of the boundary, for the same reason as the padding above — the
   ticket no longer contributes any height to the document flow once it's
   position:absolute, so nothing pushes this next element down on its own. */
.page-event-detail .event-hero + *{ margin-top:240px; }
.page-event-detail .event-hero-inner{ position:relative; z-index:2; max-width:680px; margin:0 auto; text-align:center; }
/* Sits between .big-ticket-wrap and #eventBody now (moved out of the hero,
   which reads cleaner with just the status pill + description) — its own
   top padding gives it breathing room from the ticket floating above it. */
.page-event-detail .event-tags-row{ display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-start; padding:8px 40px 32px; }
.page-event-detail .chip-opt{ font-size:11.5px; font-weight:700; color:#00264d; background:#e9eff6; border:1px solid #dbe5f0; padding:6px 13px; border-radius:999px; }
/* align-self:flex-start — this pill sits inside .ticket-body, a flex
   COLUMN (display:flex;flex-direction:column;), whose children stretch to
   its full cross-axis width by default. .page-events-browse's own copy of
   this same class already has this override; this one (used by the "More
   events like this" ticket carousel further down this same event-detail
   page) didn't, so its pill stretched edge-to-edge instead of hugging its
   own text like every other status pill on the site. */
.page-event-detail .status-pill{ align-self:flex-start; display:inline-flex; align-items:center; gap:7px; font-size:11px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; padding:7px 15px; border-radius:999px; margin-bottom:16px; }
/* Same "More events like this" carousel's .stub-cta ("View") had no
   matching rule anywhere scoped to .page-event-detail at all — it rendered
   as bare unstyled text, not even the old full-width pill the browse page
   had. Circular orange arrow icon, matching .ticket-stub-arrow everywhere
   else this ticket shape appears (index.html's live-events carousel) and
   .page-events-browse's own .stub-cta right below this. */
.page-event-detail .ticket-stub .stub-cta{
  position:relative; z-index:1; width:30px; height:30px; border-radius:50%; background:#ef7e1a; color:#fff;
  display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s ease;
}
.page-event-detail .ticket-stub .stub-cta svg{ width:14px; height:14px; }
.page-event-detail .ticket-stub .stub-cta:hover{ background:#ff9a45; }
.page-event-detail .status-pill.live{ background:#fef1e2; color:#ef7e1a; }
.page-event-detail .status-pill.upcoming{ background:#e9eff6; color:#00264d; }
.page-event-detail .status-pill.closed{ background:#f6f8fb; color:#5c6b80; border:1px solid #e2e8f0; }
.page-event-detail .status-pill .dot{
  position:static; width:6px; height:6px; border-radius:50%; border:none; background:#ef7e1a;
  
  animation: pulse 1.6s infinite;
}
.page-event-detail .event-hero-inner .sub{ font-size:15.5px; color:#5c6b80; max-width:52ch; line-height:1.65; margin:0 auto; }

/* ---------- the ticket (dark, standout centerpiece) ---------- */
/* position:absolute + bottom:0 + translateY(50%) instead of a negative
   margin-top — this now genuinely centers the card ON .event-hero's own
   bottom edge (its containing block, since .big-ticket-wrap is nested
   inside that section — see event_detail.html) regardless of the ticket's
   actual rendered height, rather than a fixed pixel guess that only
   happened to look right for one particular height. left/right:0 keeps it
   full-width so the inner .wrap can still center/cap the real content at
   1280px like every other section on this page. */
.page-event-detail .big-ticket-wrap{ position:absolute; left:0; right:0; bottom:0; transform:translateY(50%); z-index:5; }
.page-event-detail .big-ticket{
  width:100%; background:linear-gradient(150deg,#00264d,#004080 120%); border-radius:26px; box-shadow:0 30px 70px rgba(0,38,77,.35);
  display:flex; overflow:hidden; position:relative; animation:bigTicketFloat 4.5s ease-in-out infinite;
}
@keyframes bigTicketFloat{
  0%,100%{ transform:translateY(0); }
  50%{ transform:translateY(-8px); }
}
@media(prefers-reduced-motion:reduce){
  .page-event-detail .big-ticket{ animation:none; }
}
.page-event-detail .big-ticket::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.07) 1.4px,transparent 1.4px); background-size:22px 22px; pointer-events:none; }
.page-event-detail .bt-main{ flex:1; padding:44px 46px; min-width:0; position:relative; z-index:1; }
.page-event-detail .bt-eyebrow{ font-size:11px; font-weight:800; letter-spacing:.14em; text-transform:uppercase; color:#ff9a45; margin-bottom:10px; }
.page-event-detail .bt-main h1{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(26px,3.4vw,38px); color:#fff; line-height:1.12; margin-bottom:22px; }
.page-event-detail .bt-facts{ display:flex; flex-wrap:wrap; gap:22px 36px; }
.page-event-detail .bt-fact{ display:flex; align-items:center; gap:12px; }
.page-event-detail .bt-fact-icon{ width:38px; height:38px; border-radius:10px; background:rgba(255,255,255,.1); color:#ff9a45; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.page-event-detail .bt-fact-icon svg{ width:17px; height:17px; }
.page-event-detail .bt-fact-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:rgba(255,255,255,.5); }
.page-event-detail .bt-fact-value{ display:block; font-size:14.5px; font-weight:700; color:#fff; margin-top:2px; }
.page-event-detail .bt-divider{ height:1px; background:rgba(255,255,255,.14); margin:26px 0 22px; }
.page-event-detail .bt-campus{ display:flex; align-items:center; gap:14px; }
.page-event-detail .bt-campus-icon{ width:44px; height:44px; border-radius:50%; background:rgba(239,126,26,.15); color:#ff9a45; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.page-event-detail .bt-campus-icon svg{ width:20px; height:20px; }
.page-event-detail .bt-campus-label{ font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:rgba(255,255,255,.5); margin-bottom:2px; }
.page-event-detail .bt-campus-name{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:#fff; }
.page-event-detail .bt-campus-sub{ font-size:12.5px; color:rgba(255,255,255,.55); margin-top:2px; }

/* Lives right under the title, on the dark navy card — left-aligned like
   the rest of the card's content, light/translucent colours instead of a
   dark-on-light tone that would disappear here. No mockup equivalent
   (views/likes are additive real features), styled to match the card. */
.page-event-detail .ticket-hero-stats{ display:flex; align-items:center; justify-content:flex-start; gap:14px; margin:0 0 16px; font-size:13px; font-weight:600; color:rgba(255,255,255,.65); }
.page-event-detail .ticket-hero-stat{ display:inline-flex; align-items:center; gap:6px; }
.page-event-detail .ticket-hero-stat svg{ width:15px; height:15px; flex-shrink:0; }
.page-event-detail .ticket-hero-like{ display:inline-flex; align-items:center; gap:7px; padding:7px 16px; border-radius:999px; background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.16); color:#fff; font-size:13.5px; font-weight:700; transition:background .2s ease, border-color .2s ease, color .2s ease; }
.page-event-detail .ticket-hero-like svg{ width:15px; height:15px; flex-shrink:0; transition:fill .2s ease; }
.page-event-detail .ticket-hero-like:hover{ background:rgba(255,255,255,.16); }
/* Liked state — a bold solid-red pill (not just a red icon/count on the
   same translucent background) so "you already liked this" reads at a
   glance instead of needing a close look at the heart's fill colour.
   Higher specificity than the generic [data-like-btn].is-liked (public.css,
   "like buttons" section) so it wins over that rule's plain colour tint. */
.page-event-detail .ticket-hero-like.is-liked{ background:var(--danger); border-color:var(--danger); color:#fff; }
.page-event-detail .ticket-hero-like.is-liked svg{ fill:#fff; stroke:#fff; }

/* A fixed, generous width rather than a computed 1:1 square — a true
   dynamic square (sized off this row's own content-driven height) turned
   out to be unreliable with .bt-main's flex:1 sibling: both flex and grid
   track-sizing compute this item's cross size before the row's final
   height is known (which itself depends on how much width .bt-main gets,
   i.e. how much this item takes), so the browser derives a square from a
   stale, too-small estimate and the mismatch gets clipped by this card's
   own overflow:hidden. 380px reads as roughly square against this card's
   typical rendered height while sidestepping that circularity entirely. */
.page-event-detail .bt-stub{ width:380px; flex-shrink:0; background:rgba(0,0,0,.16); color:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:16px; padding:34px 22px; position:relative; z-index:1; overflow:hidden; }
.page-event-detail .bt-stub::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:0; border-left:2px dashed rgba(255,255,255,.28); }
.page-event-detail .bt-stub.has-image{ padding:0; background:none; }
.page-event-detail .bt-stub.has-image::before{ border-left-color:rgba(255,255,255,.5); }
.page-event-detail .bt-featured-img{ width:100%; height:100%; object-fit:cover; display:block; }
.page-event-detail .bt-notch{ position:absolute; left:-14px; width:28px; height:28px; border-radius:50%; background:#f6f8fb; z-index:2; }
.page-event-detail .bt-notch.top{ top:-14px; }
.page-event-detail .bt-notch.bottom{ bottom:-14px; }
.page-event-detail .bt-stub .admit{ font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:rgba(255,255,255,.5); font-weight:700; }
.page-event-detail .bt-qr{ display:grid; grid-template-columns:repeat(7,5px); grid-template-rows:repeat(7,5px); gap:2px; background:#fff; padding:8px; border-radius:8px; }
.page-event-detail .bt-qr i{ background:#00264d; border-radius:1px; }
.page-event-detail .bt-qr i.o{ background:transparent; }
.page-event-detail .bt-stub .dt{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; text-align:center; line-height:1.3; }
.page-event-detail .bt-stub .btn{ width:100%; }
.page-event-detail .bt-stub .cal-link{ font-size:11.5px; font-weight:700; color:rgba(255,255,255,.6); display:inline-flex; align-items:center; gap:5px; }
.page-event-detail .bt-stub .cal-link:hover{ color:#fff; }
.page-event-detail .bt-stub .cal-link svg{ width:13px; height:13px; }

@media(max-width:900px){
  .page-event-detail .big-ticket{ flex-direction:column; }
  .page-event-detail .bt-stub{ width:100%; padding:26px 22px 30px; }
  .page-event-detail .bt-stub.has-image{ aspect-ratio:16/9; padding:0; }
  .page-event-detail .bt-stub::before{ left:0; right:0; top:0; bottom:auto; width:auto; height:0; border-left:none; border-top:2px dashed rgba(255,255,255,.28); }
  .page-event-detail .bt-stub.has-image::before{ border-top-color:rgba(255,255,255,.5); }
  .page-event-detail .bt-notch{ left:50%; transform:translateX(-50%); }
  /* .bt-stub stacking below .bt-main (instead of beside it) roughly doubles
     the ticket's rendered height at this width — since .big-ticket-wrap
     still straddles .event-hero's bottom edge at exactly half its own
     height (position:absolute + translateY(50%)), the desktop clearance
     above (240px, sized for the side-by-side height) is no longer enough:
     the ticket's top half reaches up past it and covers the status pill/
     description/tags/About section instead of just empty padding. */
  .page-event-detail .event-hero{ padding-bottom:420px; }
  .page-event-detail .event-hero + *{ margin-top:420px; }
}
@media(max-width:620px){
  .page-event-detail .bt-main{ padding:30px 24px; }
  .page-event-detail .wrap{ padding:0 8px; }
  /* A bit more than the 900px breakpoint's 420px — narrower phones wrap the
     title/facts/campus block onto more lines, growing the (already
     column-stacked) ticket taller still. */
  .page-event-detail .event-hero{ padding-bottom:460px; }
  .page-event-detail .event-hero + *{ margin-top:480px; }
}

.page-event-detail .section-head{ display:flex; justify-content:space-between; align-items:flex-end; gap:24px; margin-bottom:36px; flex-wrap:wrap; }
.page-event-detail .section-head h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(24px,2.8vw,32px); margin-top:10px; color:#00264d; line-height:1.15; }
.page-event-detail section.pale{ background:#f6f8fb; }

.btn-ticket-outline{ background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.28); color:#fff; }
.btn-ticket-outline:hover{ background:rgba(255,255,255,.16); }
.ticket-share-icons{ display:flex; gap:10px; }
.ticket-share-icons a{
  width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.18); color:#fff; transition:all .2s ease;
}
.ticket-share-icons a:hover{ background:var(--warning); border-color:var(--warning); }
/* .ticket-share-icons's default look (translucent white on white/near-
   white border) is meant for a dark background (the old ticket-card-
   actions column) — on a plain white .event-sidebar-card it renders as
   white-on-white, effectively invisible except on hover. .on-surface
   swaps in a light-card-appropriate background/border/icon colour instead. */
.ticket-share-icons.on-surface a{ background:var(--surface-2); border-color:var(--border); color:var(--muted); }

.page-event-detail .rich-text{ font-size:15px; line-height:1.75; color:#5c6b80; }
.page-event-detail .rich-text img{ max-width:100%; border-radius:12px; }

/* Venue content block — Venue has no image field, so this is text-only
   (name/campus/address/capacity), unlike the reference mockup's photo card.
   Plain div, not an <a> (same reasoning as .guest-card above — it has no
   nested links of its own here, but keeping the same click-through pattern
   site-wide) — click-through to the venue's own detail page is wired via
   data-href + initTicketGridClicks("eventVenueBlock", ".venue-card") in
   public.js. */
.page-event-detail .venue-card{
  border:1px solid #e2e8f0; border-radius:16px; padding:0; display:flex; align-items:stretch; overflow:hidden;
  min-height:150px; cursor:pointer; transition:box-shadow .2s ease, border-color .2s ease;
}
.page-event-detail .venue-card:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); border-color:#cbd6e3; }
.page-event-detail .venue-card-text{ flex:1; padding:24px 26px; display:flex; flex-direction:column; justify-content:center; min-width:0; }
.page-event-detail .venue-name{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:#00264d; margin-bottom:5px; }
.page-event-detail .venue-detail{ font-size:13px; color:#5c6b80; line-height:1.55; }

/* --------------------- event body: content + sidebar ------------------ */
.page-event-detail .event-body-layout{ display:grid; grid-template-columns:1fr 340px; gap:56px; align-items:start; padding-top:8px; }
.page-event-detail .event-content-block{ margin-bottom:52px; }
.page-event-detail .event-content-block h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:21px; color:#00264d; margin-bottom:16px; }
@media(max-width:900px){ .page-event-detail .event-body-layout{ grid-template-columns:1fr; } }

/* auto-fit (not auto-fill) — auto-fill reserves a track for every column
   that WOULD fit the container width even when there's no item to put in
   it, so a gallery with only 1-2 photos left them stranded in a narrow
   220px column with dead empty space beside them instead of filling the
   section's actual full width. auto-fit collapses those empty tracks and
   lets the real items' 1fr basis claim the freed-up space. */
.event-gallery-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:14px; }
.event-gallery-item{ position:relative; padding:0; border:none; background:none; cursor:pointer; border-radius:14px; overflow:hidden; box-shadow:var(--shadow); transition:transform .2s ease, box-shadow .2s ease; }
.event-gallery-item:hover{ transform:translateY(-3px) scale(1.02); box-shadow:var(--shadow-lg); }
/* height:auto (not a fixed 220px + object-fit:cover) — the crop was
   cutting the top/bottom off wide photos. The full-black/fixed-height
   letterbox treatment belongs to the POPUP lightbox only (.photo-lightbox-*
   above); this on-page thumbnail just shows the whole photo at its own
   natural aspect ratio, scaled to the section's full width. */
.event-gallery-item img{ width:100%; height:auto; display:block; }
/* Same expand/fullscreen glyph the popup itself uses, sitting right on
   the thumbnail — signals "opens full-size" before the click happens,
   not only after. */
.event-gallery-expand{
  position:absolute; bottom:10px; right:10px; width:32px; height:32px; border-radius:50%;
  background:rgba(0,0,0,.5); color:#fff; display:flex; align-items:center; justify-content:center;
  backdrop-filter:blur(2px); transition:background .15s ease;
}
.event-gallery-item:hover .event-gallery-expand{ background:rgba(0,0,0,.7); }
.event-gallery-expand svg{ width:15px; height:15px; }

/* ------------------------------- gallery lightbox ------------------------------ */
/* A dedicated full-viewport photo viewer, not the standard card-in-a-
   dimmed-backdrop `.modal` — still rides on `.modal-backdrop` for
   show/hide, backdrop-click-to-close and the re-parent-to-<body> fix
   (see initModals() in scripts.js), but every visual is its own, laid
   out as direct fixed-position children of the backdrop (arrows pinned
   to the viewport edges, toolbar+filmstrip pinned to the bottom) rather
   than packed into one centered card. */
.photo-lightbox.modal-backdrop{
  background:#000; backdrop-filter:none; padding:0;
  display:none; align-items:stretch; justify-content:stretch;
}
.photo-lightbox.show{ display:block; }

.photo-lightbox-icon-btn{
  position:fixed; top:18px; z-index:3; width:44px; height:44px; border-radius:50%; border:none;
  background:rgba(255,255,255,.08); color:#fff; display:flex; align-items:center; justify-content:center;
  cursor:pointer; transition:background .15s ease;
}
.photo-lightbox-icon-btn:hover{ background:rgba(255,255,255,.18); }
.photo-lightbox-close{ right:18px; }
.photo-lightbox-fullscreen{ right:72px; }

.photo-lightbox-arrow{
  position:fixed; top:50%; transform:translateY(-50%); z-index:3; width:52px; height:52px; border-radius:50%;
  border:none; background:rgba(255,255,255,.06); color:rgba(255,255,255,.85); display:flex; align-items:center;
  justify-content:center; cursor:pointer; transition:background .15s ease, color .15s ease;
}
.photo-lightbox-arrow:hover{ background:rgba(255,255,255,.16); color:#fff; }
.photo-lightbox-arrow svg{ width:24px; height:24px; }
.photo-lightbox-prev{ left:20px; }
.photo-lightbox-next{ right:20px; }

/* Bottom clearance (128px) reserves room for the toolbar+filmstrip below
   so a tall image is never hidden behind them — stage centres the image
   within whatever's left above that. Pure #000 (matching the backdrop)
   fills every bit of that space the image itself doesn't reach — the
   image is height:100% (not max-height), so height is always the
   binding constraint and width follows the image's own aspect ratio;
   max-width:100% only kicks in to cap width for a wide/landscape photo,
   which is what letterboxes it (black bars top/bottom) instead of
   overflowing sideways. A portrait photo instead fills the full height
   and leaves black bars left/right, matching a real photo-viewer rather
   than "shrink to fit both dimensions" cropping/padding oddly. */
.photo-lightbox-stage{
  position:absolute; inset:0 0 128px 0; display:flex; align-items:center; justify-content:center;
  overflow:auto; padding:32px 24px; background:#000;
}
.photo-lightbox-img{
  height:100%; width:auto; max-width:100%; display:block; margin:auto; object-fit:contain;
  transition:transform .2s ease; transform-origin:center center; cursor:zoom-in;
}
/* Zoom applies a numeric transform:scale (tracked in JS, shown as the %
   readout) rather than a binary toggle — .photo-lightbox-stage's own
   overflow:auto is what lets the oversized image be panned once it no
   longer fits, instead of clipping at the viewport edge. */
.photo-lightbox-stage.is-zoomed{ cursor:grab; }
.photo-lightbox-stage.is-zoomed .photo-lightbox-img{ max-width:none; max-height:none; cursor:zoom-out; }

.photo-lightbox-bottom{
  position:fixed; left:0; right:0; bottom:0; z-index:3; display:flex; flex-direction:column;
  align-items:center; gap:12px; padding:14px 16px 18px; background:linear-gradient(0deg, rgba(0,0,0,.55), transparent);
}
.photo-lightbox-toolbar{
  display:flex; align-items:center; gap:10px; background:rgba(255,255,255,.1); border-radius:999px; padding:8px 14px;
  backdrop-filter:blur(6px);
}
.photo-lightbox-counter{ font-size:12.5px; font-weight:700; color:rgba(255,255,255,.85); min-width:38px; text-align:center; }
.photo-lightbox-divider{ width:1px; height:16px; background:rgba(255,255,255,.2); }
.photo-lightbox-tool{
  width:28px; height:28px; border-radius:50%; border:none; background:transparent; color:#fff;
  display:flex; align-items:center; justify-content:center; cursor:pointer; transition:background .15s ease;
}
.photo-lightbox-tool:hover{ background:rgba(255,255,255,.18); }
.photo-lightbox-tool:disabled{ opacity:.35; cursor:default; }
.photo-lightbox-tool:disabled:hover{ background:transparent; }
.photo-lightbox-zoom-pct{ font-size:12px; font-weight:700; color:rgba(255,255,255,.85); min-width:38px; text-align:center; }

.photo-lightbox-filmstrip{
  display:flex; gap:8px; max-width:100%; overflow-x:auto; padding:2px;
  -webkit-mask-image:linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
  mask-image:linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
}
.photo-lightbox-thumb{
  flex-shrink:0; width:56px; height:56px; padding:0; border-radius:8px; border:2px solid transparent;
  background:none; cursor:pointer; overflow:hidden; opacity:.55; transition:opacity .15s ease, border-color .15s ease;
}
.photo-lightbox-thumb:hover{ opacity:.85; }
.photo-lightbox-thumb.is-active{ opacity:1; border-color:var(--accent, var(--primary)); }
.photo-lightbox-thumb img{ width:100%; height:100%; object-fit:cover; display:block; }

@media (max-width:640px){
  .photo-lightbox-arrow{ width:40px; height:40px; }
  .photo-lightbox-arrow svg{ width:19px; height:19px; }
  .photo-lightbox-prev{ left:8px; }
  .photo-lightbox-next{ right:8px; }
  .photo-lightbox-stage{ padding:60px 50px; inset:0 0 112px 0; }
  .photo-lightbox-thumb{ width:44px; height:44px; }
}

.page-event-detail .event-body-layout > aside{ display:flex; flex-direction:column; gap:0; }
.page-event-detail .event-sidebar-card{ border:1px solid #e2e8f0; border-radius:18px; padding:26px; margin-bottom:20px; background:#fff; }
.page-event-detail .event-sidebar-card h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:#00264d; margin-bottom:16px; }
.page-event-detail .sidebar-actions{ display:flex; flex-direction:column; gap:10px; margin-bottom:18px; }
.page-event-detail .sidebar-fact{ padding:14px 0; border-top:1px solid #e2e8f0; }
.page-event-detail .sidebar-fact:first-of-type{ border-top:none; padding-top:0; }
.page-event-detail .sidebar-fact-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:#5c6b80; margin-bottom:6px; }
.page-event-detail .chip-opt-dark{ display:inline-block; font-size:12.5px; font-weight:700; color:#00264d; background:#e9eff6; padding:5px 12px; border-radius:999px; margin:0 6px 6px 0; }
.page-event-detail .sidebar-contact-name{ font-size:13.5px; font-weight:700; color:#0a1626; margin-bottom:8px; }
.page-event-detail .sidebar-contact-link{ display:flex; align-items:center; gap:8px; font-size:13px; color:#004080; font-weight:600; margin-bottom:8px; }
.page-event-detail .sidebar-contact-link:last-child{ margin-bottom:0; }
.page-event-detail .sidebar-contact-link svg{ width:14px; height:14px; flex-shrink:0; }
/* Multiple organizers/contacts on one event — each gets its own block,
   separated by a divider rather than running together unlabelled. */
.page-event-detail .sidebar-contact-block{ margin-bottom:14px; }
.page-event-detail .sidebar-contact-block:last-child{ margin-bottom:0; }
.page-event-detail .sidebar-contact-block-divided{ padding-top:14px; border-top:1px solid #e2e8f0; }
.page-event-detail .sidebar-tags{ display:flex; flex-wrap:wrap; gap:8px; }
.page-event-detail .sidebar-tags a{ font-size:12px; font-weight:700; color:#ef7e1a; background:#fff; border:1px solid #cbd6e3; padding:6px 12px; border-radius:999px; }
.page-event-detail .sidebar-tags a:hover{ background:#ef7e1a; color:#fff; border-color:#ef7e1a; }
.page-event-detail .sidebar-share-icons{ display:flex; gap:8px; }
.page-event-detail .sidebar-share-icons a{ width:32px; height:32px; border-radius:50%; background:#f6f8fb; border:1px solid #e2e8f0; display:flex; align-items:center; justify-content:center; color:#5c6b80; transition:.18s; }
.page-event-detail .sidebar-share-icons a:hover{ background:#00264d; border-color:#00264d; color:#fff; }
.page-event-detail .sidebar-share-icons svg{ width:14px; height:14px; }

/* Sticky mini action bar — hidden until initEventStickyBar (public.js)
   detects the ticket card has scrolled out of view, so booking stays one
   tap away no matter how far down the page you've scrolled. */
.event-sticky-bar{
  position:fixed; left:0; right:0; z-index:39; background:var(--surface); border-bottom:1px solid var(--border);
  box-shadow:var(--shadow); transform:translateY(-100%); opacity:0; transition:transform .25s ease, opacity .25s ease;
}
.event-sticky-bar.show{ transform:translateY(0); opacity:1; }
.event-sticky-bar-inner{ max-width:1280px; margin:0 auto; padding:12px 40px; display:flex; align-items:center; justify-content:space-between; gap:16px; }
.event-sticky-title{ font-size:14px; font-weight:700; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

@media (max-width:980px){
  .event-body-layout{ grid-template-columns:1fr; }
  .event-sidebar{ position:static; }
}

/* ---------------------------------- pagination ------------------------------ */
.pagination{ display:flex; align-items:center; justify-content:center; gap:16px; margin-top:34px; }
.page-info{ font-size:12.5px; color:var(--muted); font-weight:600; }

/* --------------------------- generic light page head ----------------------- */
.public-page-head{ background:var(--surface-2); border-bottom:1px solid var(--border); padding:44px 0; }
.public-page-head-inner{ max-width:1280px; margin:0 auto; padding:0 40px; }
.public-page-eyebrow{
  display:inline-flex; align-items:center; gap:9px;
  font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:var(--primary-text);
}
.public-page-eyebrow::before{
  content:""; width:7px; height:7px; border-radius:50%; background:var(--accent, var(--primary-text)); flex-shrink:0;
}
.public-page-head-inner h1{ font-family:'Playfair Display',serif; font-size:30px; font-weight:800; margin:8px 0 6px; }
.public-page-head-inner p{ color:var(--muted); margin:0; }

/* ------------------------------- public calendar ---------------------------- */
/* Reproduces the calendar.html mockup's own CSS verbatim, scoped under
   .page-calendar so its .hero/.hero-inner/.eyebrow/.btn (single-column,
   centered — a different shape than the homepage's two-column .hero) never
   collide with the identically-named homepage classes. The .cal-*/.up-*/
   .live-feature*/.day-panel* rules below stay unscoped (page-specific
   names, used nowhere else) and were already renamed in place to the
   mockup's own class names (.today/.selected/.outside/.mandatory/
   .festival/.other) rather than duplicated under the scope — server-
   rendered from app.views.public_calendar, which merges Events +
   AcademicCalendarItem per day and now also takes a `mode` param
   ('event'/'holiday') deciding which of the two shows in the grid/day
   panel — see .cal-mode-toggle below (replaces the old 5-way legend/
   layer-toggle chip row). Every month-nav/day-select/mode-toggle click
   re-fetches templates/public/_calendar_body.html in the background and
   swaps #calendarApp's contents in place (calendar.html's extra_js) rather
   than a full page reload, same fetch+pushState convention as the events-
   browse filter form. */
.page-calendar{ font-family:'Noto Sans',-apple-system,sans-serif; color:#0a1626; }
.page-calendar .wrap{ max-width:1280px; margin:0 auto; padding:0 40px; }
.page-calendar section{ padding:64px 0; }

.page-calendar .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-calendar .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }
.page-calendar .eyebrow.center{ justify-content:center; }
.page-calendar .eyebrow.on-dark{ color:#ff9a45; }

.page-calendar .btn{ display:inline-flex; align-items:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.page-calendar .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-calendar .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
.page-calendar .btn-sm{ padding:9px 18px; font-size:13px; }

.page-calendar .hero{ position:relative; background:#00264d; overflow:hidden; padding:64px 0 88px; }
.page-calendar .hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.18) 1.6px,transparent 1.6px); background-size:24px 24px; opacity:1; }
.page-calendar .hero::after{ content:""; position:absolute; top:-180px; left:50%; transform:translateX(-50%); width:640px; height:640px; border-radius:50%; background:radial-gradient(circle,rgba(239,126,26,.30),transparent 70%); pointer-events:none; }
.page-calendar .hero-inner{ display:block; position:relative; z-index:2; max-width:820px; margin:0 auto; text-align:center; }
.page-calendar h1.hero-title{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(30px,4vw,44px); line-height:1.1; letter-spacing:-.01em; color:#fff; margin:16px 0 14px; }
.page-calendar h1.hero-title em{ color:#ff9a45; font-style:italic; }
.page-calendar .hero-sub{ font-size:15.5px; line-height:1.65; color:rgba(255,255,255,.78); max-width:52ch; margin:0 auto; }

/* Floats up into .page-hero's own bottom padding via negative margin, same
   overlap mechanic as .ticket-card/.guest-badge elsewhere on the site. */
.cal-toolbar{
  display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap;
  background:var(--surface); border:1px solid var(--border); border-radius:18px; box-shadow:var(--shadow-lg);
  padding:20px 28px; margin-top:-58px; position:relative; z-index:6;
}
.cal-nav{ display:flex; align-items:center; gap:14px; }
.cal-arrow{ width:38px; height:38px; border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; color:var(--text); transition:background .2s ease, border-color .2s ease, color .2s ease; flex-shrink:0; }
.cal-arrow:hover{ background:var(--primary); border-color:var(--primary); color:#fff; }
.cal-month{ font-family:'Playfair Display',serif; font-weight:700; font-size:21px; color:var(--text); min-width:190px; text-align:center; }
.cal-today-btn{ font-size:12.5px; font-weight:700; color:var(--text); border:1px solid var(--border); border-radius:999px; padding:9px 18px; flex-shrink:0; }
.cal-today-btn:hover{ background:var(--primary-100); }

.cal-shell{ background:var(--surface); border:1px solid var(--border); border-radius:20px; box-shadow:var(--shadow-md); overflow:hidden; margin-top:18px; position:relative; z-index:5; }
/* Replaces the old 5-way legend/layer-toggle chip row — a single
   mutually-exclusive Event/Holiday mode switch instead, driving which of
   Event/AcademicCalendarItem data the grid + day panel below render (see
   app.views.public_calendar's `mode` param). Literal hex, same reasoning
   as the header's mega-menu/search-trigger elsewhere in this file. */
.cal-mode-toggle{ display:flex; gap:4px; padding:18px 26px; border-bottom:1px solid #e2e8f0; background:#fff; }
.cal-mode-btn{
  padding:10px 20px; border-radius:999px; border:1px solid #e2e8f0; background:#f6f8fb; color:#5c6b80;
  font-family:inherit; font-size:13px; font-weight:700; cursor:pointer; transition:background .18s ease, color .18s ease, border-color .18s ease;
}
.cal-mode-btn:hover{ border-color:#ef7e1a; color:#0a1626; }
.cal-mode-btn.active{ background:#00264d; border-color:#00264d; color:#fff; }
.cal-mode-btn.active:hover{ color:#fff; }

.cal-weekdays{ display:grid; grid-template-columns:repeat(7,1fr); border-bottom:1px solid var(--border); background:var(--surface); }
.cal-weekdays span{ padding:12px 8px; text-align:center; font-size:11px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }

.cal-grid{ display:grid; grid-template-columns:repeat(7,1fr); }
.cal-cell{
  min-height:120px; min-width:0; border-right:1px solid var(--border); border-bottom:1px solid var(--border); padding:10px 8px;
  display:flex; flex-direction:column; gap:5px; position:relative; color:inherit; transition:background .15s ease;
}
.cal-cell:nth-child(7n){ border-right:none; }
.cal-cell.outside{ background:var(--bg); }
a.cal-cell.has-events{ cursor:pointer; }
a.cal-cell.has-events:hover{ background:var(--accent-100, var(--primary-100)); }
.cal-cell.selected{ background:var(--accent-100, var(--primary-100)); box-shadow:inset 0 0 0 2px var(--accent, var(--primary)); }
.cal-date{ font-family:'Playfair Display',serif; font-weight:700; font-size:14.5px; color:var(--text); }
.cal-cell.today .cal-date{
  display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; border-radius:50%;
  background:var(--accent, var(--primary-text)); color:#fff;
}
/* A small pulsing dot in the cell's corner, same idea as the homepage's
   live badge — flags "something live is happening today" at a glance
   before the visitor even opens the day panel. */
.cal-cell.has-live::after{ content:""; position:absolute; top:8px; right:8px; width:7px; height:7px; border-radius:50%; background:var(--accent, var(--primary-text)); animation:calLivePulse 1.6s ease-in-out infinite; }
@keyframes calLivePulse{ 0%,100%{ box-shadow:0 0 0 0 rgba(239,126,26,.55); } 50%{ box-shadow:0 0 0 6px rgba(239,126,26,0); } }
/* white-space:normal + overflow-wrap (not the mockup's nowrap/ellipsis) —
   long real event titles were overflowing straight past the cell's own
   column into the next day's cell instead of clipping, because a flex/grid
   item's default min-width:auto lets unbreakable nowrap text force the
   item wider than its column before overflow:hidden ever gets to act.
   Wrapping onto additional lines (the cell's own min-height, not a fixed
   height, so it grows to fit) keeps every title fully inside its own day
   instead of bleeding into the next one. */
.cal-pill{ display:block; font-size:10.5px; font-weight:700; padding:3px 6px; border-radius:5px; line-height:1.3; white-space:normal; overflow-wrap:break-word; word-break:break-word; color:#fff; }
/* Distinct colour per status so a glance at the grid tells live/upcoming/
   past apart: live is the brand orange (matches the "College Events"
   legend chip and the live-dot glow used everywhere else on the site),
   upcoming is navy (reads as "on the calendar, not happening yet"), past
   is a pale blue chip with navy text rather than a dimmed orange — a
   washed-out orange still reads as "orange" at a glance, this doesn't. */
.cal-pill.live{ background:#ef7e1a; color:#fff; }
.cal-pill.upcoming{ background:#00264d; color:#fff; }
.cal-pill.closed{ background:#dbe5f0; color:#00264d; }
.cal-pill.mandatory{ background:var(--hol-mandatory); }
.cal-pill.festival{ background:var(--hol-festival); }
.cal-pill.exam{ background:var(--acad-exam); }
.cal-pill.other{ background:var(--acad-other); }
.cal-more{ font-size:10.5px; font-weight:700; color:var(--text); }

@media (max-width:760px){
  .cal-cell{ min-height:78px; padding:6px 5px; }
  .cal-pill{ font-size:0; padding:0; width:6px; height:6px; border-radius:50%; }
  .cal-more{ font-size:9px; }
  .cal-weekdays span{ font-size:9.5px; padding:8px 2px; }
  .cal-toolbar{ margin-top:-30px; padding:16px 18px; }
  .cal-shell{ margin-top:14px; }
  .cal-mode-toggle{ padding:14px 16px; }
}

/* ------------------------------ day agenda panel ---------------------------- */
.day-panel{ margin-top:56px; }
.day-panel-head{ display:flex; align-items:baseline; gap:10px; margin-bottom:24px; flex-wrap:wrap; }
.day-panel-head h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:22px; color:var(--text); }
.day-panel-head span{ font-size:13px; color:var(--muted); }
.day-panel-empty{ border:1px dashed var(--border); border-radius:18px; padding:44px 20px; text-align:center; color:var(--muted); font-size:14px; }

.upcoming-list{ display:flex; flex-direction:column; border:1px solid var(--border); border-radius:18px; overflow:hidden; background:var(--surface); }
.up-row{ display:flex; align-items:center; gap:22px; padding:20px 26px; border-bottom:1px solid var(--border); transition:background .2s ease; }
.up-row:last-child{ border-bottom:none; }
.up-row:hover{ background:var(--bg); }
.up-row.academic{ border-left:4px solid var(--cat-color, var(--primary-600)); }
.up-date{ width:64px; flex-shrink:0; text-align:center; border-right:1px dashed var(--border); padding-right:18px; }
.up-date .d{ font-family:'Playfair Display',serif; font-weight:700; font-size:24px; color:var(--text); line-height:1; }
.up-date .m{ font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--accent, var(--primary-text)); margin-top:4px; }
.up-main{ flex:1; min-width:0; }
.up-main h4{ font-family:'Playfair Display',serif; font-weight:700; font-size:16.5px; color:var(--text); margin-bottom:4px; }
.up-main p{ font-size:12.5px; color:var(--muted); }
.up-status{
  display:inline-flex; align-items:center; gap:6px; flex-shrink:0; font-size:10.5px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase; padding:5px 10px; border-radius:999px;
}
/* Same soft/pale treatment as .event-status-pill everywhere else on the
   site — this row's status badge used to be its own separate red/orange
   colour scheme instead of matching. */
.up-status.live{ background:var(--accent-100, var(--primary-100)); color:var(--accent, var(--primary-text)); }
.up-status.live::before{
  content:""; width:6px; height:6px; border-radius:50%; background:currentColor; flex-shrink:0;
  
  animation: pulse 1.6s infinite;
}
.up-status.upcoming{ background:var(--primary-100); color:var(--upcoming-text, var(--primary)); }
.up-status.closed{ background:var(--bg); color:var(--muted); border:1px solid var(--border); }
.up-status.mandatory{ background:var(--hol-mandatory-pale); color:var(--hol-mandatory); }
.up-status.festival{ background:var(--hol-festival-pale); color:var(--hol-festival); }
.up-status.exam{ background:var(--acad-exam-pale); color:var(--acad-exam); }
.up-status.other{ background:var(--acad-other-pale); color:var(--acad-other); }
.up-tags{ display:flex; gap:6px; flex-shrink:0; }
/* Navy text on the pale-blue pill (matching .up-status.upcoming right
   above and the mockup's own #Sports/#Cultural tags) — was accidentally
   using --primary-text (the site's orange), which read wrong next to the
   grey "Past"/pale-blue "Upcoming" status pill beside it. */
.up-tags span{ font-size:10.5px; font-weight:700; color:var(--upcoming-text, var(--primary)); background:var(--primary-100); padding:3px 9px; border-radius:999px; white-space:nowrap; }
.up-arrow{ width:34px; height:34px; border-radius:50%; border:1px solid var(--border); display:flex; align-items:center; justify-content:center; color:var(--text); flex-shrink:0; }
.up-row:hover .up-arrow{ background:var(--accent, var(--primary)); border-color:var(--accent, var(--primary)); color:#fff; }

/* Featured live event — same idea as the homepage's live tickets, just a
   single wide banner since the day panel only ever shows one day at a
   time. */
.live-feature{
  position:relative; display:flex; align-items:center; gap:26px; padding:26px 30px; border-radius:18px; margin-bottom:18px;
  background:linear-gradient(135deg, var(--primary) 0%, var(--primary-600) 100%); box-shadow:0 16px 40px rgba(0,38,77,.22); overflow:hidden;
}
.live-feature::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.12) 1.4px, transparent 1.4px); background-size:20px 20px; pointer-events:none; }
.live-feature-pulse{
  position:relative; z-index:1; flex-shrink:0; display:flex; align-items:center; gap:8px; background:rgba(239,126,26,.18); border:1px solid rgba(255,154,69,.55);
  color:#ff9a45; font-size:11px; font-weight:800; letter-spacing:.08em; text-transform:uppercase; padding:7px 13px; border-radius:999px;
}
.live-feature-main{ position:relative; z-index:1; flex:1; min-width:0; }
.live-feature-main h4{ font-family:'Playfair Display',serif; font-weight:700; font-size:20px; color:#fff; margin-bottom:6px; }
.live-feature-main p{ font-size:13px; color:rgba(255,255,255,.78); }
.live-feature-tags{ position:relative; z-index:1; display:flex; gap:6px; flex-shrink:0; flex-wrap:wrap; }
.live-feature-tags span{ font-size:10.5px; font-weight:700; color:#fff; background:rgba(255,255,255,.14); padding:4px 10px; border-radius:999px; white-space:nowrap; }
.live-feature .btn{ position:relative; z-index:1; flex-shrink:0; }
@media (max-width:620px){ .live-feature{ flex-wrap:wrap; padding:22px 20px; } .live-feature-tags{ order:3; width:100%; } }

@media (max-width:980px){
  .hero-inner{ grid-template-columns:1fr; }
  .ticket-card{ grid-template-columns:1fr; margin-top:-40px; }
  /* The seam perforation is drawn for a left/right split — stacked on
     mobile it would just be a stray dashed line down the left edge, so it
     switches to a single top notch + top border instead of trying to
     redraw a two-notch vertical seam horizontally. */
  .ticket-card-actions{ border-left:none; border-top:2px dashed var(--border); padding:26px 26px 30px; }
  .ticket-card-actions::before{ left:50%; top:-15px; transform:translateX(-50%); }
  .ticket-card-actions::after{ display:none; }
}
@media (max-width:640px){
  .hero-copy h1{ font-size:32px; }
  .hero-stat{ padding-right:16px; }
  /* Horizontal padding is owned by the consistent-page-container rules
     further down (kept at the very end of this file, same reasoning as
     style.css's own "RESPONSIVE OVERRIDES" section — a rule here would
     otherwise be beaten by any later same-specificity base rule); this one
     only needs to keep its own vertical rhythm tightened on mobile. */
  .public-section{ padding-top:40px; padding-bottom:40px; }
}

/* =====================================================================
   Guests directory + guest detail (templates/public/guests.html,
   templates/public/guest_detail.html) — reuses the site's existing ticket/
   sidebar/section components wherever the shape already matches (VIP and
   related-guest carousels are literally .ticket-carousel/.ticket/.ticket-
   stub from the live-events row; the sidebar reuses .event-sidebar-card/
   .ticket-share-icons as-is), and only adds genuinely new pieces below:
   the hero ID badge, industry chips, the value grid, the directory cards,
   the detail page's own gold boarding-pass badge, and the visit-history
   timeline.
   ===================================================================== */

/* ----------------------- guest ID badge (hero visual) --------------------- */
.id-badge{ background:var(--surface); border-radius:18px; box-shadow:var(--shadow-lg); position:relative; transform:rotate(2.5deg); overflow:hidden; transition:transform .25s ease; }
.id-badge:hover{ transform:rotate(0deg) translateY(-4px); }
.id-lanyard{
  height:14px;
  background:repeating-linear-gradient(45deg, var(--accent, var(--primary-text)) 0 8px, color-mix(in srgb, var(--accent, var(--primary-text)) 55%, white) 8px 16px);
}
.id-punch{ position:absolute; top:14px; left:50%; transform:translateX(-50%); width:34px; height:34px; border-radius:50%; background:var(--primary); }
.id-body{ padding:26px 24px 22px; }
.id-top{ display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:18px; }
.id-org{ font-family:'Playfair Display',serif; font-weight:700; font-size:13px; color:var(--text); }
.id-org small{ display:block; font-family:'Noto Sans',sans-serif; font-size:9.5px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); margin-top:2px; }
.id-vip-tag{ background:var(--gold-pale); color:var(--gold); font-size:10px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; padding:5px 10px; border-radius:999px; display:flex; align-items:center; gap:5px; flex-shrink:0; }
.id-photo{
  width:76px; height:76px; border-radius:50%; background:var(--primary); color:#fff; overflow:hidden;
  display:flex; align-items:center; justify-content:center; font-family:'Playfair Display',serif; font-weight:700; font-size:24px;
  margin:0 auto 14px; border:3px solid var(--accent-100, var(--primary-100));
}
.id-photo img{ width:100%; height:100%; object-fit:cover; }
.id-name{ font-family:'Playfair Display',serif; font-weight:700; font-size:20px; color:var(--text); text-align:center; }
.id-role{ font-size:12.5px; color:var(--muted); text-align:center; margin-top:4px; line-height:1.5; }
.id-divider{ border-top:1px dashed var(--border); margin:18px 0 14px; }
.id-meta{ display:flex; justify-content:space-between; font-size:11px; color:var(--muted); }
.id-meta b{ display:block; color:var(--text); font-size:12.5px; font-family:'Playfair Display',serif; margin-top:2px; }
/* width:100% — .id-badge is a direct child of .hero-inner, a CSS grid
   (collapsed to a single column below 980px). A non-auto margin on a grid
   item opts it out of the default stretch-to-fill-the-column sizing (same
   bug as .mega-foot's own margin:0 auto elsewhere in this file), so without
   an explicit width this card was shrinking to its own content's intrinsic
   size — ~195px, not the intended ~340px — and centering that shrunk box
   instead. That's what was crushing .id-meta's "Campus"/"Sessions" row down
   to ~147px wide and running their values together with no gap at all.
   width:100% (capped by max-width, centered by the same margin:auto) fixes
   it the same way .mega-foot{width:100%} did. */
/* margin's 3rd value (48px bottom, not just "0 auto") — .hero-inner's own
   padding-bottom:64px (meant to give the stacked card breathing room below
   it before the section ends) gets silently zeroed out at <=620px: .wrap
   picks up a narrower side-padding override in its own later media query,
   same specificity as .hero-inner's rule, and a later same-specificity
   shorthand wins the padding-bottom tug-of-war even though .wrap never
   meant to touch vertical padding at all. Giving the card its own
   margin-bottom sidesteps that fight entirely instead of chasing
   specificity/order between two unrelated rules. */
@media (max-width:980px){ .id-badge{ transform:none; width:100%; max-width:340px; margin:0 auto 48px; } }

/* ------------------------------ industry chips ----------------------------- */
/* .page-guests-scoped — see the .home-tag-pill comment above for why this
   couldn't stay a plain unscoped .tag-pill (guests.html already has its own
   .page-guests wrapper div, so scoping this one needed no markup change). */
.tag-cloud{ display:flex; flex-wrap:wrap; gap:10px; }
.page-guests .tag-pill{
  padding:9px 16px; border-radius:999px; background:var(--surface); color:var(--text); font-weight:700; font-size:13px;
  border:1px solid var(--border); transition:border-color .15s ease, color .15s ease, background .15s ease;
}
.page-guests .tag-pill:hover{ border-color:var(--accent, var(--primary-text)); color:var(--accent, var(--primary-text)); }
.page-guests .tag-pill.active{ background:var(--primary); border-color:var(--primary); color:#fff; }
.page-guests .tag-pill .n, .page-guests .status-tab .n{ opacity:.65; font-weight:600; margin-left:4px; }

/* ------------------------------- value grid -------------------------------- */
.value-grid{ display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }
.value-card{ display:flex; align-items:center; gap:14px; background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:18px; transition:border-color .15s ease, box-shadow .15s ease; }
.value-card:hover{ border-color:var(--accent, var(--primary-text)); box-shadow:var(--shadow-sm); }
.value-badge{ width:44px; height:44px; border-radius:50%; background:var(--primary); color:#fff; font-family:'Playfair Display',serif; font-weight:700; font-size:16px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.value-card .name{ font-weight:700; font-size:13.5px; color:var(--text); }
.value-card .cnt{ font-size:11.5px; color:var(--muted); margin-top:2px; }
@media (max-width:950px){ .value-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:600px){ .value-grid{ grid-template-columns:1fr; } }

/* --------------------------- guest directory grid --------------------------- */
/* Full-directory cards are the exact same .ticket partial as the VIP/
   related-guests carousels (gold stub for VIP guests, navy for everyone
   else) — just laid out in a grid instead of a horizontally-scrolling row.
   .ticket's own flex-basis (sized for the scrolling rows) is inert here
   since flex-basis only applies to flex items, and a grid item, so it
   naturally fills its column's width instead. */
/* .status-tabs/.status-tab's shared definition (style.css) is the
   dashboard's iOS-style segmented control — one pale pill-shaped
   container, individual tabs flat/borderless, only the active one getting
   a small white chip. Public pages use their own look instead: each tab
   is its own bordered white pill, the active one solid navy — overridden
   here (not in style.css) since public.css only ever loads on pages
   extending public/base.html, so this can't leak onto the dashboard's own
   use of the same shared classes. */
.status-tabs{ display:flex; align-items:center; gap:10px; padding:0; background:transparent; border:none; flex-wrap:wrap; }
.status-tab{
  padding:9px 20px; border-radius:999px; font-size:13px; font-weight:700; color:var(--text);
  background:var(--surface); border:1px solid var(--border); display:flex; align-items:center; gap:6px; cursor:pointer;
  transition:background .15s ease, color .15s ease, border-color .15s ease;
}
.status-tab:hover{ border-color:var(--primary); }
.status-tab.active{ background:var(--primary); color:#fff; border-color:var(--primary); box-shadow:none; }
/* .status-tabs' own definition (above) already has no margin outside its
   box — any page using it sitting directly above a card grid (not nested
   inside a form/card that already carries its own margin-bottom, the way
   it started out inside events_list.html's old filter form) needs this
   modifier explicitly opted into via markup, rather than every use of the
   shared class getting extra margin whether it needs it or not. Also adds
   the divider line under the row separating it from the grid below. */
.status-tabs.standalone{ margin-bottom:28px; padding-bottom:20px; border-bottom:1px solid var(--border); }
/* Brief dim while the events-browse results swap in over fetch()
   (events_list.html's extra_js) so filter/tab/pagination clicks don't feel
   inert during the no-reload request. */
#eventsResultsWrap[aria-busy="true"]{ opacity:.5; pointer-events:none; transition:opacity .15s ease; }
/* Shared 3-column responsive card grid — .ticket cards laid out in a plain
   grid instead of a horizontally-scrolling row, used by both the guests
   directory and the events browse page. .ticket's own flex-basis (sized
   for the scrolling carousels) is inert here since flex-basis only applies
   to flex items, and a grid item, so it naturally fills its column's width
   instead. */
.ticket-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
@media (max-width:950px){ .ticket-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:600px){ .ticket-grid{ grid-template-columns:1fr; } }

/* --------------------------- guest detail hero ------------------------------ */
/* Reproduces the guest-detail.html mockup's own CSS verbatim, scoped under
   .page-guest-detail for the same reasons as .page-event-detail (see that
   block above) — a stacked, non-overlapping layout (back-bar > guest-hero >
   big-badge-wrap > body section), so no JS-measured overlap/#guestBody
   padding hack is needed any more. */
.page-guest-detail{ font-family:'Noto Sans',-apple-system,sans-serif; color:#0a1626; }
.page-guest-detail .wrap{ max-width:1280px; margin:0 auto; padding:0 40px; }
.page-guest-detail section{ padding:72px 0; }
.page-guest-detail section.pale{ background:#f6f8fb; }

.page-guest-detail .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-guest-detail .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }
.page-guest-detail .eyebrow.center{ justify-content:center; }
.page-guest-detail .btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.page-guest-detail .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-guest-detail .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
.page-guest-detail .btn-outline{ border:1.5px solid #cbd6e3; color:#00264d; }
.page-guest-detail .btn-outline:hover{ background:#e9eff6; }
.page-guest-detail .btn-outline-dark{ border:1.5px solid rgba(255,255,255,.5); color:#fff; }
.page-guest-detail .btn-outline-dark:hover{ background:rgba(255,255,255,.12); }
.page-guest-detail .btn-sm{ padding:9px 18px; font-size:13px; }
.page-guest-detail .btn-block{ width:100%; }

.page-guest-detail .back-bar{ background:#f6f8fb; border-bottom:1px solid #e2e8f0; }
/* Longhand top/bottom only — see .page-event-detail .back-bar-inner's own
   comment for why the shorthand version broke this row's left/right inset. */
.page-guest-detail .back-bar-inner{ display:flex; align-items:center; gap:8px; padding-top:13px; padding-bottom:13px; font-size:13px; font-weight:700; color:#5c6b80; flex-wrap:wrap; }
.page-guest-detail .back-bar-inner a:not(.admin-edit-link):hover{ color:#00264d; }
.page-guest-detail .back-bar-inner .sep{ color:#cbd6e3; }
.page-guest-detail .back-bar-inner .current{ color:#0a1626; font-weight:700; }

.page-guest-detail .guest-hero{ position:relative; background:#f6f8fb; overflow:hidden; padding:60px 0 110px; }
.page-guest-detail .guest-hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(0,38,77,.10) 1.5px,transparent 1.5px); background-size:24px 24px; pointer-events:none; }
.page-guest-detail .guest-hero-inner{ position:relative; z-index:2; max-width:680px; margin:0 auto; text-align:center; }
.page-guest-detail .guest-hero-tags{ display:flex; gap:8px; flex-wrap:wrap; justify-content:center; margin-bottom:18px; }
.page-guest-detail .chip-opt{ font-size:11.5px; font-weight:700; color:#00264d; background:#e9eff6; border:1px solid #dbe5f0; padding:6px 13px; border-radius:999px; }
.page-guest-detail .chip-opt.vip{ color:#b8892b; background:#fbf3e2; border-color:#f0dfb4; display:inline-flex; align-items:center; gap:5px; }
.page-guest-detail .chip-opt.vip::before{ content:"★"; font-size:10px; }
.page-guest-detail h1.guest-name-h1{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(30px,4vw,46px); color:#00264d; line-height:1.12; margin-bottom:8px; }
.page-guest-detail .guest-hero-role{ font-size:16px; color:#0a1626; font-weight:600; margin-bottom:6px; }
.page-guest-detail .guest-hero-inner .sub{ font-size:15px; color:#5c6b80; max-width:52ch; line-height:1.65; margin:14px auto 0; }

/* ---------- badge centerpiece ---------- */
.page-guest-detail .big-badge-wrap{ position:relative; z-index:5; margin-top:-64px; padding-bottom:70px; }
.page-guest-detail .big-badge{ width:100%; background:linear-gradient(150deg,#00264d,#004080 120%); border-radius:26px; box-shadow:0 30px 70px rgba(0,38,77,.35); display:flex; overflow:hidden; position:relative; }
.page-guest-detail .big-badge::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.07) 1.4px,transparent 1.4px); background-size:22px 22px; pointer-events:none; }
.page-guest-detail .bb-main{ flex:1; padding:44px 46px; min-width:0; position:relative; z-index:1; display:flex; gap:28px; align-items:center; }
.page-guest-detail .bb-photo{ width:96px; height:96px; border-radius:50%; background:rgba(255,255,255,.1); border:3px solid #ff9a45; color:#fff; overflow:hidden; display:flex; align-items:center; justify-content:center; font-family:'Playfair Display',serif; font-weight:700; font-size:30px; flex-shrink:0; }
.page-guest-detail .bb-photo img{ width:100%; height:100%; object-fit:cover; }
.page-guest-detail .bb-eyebrow{ font-size:11px; font-weight:800; letter-spacing:.14em; text-transform:uppercase; color:#ff9a45; margin-bottom:8px; }
.page-guest-detail .bb-main h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(20px,2.6vw,26px); color:#fff; line-height:1.2; margin-bottom:20px; }
.page-guest-detail .bb-facts{ display:flex; flex-wrap:wrap; gap:22px 36px; }
.page-guest-detail .bb-fact{ display:flex; align-items:center; gap:12px; }
.page-guest-detail .bb-fact-icon{ width:38px; height:38px; border-radius:10px; background:rgba(255,255,255,.1); color:#ff9a45; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.page-guest-detail .bb-fact-icon svg{ width:17px; height:17px; }
.page-guest-detail .bb-fact-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:rgba(255,255,255,.5); }
.page-guest-detail .bb-fact-value{ display:block; font-size:14.5px; font-weight:700; color:#fff; margin-top:2px; }

.page-guest-detail .bb-stub{ width:230px; flex-shrink:0; background:linear-gradient(160deg,#d9a53f,#b8892b 120%); color:#fff; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px; padding:34px 22px; position:relative; z-index:1; }
.page-guest-detail .bb-stub::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:0; border-left:2px dashed rgba(255,255,255,.4); }
.page-guest-detail .bb-notch{ position:absolute; left:-14px; width:28px; height:28px; border-radius:50%; background:#f6f8fb; z-index:2; }
.page-guest-detail .bb-notch.top{ top:-14px; }
.page-guest-detail .bb-notch.bottom{ bottom:-14px; }
.page-guest-detail .bb-stub .admit{ font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:rgba(255,255,255,.75); font-weight:700; display:flex; align-items:center; gap:5px; }
.page-guest-detail .bb-stub .admit::before{ content:"★"; }
.page-guest-detail .bb-stub .visits{ font-family:'Playfair Display',serif; font-weight:700; font-size:30px; text-align:center; line-height:1; }
.page-guest-detail .bb-stub .visits-lbl{ font-size:10.5px; letter-spacing:.08em; text-transform:uppercase; color:rgba(255,255,255,.75); font-weight:700; margin-top:-6px; }
.page-guest-detail .bb-stub .btn{ width:100%; background:#fff; color:#b8892b; box-shadow:none; }
.page-guest-detail .bb-stub .btn:hover{ background:rgba(255,255,255,.9); transform:translateY(-2px); }
@media (max-width:900px){
  .page-guest-detail .big-badge{ flex-direction:column; }
  .page-guest-detail .bb-main{ flex-direction:column; text-align:center; padding:36px 30px; }
  .page-guest-detail .bb-facts{ justify-content:center; }
  .page-guest-detail .bb-stub{ width:100%; padding:26px 22px 30px; }
  .page-guest-detail .bb-stub::before{ left:0; right:0; top:0; bottom:auto; width:auto; height:0; border-left:none; border-top:2px dashed rgba(255,255,255,.4); }
  .page-guest-detail .bb-notch{ left:50%; transform:translateX(-50%); }
}
@media (max-width:620px){
  .page-guest-detail .bb-main{ padding:30px 24px; }
}

.page-guest-detail .section-head{ display:flex; justify-content:space-between; align-items:flex-end; gap:24px; margin-bottom:36px; flex-wrap:wrap; }
.page-guest-detail .section-head h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(24px,2.8vw,32px); margin-top:10px; color:#00264d; line-height:1.15; }
.page-guest-detail .view-all{ font-weight:700; font-size:13.5px; color:#00264d; display:inline-flex; align-items:center; gap:6px; border-bottom:2px solid #ef7e1a; padding-bottom:2px; flex-shrink:0; }
.page-guest-detail .view-all:hover{ color:#ef7e1a; }

/* ---------- body layout ---------- */
.page-guest-detail .guest-body-layout{ display:grid; grid-template-columns:1fr 340px; gap:56px; align-items:start; padding-top:8px; }
.page-guest-detail .guest-content-block{ margin-bottom:52px; }
.page-guest-detail .guest-content-block h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:21px; color:#00264d; margin-bottom:16px; }
.page-guest-detail .rich-text{ font-size:15px; line-height:1.75; color:#5c6b80; }
.page-guest-detail .rich-text p{ margin-bottom:14px; }
.page-guest-detail .rich-text p:last-child{ margin-bottom:0; }
@media(max-width:900px){ .page-guest-detail .guest-body-layout{ grid-template-columns:1fr; } }

.page-guest-detail .topic-chips{ display:flex; flex-wrap:wrap; gap:10px; }
.page-guest-detail .topic-chip{ padding:9px 16px; border-radius:999px; background:#fff; color:#00264d; font-weight:700; font-size:13px; border:1px solid #cbd6e3; }
.page-guest-detail .format-row{ display:flex; flex-wrap:wrap; gap:10px; }
.page-guest-detail .format-badge{ display:flex; align-items:center; gap:8px; padding:10px 16px; border-radius:12px; background:#fff; border:1px solid #e2e8f0; font-size:13px; font-weight:700; color:#0a1626; }
.page-guest-detail .format-badge svg{ width:15px; height:15px; color:#ef7e1a; }

.page-guest-detail section.pale .agenda-item::before{ border-color:#f6f8fb; }

/* ---------- sidebar ---------- */
.page-guest-detail .guest-sidebar-card{ border:1px solid #e2e8f0; border-radius:18px; padding:26px; margin-bottom:20px; background:#fff; }
.page-guest-detail .guest-sidebar-card h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:#00264d; margin-bottom:16px; }
.page-guest-detail .sidebar-actions{ display:flex; flex-direction:column; gap:10px; margin-bottom:6px; }
.page-guest-detail .sidebar-fact{ padding:14px 0; border-top:1px solid #e2e8f0; }
.page-guest-detail .sidebar-fact:first-of-type{ border-top:none; padding-top:0; }
.page-guest-detail .sidebar-fact-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:#5c6b80; margin-bottom:6px; }
.page-guest-detail .chip-opt-dark{ display:inline-block; font-size:12.5px; font-weight:700; color:#00264d; background:#e9eff6; padding:5px 12px; border-radius:999px; margin:0 6px 6px 0; }
.page-guest-detail .sidebar-fact .val{ font-size:14.5px; font-weight:700; color:#0a1626; }
.page-guest-detail .sidebar-contact-name{ font-size:13.5px; font-weight:700; color:#0a1626; margin-bottom:8px; }
.page-guest-detail .sidebar-contact-link{ display:flex; align-items:center; gap:8px; font-size:13px; color:#004080; font-weight:600; margin-bottom:8px; }
.page-guest-detail .sidebar-contact-link:last-child{ margin-bottom:0; }
.page-guest-detail .sidebar-contact-link svg{ width:14px; height:14px; flex-shrink:0; }
.page-guest-detail .sidebar-share-icons{ display:flex; gap:8px; }
.page-guest-detail .sidebar-share-icons a{ width:32px; height:32px; border-radius:50%; background:#f6f8fb; border:1px solid #e2e8f0; display:flex; align-items:center; justify-content:center; color:#5c6b80; transition:.18s; }
.page-guest-detail .sidebar-share-icons a:hover{ background:#00264d; border-color:#00264d; color:#fff; }
.page-guest-detail .sidebar-share-icons svg{ width:14px; height:14px; }
.page-guest-detail .sidebar-tags{ display:flex; flex-wrap:wrap; gap:8px; }
.page-guest-detail .sidebar-tags a{ font-size:12px; font-weight:700; color:#ef7e1a; background:#fff; border:1px solid #cbd6e3; padding:6px 12px; border-radius:999px; }
.page-guest-detail .sidebar-tags a:hover{ background:#ef7e1a; color:#fff; border-color:#ef7e1a; }

/* ---------- related guest cards ---------- */
.page-guest-detail .rel-card{ flex:0 0 300px; scroll-snap-align:start; background:#fff; border:1px solid #e2e8f0; border-radius:18px; padding:22px; box-shadow:0 2px 10px rgba(0,38,77,.06); transition:.25s ease; }
.page-guest-detail .rel-card:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); transform:translateY(-4px); }
.page-guest-detail .rel-top{ display:flex; align-items:center; gap:12px; margin-bottom:12px; }
.page-guest-detail .rel-avatar{ width:48px; height:48px; border-radius:50%; background:#00264d; color:#fff; display:flex; align-items:center; justify-content:center; font-family:'Playfair Display',serif; font-weight:700; font-size:15px; flex-shrink:0; overflow:hidden; }
.page-guest-detail .rel-name{ font-family:'Playfair Display',serif; font-weight:700; font-size:15.5px; color:#00264d; }
.page-guest-detail .rel-role{ font-size:12px; color:#5c6b80; margin-top:2px; }
.page-guest-detail .rel-tags{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:12px; }
.page-guest-detail .rel-tags span{ font-size:10.5px; font-weight:700; color:#004080; background:#e9eff6; padding:3px 9px; border-radius:999px; }
.page-guest-detail .rel-tags span.vip{ color:#b8892b; background:#fbf3e2; }
.page-guest-detail .rel-link{ font-size:12.5px; font-weight:700; color:#ef7e1a; display:inline-flex; align-items:center; gap:6px; border-top:1px dashed #cbd6e3; padding-top:12px; width:100%; }

/* ---------- CTA band ---------- */
.page-guest-detail .cta-band{ background:#00264d; color:#fff; text-align:center; padding:90px 0; position:relative; overflow:hidden; }
.page-guest-detail .cta-band::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.07) 1.4px,transparent 1.4px); background-size:26px 26px; }
.page-guest-detail .cta-band h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(28px,3.6vw,44px); max-width:22ch; margin:16px auto 12px; line-height:1.18; position:relative; color:#fff; }
.page-guest-detail .cta-band p{ color:rgba(255,255,255,.7); max-width:50ch; margin:0 auto 30px; font-size:15.5px; position:relative; }
.page-guest-detail .cta-band .hero-cta{ display:flex; gap:14px; justify-content:center; flex-wrap:wrap; position:relative; }

@media(max-width:620px){
  .page-guest-detail .wrap{ padding:0 20px; }
}

/* ------------------------------ visit history agenda ----------------------- */
.agenda{ border-left:2px dashed var(--border); margin-left:8px; }
.agenda-item{ position:relative; padding:0 0 28px 28px; }
.agenda-item:last-child{ padding-bottom:0; }
.agenda-item::before{
  content:""; position:absolute; left:-7px; top:2px; width:12px; height:12px; border-radius:50%;
  background:var(--accent, var(--primary-text)); border:3px solid var(--bg); box-shadow:0 0 0 2px var(--accent, var(--primary-text));
}
.agenda-time{ font-size:11.5px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; color:var(--accent, var(--primary-text)); margin-bottom:4px; }
.agenda-title{ font-family:'Playfair Display',serif; font-weight:700; font-size:16px; color:var(--text); margin-bottom:3px; }
.agenda-desc{ font-size:13.5px; color:var(--muted); line-height:1.55; }
.agenda-tag{ display:inline-block; font-size:10.5px; font-weight:700; color:var(--primary-text); background:var(--primary-100); padding:2px 9px; border-radius:999px; margin-top:6px; }

/* ------------------------------- consistent page container ----------------------- */
/* Every full-width band from the header down to the footer shares the same
   1280px column (matching the 5 rebuilt pages' own .wrap) and 24px desktop
   side-gutter — but .site-footer-bottom was
   missing the max-width/margin:auto entirely (rendered full-bleed instead of
   lining up with the column above it), and the header/utility-bar/footer
   each shrank that gutter at their own breakpoint/value picked independently
   in style.css (16px at 820px for the header, 18px at only 520px for the
   footer, 12px at 640px for both — none matching), while .hero-inner,
   .public-page-head-inner and .event-sticky-bar-inner never shrank it at
   all. Content edges (logo, hero heading, section headings, footer text)
   drifted out of alignment with each other as the viewport narrowed.
   Centralised here as one shared two-step scheme instead. Kept at the very
   end of this file — same reason style.css keeps its own responsive
   overrides last (see that file's comment): CSS resolves same-specificity
   ties by source order, so if this lived earlier, each selector's own later
   base rule (e.g. .event-sticky-bar-inner, .public-page-head-inner further
   up) would silently win back regardless of viewport. Only sets left/right
   padding, leaving each container's own top/bottom padding and the header/
   utility bar's unrelated gap/icon-size rules in style.css untouched. */
.site-footer-bottom{ max-width:1280px; margin:0 auto; }
@media (max-width:820px){
  .public-header-inner,
  .public-utility-inner,
  .hero-inner,
  .public-section,
  .public-page-head-inner,
  .site-footer-inner,
  .site-footer-social,
  .site-footer-bottom,
  .event-sticky-bar-inner{ padding-left:20px; padding-right:20px; }
}
@media (max-width:600px){
  .public-header-inner,
  .public-utility-inner,
  .hero-inner,
  .public-section,
  .public-page-head-inner,
  .site-footer-inner,
  .site-footer-social,
  .site-footer-bottom,
  .event-sticky-bar-inner{ padding-left:8px; padding-right:8px; }
}

/* ============================================================================
   EVENTS BROWSE PAGE (templates/public/events_list.html + _events_results.html)
   ---------------------------------------------------------------------------
   Reproduces the all-events.html mockup's own CSS verbatim (colours, sizes,
   spacing), scoped under .page-events-browse so its class names — many
   shared with other mockups (.hero, .ticket, .status-tabs, .pagination) but
   with page-specific values there — never leak into or get overridden by
   the equivalent classes on other public pages. Header/topbar/footer are
   untouched; this page's own markup starts right after the shared header.
   ============================================================================ */
.page-events-browse{ font-family:'Noto Sans',-apple-system,sans-serif; color:#0a1626; }
.page-events-browse .wrap{ max-width:1280px; margin:0 auto; padding:0 40px; }
.page-events-browse ul{ list-style:none; }
.page-events-browse button{ font-family:inherit; cursor:pointer; border:none; background:none; }

.page-events-browse .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-events-browse .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }
.page-events-browse .eyebrow.center{ justify-content:center; }
.page-events-browse .eyebrow.on-dark{ color:#ff9a45; }

.page-events-browse .btn{ display:inline-flex; align-items:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.page-events-browse .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-events-browse .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
.page-events-browse .btn-outline{ border:1.5px solid rgba(255,255,255,.5); color:#fff; background:transparent; }
.page-events-browse .btn-outline:hover{ background:rgba(255,255,255,.12); }
.page-events-browse .btn-navy-outline{ border:1.5px solid #00264d; color:#00264d; }
.page-events-browse .btn-navy-outline:hover{ background:#00264d; color:#fff; }
.page-events-browse .btn-sm{ padding:9px 18px; font-size:13px; }

.page-events-browse section{ padding:88px 0; }

/* ---------- hero / search & filter ---------- */
.page-events-browse .hero{ position:relative; background:#00264d; overflow:hidden; padding:88px 0 0; }
.page-events-browse .hero-bg{ position:absolute; inset:0; z-index:0; }
.page-events-browse .hero-bg img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0; animation:heroFade 15s infinite; }
.page-events-browse .hero-bg img:nth-child(1){ animation-delay:0s; }
.page-events-browse .hero-bg img:nth-child(2){ animation-delay:5s; }
.page-events-browse .hero-bg img:nth-child(3){ animation-delay:10s; }
@keyframes heroFade{ 0%{opacity:0;} 6%{opacity:1;} 30%{opacity:1;} 36%{opacity:0;} 100%{opacity:0;} }
.page-events-browse .hero-bg::after{ content:""; position:absolute; inset:0; background:linear-gradient(120deg,rgba(0,38,77,.94),rgba(0,38,77,.86) 55%,rgba(0,64,128,.72)); }
.page-events-browse .hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.09) 1.4px,transparent 1.4px); background-size:26px 26px; opacity:.5; pointer-events:none; z-index:1; }
.page-events-browse .hero-inner{ position:relative; z-index:2; display:grid; grid-template-columns:1.15fr .85fr; gap:56px; align-items:center; padding-bottom:64px; }
.page-events-browse h1.hero-title{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(36px,4.6vw,56px); line-height:1.1; letter-spacing:-.01em; color:#fff; margin:16px 0 20px; }
.page-events-browse h1.hero-title em{ color:#ff9a45; font-style:italic; }
.page-events-browse .hero-sub{ font-size:16.5px; line-height:1.65; color:rgba(255,255,255,.78); max-width:52ch; margin-bottom:32px; }

.page-events-browse .browse-hero{ padding-bottom:64px; }
.page-events-browse .browse-hero-inner{ display:block; max-width:760px; margin:0 auto; text-align:center; }
.page-events-browse .browse-hero .hero-title{ font-size:clamp(30px,4vw,44px); }

.page-events-browse .filter-panel{ background:#fff; border-radius:18px; padding:22px; box-shadow:0 24px 60px rgba(0,38,77,.16); margin-top:8px; }
/* position/flex-basis/min-width reset — the dashboard's own .filter-search
   (style.css, shared everywhere) is a different design: position:relative
   with an absolutely-positioned icon <svg> overlaid inside a single input
   (input{padding-left:30px !important}). That still applied here since
   this page's own rule never named those properties, so the icon and the
   input's reserved icon-space stopped lining up with this page's actual
   flex-laid-out icon+input. Un-setting them (and killing the dashboard's
   absolute icon position + forced padding for any svg/input nested here)
   restores the mockup's plain side-by-side icon+input row. */
.page-events-browse .filter-search{ position:static; flex:none; display:flex; align-items:center; gap:10px; background:#f6f8fb; border:1px solid #e2e8f0; border-radius:999px; padding:13px 20px; margin-bottom:14px; }
.page-events-browse .filter-search svg{ position:static; }
.page-events-browse .filter-search .fs-icon{ color:#5c6b80; font-size:16px; display:flex; }
.page-events-browse .filter-search input{ border:none; background:none; outline:none; flex:1; font-family:inherit; font-size:14.5px; color:#0a1626; padding-left:0 !important; }
.page-events-browse .filter-row{ display:flex; gap:12px; flex-wrap:wrap; align-items:center; }
.page-events-browse .filter-select{ flex:1; min-width:150px; padding:11px 16px; border-radius:999px; border:1px solid #e2e8f0; background:#fff; color:#0a1626; font-family:inherit; font-size:13.5px; font-weight:600; }
.page-events-browse .clear-link{ font-size:13px; font-weight:700; color:#5c6b80; text-decoration:underline; white-space:nowrap; }
.page-events-browse .clear-link:hover{ color:#ef7e1a; }

/* ---------- status tabs + results ---------- */
.page-events-browse .results-section{ padding-top:56px; }
.page-events-browse .status-tabs{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:32px; padding-bottom:18px; border-bottom:1px solid #e2e8f0; }
.page-events-browse .status-tab{ padding:9px 18px; border-radius:999px; font-weight:700; font-size:13.5px; color:#5c6b80; border:1px solid #cbd6e3; background:none; }
.page-events-browse .status-tab:hover{ border-color:#00264d; color:#00264d; }
.page-events-browse .status-tab.active{ background:#00264d; border-color:#00264d; color:#fff; }
.page-events-browse .results-count{ margin-left:auto; font-size:13px; color:#5c6b80; font-weight:600; }

.page-events-browse .results-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:22px; }

/* ---------- ticket cards ---------- */
.page-events-browse .ticket{ flex:0 0 340px; background:#fff; border:1px solid #e2e8f0; border-radius:18px; display:flex; overflow:visible; box-shadow:0 2px 10px rgba(0,38,77,.06); transition:.25s ease; position:relative; }
.page-events-browse .ticket:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); transform:translateY(-4px); }
.page-events-browse .grid-ticket.ticket{ flex:none; width:100%; }
.page-events-browse .ticket-body{ flex:1; padding:22px 20px; display:flex; flex-direction:column; min-width:0; }
.page-events-browse .status-pill{ align-self:flex-start; display:inline-flex; align-items:center; gap:6px; font-size:10.5px; font-weight:800; letter-spacing:.06em; text-transform:uppercase; padding:5px 10px; border-radius:999px; margin-bottom:12px; }
.page-events-browse .status-pill.live{ background:#fef1e2; color:#ef7e1a; }
.page-events-browse .status-pill.live .dot{
  position:static; width:6px; height:6px; border-radius:50%; border:none; background:#ef7e1a;
  
  animation: pulse 1.6s infinite;
}
@keyframes pulse{ 0%,100%{opacity:1;} 50%{opacity:.25;} }
.page-events-browse .status-pill.upcoming{ background:#e9eff6; color:#00264d; }
.page-events-browse .status-pill.closed{ background:#f6f8fb; color:#5c6b80; }
.page-events-browse .tags-inline{ display:flex; gap:6px; flex-wrap:wrap; margin-bottom:10px; }
.page-events-browse .tags-inline span{ font-size:11px; font-weight:700; color:#004080; background:#e9eff6; padding:3px 9px; border-radius:999px; }
.page-events-browse .ticket-body h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:19px; color:#00264d; margin-bottom:8px; line-height:1.3; }
.page-events-browse .ticket-body p{ font-size:13.5px; color:#5c6b80; line-height:1.55; margin-bottom:14px; }
.page-events-browse .ticket-facts{ font-size:12.5px; color:#5c6b80; display:flex; flex-direction:column; gap:4px; margin-top:auto; padding-top:10px; border-top:1px dashed #cbd6e3; }
.page-events-browse .ticket-facts b{ color:#0a1626; }
.page-events-browse .ticket-stub{ width:96px; flex-shrink:0; background:#00264d; color:#fff; display:flex; flex-direction:column; align-items:center; justify-content:space-between; padding:18px 8px; position:relative; border-left:2px dashed rgba(255,255,255,.25); border-radius:0 18px 18px 0; }
.page-events-browse .ticket-stub::before,
.page-events-browse .ticket-stub::after{ content:""; position:absolute; left:-9px; width:18px; height:18px; border-radius:50%; background:#f6f8fb; z-index:2; }
.page-events-browse .ticket-stub::before{ top:-9px; }
.page-events-browse .ticket-stub::after{ bottom:-9px; }
.page-events-browse .grid-ticket .ticket-stub{ border-radius:0 18px 18px 0; }
.page-events-browse .ticket-stub .vert{ writing-mode:vertical-rl; transform:rotate(180deg); font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.6); font-weight:700; }
/* Circular arrow icon (not the old full-width "View" text pill) — matches
   .ticket-stub-arrow's look on every other ticket-shaped card on the site
   (index.html's live-events carousel, "More events like this", etc). Stays
   a real <a> (not a decorative span like those) since this card has no
   whole-card data-href click handler of its own — this is each result's
   only way into its event-detail page. */
.page-events-browse .ticket-stub .stub-cta{
  position:relative; z-index:1; width:30px; height:30px; border-radius:50%; background:#ef7e1a; color:#fff;
  display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s ease;
}
.page-events-browse .ticket-stub .stub-cta svg{ width:14px; height:14px; }
.page-events-browse .ticket-stub .stub-cta:hover{ background:#ff9a45; }
/* Same specificity as the unconditional navy background two rules up, so
   without this override here (later in the cascade) it would win over the
   shared global .ticket-stub.is-past gradient and every past card's stub
   would stay navy instead of reading as "already happened". */
.page-events-browse .ticket-stub.is-past{ background:linear-gradient(180deg,#6d7889,#4b5563); }

/* ---------- pagination + empty state ---------- */
.page-events-browse .pagination{ display:flex; align-items:center; justify-content:center; gap:18px; margin-top:48px; }
.page-events-browse .page-btn{ display:inline-flex; align-items:center; gap:6px; padding:11px 22px; border-radius:999px; border:1px solid #cbd6e3; font-weight:700; font-size:13.5px; color:#00264d; }
.page-events-browse .page-btn:hover{ background:#00264d; color:#fff; border-color:#00264d; }
.page-events-browse .page-info{ font-size:13px; color:#5c6b80; font-weight:600; }
.page-events-browse .empty-state{ text-align:center; padding:60px 20px; }
.page-events-browse .empty-state h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:22px; color:#00264d; margin-bottom:10px; }
.page-events-browse .empty-state p{ font-size:14px; color:#5c6b80; }

@media(max-width:980px){
  .page-events-browse .results-grid{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:620px){
  .page-events-browse .wrap{ padding:8px; }
  .page-events-browse .results-grid{ grid-template-columns:1fr; }
  .page-events-browse .filter-row{ flex-direction:column; align-items:stretch; }
  .page-events-browse .status-tabs{ gap:6px; }
  .page-events-browse .results-count{ width:100%; margin-left:0; order:5; }
}

/* ============================================================================
   GUESTS DIRECTORY PAGE (templates/public/guests.html)
   ---------------------------------------------------------------------------
   Reproduces the guests.html mockup's own CSS verbatim, scoped under
   .page-guests. .id-badge/.id-*, .tag-cloud/.tag-pill and .value-grid/
   .value-card/.value-badge are already unscoped, guests-page-only, and
   already match the mockup's own values — left untouched rather than
   duplicated under the scope. .ticket-carousel/.ticket-row/.carousel-arrow/
   .ticket-carousel-nav-row (VIP spotlight's carousel shell) are the same
   shared unscoped components the event-detail "more like this" row uses;
   only the .vip-card content inside them (a different design from .ticket)
   is new. */
.page-guests{ font-family:'Noto Sans',-apple-system,sans-serif; color:#0a1626; }
.page-guests .wrap{ max-width:1280px; margin:0 auto; padding:0 40px; }
/* background:#fff explicit here (not left to inherit) — the shared site
   body{background:var(--bg)} (style.css) is #F5F6FB, a pale blue-gray
   dashboard tone, not pure white. This mockup-port page assumes a plain
   white body everywhere except .pale sections, same as the original mockup
   HTML's own body{background:var(--paper)} (--paper:#fff there) — without
   this, every non-.pale section here (the search/filter hero, "Why it
   matters") was quietly showing the dashboard's own off-white instead. */
.page-guests section{ padding:88px 0; background:#fff; }
.page-guests section.pale{ background:#f6f8fb; }

.page-guests .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-guests .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }
.page-guests .eyebrow.center{ justify-content:center; }
.page-guests .eyebrow.on-dark{ color:#ff9a45; }

.page-guests .btn{ display:inline-flex; align-items:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.page-guests .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-guests .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
/* Reused .btn-outline class name also exists (differently) as a plain,
   unscoped rule in style.css for the dashboard: `.btn-outline{
   background:var(--surface); ... }`. That rule's `background` declaration
   still wins here for the `background-color` property specifically — this
   page-scoped override never re-declared it, only border/color — since CSS
   cascades per PROPERTY, not per rule block; a higher-specificity selector
   only overrides a property it actually SETS. The result was a solid white
   button with white text sitting on the navy hero (invisible). Explicit
   background:transparent here reclaims that property. */
.page-guests .btn-outline{ border:1.5px solid rgba(255,255,255,.5); color:#fff; background:transparent; }
.page-guests .btn-outline:hover{ background:rgba(255,255,255,.12); }
.page-guests .btn-sm{ padding:9px 18px; font-size:13px; }

/* ---------- hero ---------- */
.page-guests .hero{ position:relative; background:#00264d; overflow:hidden; padding:88px 0 0; }
.page-guests .hero-bg{ position:absolute; inset:0; z-index:0; }
.page-guests .hero-bg img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:0; animation:heroFade 15s infinite; }
.page-guests .hero-bg img:nth-child(1){ animation-delay:0s; }
.page-guests .hero-bg img:nth-child(2){ animation-delay:5s; }
.page-guests .hero-bg img:nth-child(3){ animation-delay:10s; }
.page-guests .hero-bg::after{ content:""; position:absolute; inset:0; background:linear-gradient(120deg,rgba(0,38,77,.94),rgba(0,38,77,.86) 55%,rgba(0,64,128,.72)); }
.page-guests .hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.09) 1.4px,transparent 1.4px); background-size:26px 26px; opacity:.5; pointer-events:none; z-index:1; }
.page-guests .hero-inner{ position:relative; z-index:2; display:grid; grid-template-columns:1.15fr .85fr; gap:56px; align-items:center; padding-bottom:64px; }
.page-guests h1.hero-title{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(36px,4.6vw,56px); line-height:1.1; letter-spacing:-.01em; color:#fff; margin:16px 0 20px; }
.page-guests h1.hero-title em{ color:#ff9a45; font-style:italic; }
.page-guests .hero-sub{ font-size:16.5px; line-height:1.65; color:rgba(255,255,255,.78); max-width:52ch; margin-bottom:32px; }
.page-guests .hero-cta{ display:flex; gap:14px; flex-wrap:wrap; margin-bottom:44px; }
.page-guests .hero-stats{ display:flex; gap:0; border-top:1px solid rgba(255,255,255,.16); padding-top:26px; }
.page-guests .hstat{ flex:1; padding-right:24px; }
.page-guests .hstat .num{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(24px,2.4vw,32px); color:#fff; }
.page-guests .hstat .lbl{ font-size:12.5px; color:rgba(255,255,255,.62); text-transform:uppercase; letter-spacing:.08em; margin-top:4px; font-weight:600; }

/* ---------- search & filter (browse-hero) ---------- */
.page-guests .browse-hero{ padding:56px 0; }
.page-guests .browse-hero-inner{ display:block; max-width:820px; margin:0 auto; text-align:center; }
.page-guests .filter-panel{ background:#fff; border-radius:18px; padding:22px; margin-top:8px; text-align:left; }
/* Same style.css .filter-search bleed-through fix as the events-browse
   page (see that page's own block for why) — position/flex reset plus
   killing the dashboard's absolute icon + forced input padding-left. */
.page-guests .filter-search{ position:static; flex:none; display:flex; align-items:center; gap:10px; background:#f6f8fb; border:1px solid #e2e8f0; border-radius:999px; padding:13px 20px; margin-bottom:14px; }
.page-guests .filter-search svg{ position:static; }
.page-guests .filter-search .fs-icon{ color:#5c6b80; font-size:16px; display:flex; }
.page-guests .filter-search input{ border:none; background:none; outline:none; flex:1; font-family:inherit; font-size:14.5px; color:#0a1626; padding-left:0 !important; }
.page-guests .filter-row{ display:flex; gap:12px; flex-wrap:wrap; align-items:center; }
.page-guests .filter-select{ flex:1; min-width:150px; padding:11px 16px; border-radius:999px; border:1px solid #e2e8f0; background:#fff; color:#0a1626; font-family:inherit; font-size:13.5px; font-weight:600; }
.page-guests .clear-link{ font-size:13px; font-weight:700; color:#5c6b80; text-decoration:underline; white-space:nowrap; }
.page-guests .clear-link:hover{ color:#ef7e1a; }

.page-guests .section-head{ display:flex; justify-content:space-between; align-items:flex-end; gap:24px; margin-bottom:42px; flex-wrap:wrap; }
.page-guests .section-head h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(26px,3vw,38px); margin-top:12px; color:#00264d; line-height:1.15; }
.page-guests .section-head p{ color:#5c6b80; font-size:15px; max-width:56ch; margin-top:10px; }

/* ---------- VIP spotlight card ---------- */
.page-guests .vip-card{ flex:0 0 320px; scroll-snap-align:start; background:#fff; border:1px solid #e2e8f0; border-radius:18px; display:flex; overflow:visible; box-shadow:0 2px 10px rgba(0,38,77,.06); transition:.25s ease; position:relative; }
.page-guests .vip-card:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); transform:translateY(-4px); }
.page-guests .vip-card-body{ flex:1; padding:22px 20px; display:flex; flex-direction:column; min-width:0; }
.page-guests .vip-avatar-row{ display:flex; align-items:center; gap:12px; margin-bottom:14px; }
.page-guests .vip-avatar{ width:52px; height:52px; border-radius:50%; background:#00264d; color:#fff; display:flex; align-items:center; justify-content:center; font-family:'Playfair Display',serif; font-weight:700; font-size:16px; flex-shrink:0; overflow:hidden; }
.page-guests .vip-avatar-row h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:16.5px; color:#00264d; line-height:1.25; }
.page-guests .vip-avatar-row .role{ font-size:12px; color:#5c6b80; margin-top:2px; }
.page-guests .vip-card-body p{ font-size:13px; color:#5c6b80; line-height:1.55; margin-bottom:14px; }
.page-guests .vip-facts{ font-size:12px; color:#5c6b80; display:flex; flex-direction:column; gap:4px; margin-top:auto; padding-top:10px; border-top:1px dashed #cbd6e3; }
.page-guests .vip-facts b{ color:#0a1626; }
.page-guests .vip-stub{ width:88px; flex-shrink:0; background:linear-gradient(160deg,#b8892b,#8f6716); color:#fff; display:flex; flex-direction:column; align-items:center; justify-content:space-between; padding:18px 6px; position:relative; border-left:2px dashed rgba(255,255,255,.35); border-radius:0 18px 18px 0; }
.page-guests .vip-stub::before,
.page-guests .vip-stub::after{ content:""; position:absolute; left:-9px; width:18px; height:18px; border-radius:50%; background:#f6f8fb; }
.page-guests section:not(.pale) .vip-stub::before,
.page-guests section:not(.pale) .vip-stub::after{ background:#fff; }
.page-guests .vip-stub::before{ top:-9px; }
.page-guests .vip-stub::after{ bottom:-9px; }
.page-guests .vip-stub .vert{ writing-mode:vertical-rl; transform:rotate(180deg); font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:rgba(255,255,255,.85); font-weight:800; }
.page-guests .vip-stub .star{ font-size:15px; }

/* ---------- status tabs + results ---------- */
.page-guests .status-tabs{ margin-bottom:32px; padding-bottom:18px; border-bottom:1px solid #e2e8f0; }
.page-guests .results-count{ margin-left:auto; font-size:13px; color:#5c6b80; font-weight:600; }

/* ---------- guest directory cards ---------- */
.page-guests .guest-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:20px; }
.page-guests .guest-card{ background:#fff; border:1px solid #e2e8f0; border-radius:16px; padding:22px; display:flex; flex-direction:column; gap:2px; transition:.2s ease; position:relative; }
.page-guests .guest-card:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); transform:translateY(-3px); border-color:#cbd6e3; }
.page-guests .gc-top{ display:flex; align-items:flex-start; gap:14px; margin-bottom:12px; }
.page-guests .gc-avatar-wrap{ position:relative; flex-shrink:0; }
.page-guests .gc-avatar{ width:54px; height:54px; border-radius:50%; background:#00264d; color:#fff; display:flex; align-items:center; justify-content:center; font-family:'Playfair Display',serif; font-weight:700; font-size:16px; border:3px solid #f6f8fb; overflow:hidden; }
.page-guests .gc-vip-star{ position:absolute; bottom:-3px; right:-3px; width:20px; height:20px; border-radius:50%; background:#b8892b; color:#fff; font-size:10px; display:flex; align-items:center; justify-content:center; border:2px solid #fff; }
.page-guests .gc-name{ font-family:'Playfair Display',serif; font-weight:700; font-size:16px; color:#0a1626; line-height:1.3; }
.page-guests .gc-role{ font-size:12.5px; color:#5c6b80; margin-top:3px; line-height:1.5; }
.page-guests .gc-tags{ display:flex; flex-wrap:wrap; gap:6px; margin:12px 0; }
.page-guests .gc-tags span{ font-size:10.5px; font-weight:700; color:#004080; background:#e9eff6; padding:3px 9px; border-radius:999px; }
.page-guests .gc-tags span.vip{ color:#b8892b; background:#fbf3e2; }
.page-guests .gc-bio{ font-size:12.5px; color:#5c6b80; line-height:1.6; margin-bottom:14px; flex:1; }
.page-guests .gc-foot{ display:flex; justify-content:space-between; align-items:center; font-size:11.5px; color:#5c6b80; border-top:1px dashed #cbd6e3; padding-top:12px; margin-top:auto; }
.page-guests .gc-foot .campus{ font-weight:600; }
.page-guests .gc-foot .go{ color:#ef7e1a; font-weight:700; display:inline-flex; align-items:center; gap:4px; font-size:12px; }

.page-guests .empty-state{ text-align:center; padding:60px 20px; color:#5c6b80; }
.page-guests .empty-state h3{ font-family:'Playfair Display',serif; color:#00264d; font-size:20px; margin-bottom:8px; }

.page-guests .pagination{ display:flex; align-items:center; justify-content:center; gap:18px; margin-top:48px; }
.page-guests .page-btn{ display:inline-flex; align-items:center; gap:6px; padding:11px 22px; border-radius:999px; border:1px solid #cbd6e3; font-weight:700; font-size:13.5px; color:#00264d; }
.page-guests .page-btn:hover{ background:#00264d; color:#fff; border-color:#00264d; }
.page-guests .page-info{ font-size:13px; color:#5c6b80; font-weight:600; }

/* ---------- CTA band ---------- */
.page-guests .cta-band{ background:#00264d; color:#fff; text-align:center; padding:90px 0; position:relative; overflow:hidden; }
.page-guests .cta-band::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(255,255,255,.07) 1.4px,transparent 1.4px); background-size:26px 26px; }
.page-guests .cta-band h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(28px,3.6vw,44px); max-width:22ch; margin:16px auto 12px; line-height:1.18; position:relative; color:#fff; }
.page-guests .cta-band p{ color:rgba(255,255,255,.7); max-width:50ch; margin:0 auto 30px; font-size:15.5px; position:relative; }
.page-guests .cta-band .hero-cta{ justify-content:center; position:relative; }

@media(max-width:980px){
  .page-guests .hero-inner{ grid-template-columns:1fr; }
  .page-guests .guest-grid{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:620px){
  .page-guests .wrap{ padding:0 20px; }
  .page-guests .guest-grid{ grid-template-columns:1fr; }
  .page-guests .filter-row{ flex-direction:column; align-items:stretch; }
  .page-guests .status-tabs{ gap:6px; }
  .page-guests .results-count{ width:100%; margin-left:0; order:5; }
}

/* =====================================================================
   Venues directory (templates/public/venues.html) — ported verbatim from
   the pes-venues.html reference mockup: breadcrumb + light dotted-paper
   hero with a centered campus filter + a plain 3-up card grid. Hardcoded
   hex (not CSS vars) since this is a literal mockup port, same reasoning
   as .page-guest-detail/.page-event-detail above.
   ===================================================================== */
.page-venues .wrap{ max-width:1280px; margin:0 auto; padding:13px 40px; }

.page-venues .back-bar{ background:#f6f8fb; border-bottom:1px solid #e2e8f0; }
.page-venues .back-bar-inner{ display:flex; align-items:center; gap:8px; font-size:13px; font-weight:700; color:#5c6b80; flex-wrap:wrap; }
.page-venues .back-bar-inner a:hover{ color:#00264d; }
.page-venues .back-bar-inner .sep{ color:#cbd6e3; }
.page-venues .back-bar-inner .current{ color:#0a1626; font-weight:700; }

.page-venues .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; justify-content:center; }
.page-venues .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }

.page-venues .venues-hero{ position:relative; background:#f6f8fb; overflow:hidden; padding:64px 0 40px; }
.page-venues .venues-hero::before{ content:""; position:absolute; inset:0; background-image:radial-gradient(rgba(0,38,77,.10) 1.5px,transparent 1.5px); background-size:24px 24px; pointer-events:none; }
.page-venues .venues-hero-inner{ position:relative; z-index:2; max-width:680px; margin:0 auto; text-align:center; }
.page-venues .venues-hero-inner h1{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(30px,4vw,44px); color:#00264d; margin:14px 0 12px; line-height:1.15; }
.page-venues .venues-hero-inner .sub{ font-size:15.5px; color:#5c6b80; line-height:1.65; max-width:52ch; margin:0 auto 30px; }

.page-venues .campus-filter{ display:flex; gap:8px; justify-content:center; flex-wrap:wrap; position:relative; z-index:2; }
.page-venues .filter-pill{ padding:9px 18px; border-radius:999px; font-weight:700; font-size:13.5px; color:#5c6b80; border:1px solid #cbd6e3; background:#fff; }
.page-venues .filter-pill:hover{ border-color:#00264d; color:#00264d; }
.page-venues .filter-pill.active{ background:#00264d; border-color:#00264d; color:#fff; }

.page-venues .venues-section{ padding:56px 0 90px; }
.page-venues .venue-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:26px; }
.page-venues .venue-card{ display:block; border:1px solid #e2e8f0; border-radius:18px; overflow:hidden; background:#fff; transition:.22s ease; }
.page-venues .venue-card:hover{ box-shadow:0 12px 34px rgba(0,38,77,.10); transform:translateY(-4px); border-color:#cbd6e3; }
.page-venues .venue-card-photo{ position:relative; height:190px; overflow:hidden; background:linear-gradient(135deg,#00264d,#004080); }
.page-venues .venue-card-photo img{ width:100%; height:100%; object-fit:cover; transition:.35s ease; }
.page-venues .venue-card:hover .venue-card-photo img{ transform:scale(1.06); }
.page-venues .venue-card-campus{ position:absolute; top:12px; left:12px; background:rgba(0,38,77,.78); backdrop-filter:blur(4px); color:#fff; font-size:10.5px; font-weight:700; padding:5px 11px; border-radius:999px; z-index:2; }
.page-venues .venue-card-cap{ position:absolute; top:12px; right:12px; background:rgba(255,255,255,.92); color:#00264d; font-size:10.5px; font-weight:800; padding:5px 11px; border-radius:999px; z-index:2; }
.page-venues .venue-card-body{ padding:18px 20px 20px; }
.page-venues .venue-card-body h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:18px; color:#00264d; margin-bottom:6px; }
.page-venues .venue-card-body p{ font-size:12.5px; color:#5c6b80; margin-bottom:14px; }
.page-venues .venue-card-foot{ display:flex; justify-content:space-between; align-items:center; border-top:1px dashed #cbd6e3; padding-top:12px; }
.page-venues .venue-card-events{ font-size:12px; font-weight:700; color:#004080; }
.page-venues .venue-card-arrow{ width:30px; height:30px; border-radius:50%; background:#ef7e1a; color:#fff; display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s ease; }
.page-venues .venue-card-arrow svg{ width:14px; height:14px; }
.page-venues .venue-card:hover .venue-card-arrow{ background:#ff9a45; }

/* Not present in the static reference (which shows a fixed 9-card page) —
   pagination/empty-state only ever appear once real data exceeds one page
   or a campus filter has zero venues, so they add no visible deviation for
   the common case that matches the reference. */
.page-venues .empty-state{ text-align:center; padding:60px 20px; color:#5c6b80; }
.page-venues .empty-state h3{ font-family:'Playfair Display',serif; color:#00264d; font-size:20px; margin-bottom:8px; }
.page-venues .pagination{ display:flex; align-items:center; justify-content:center; gap:18px; margin-top:40px; }
.page-venues .page-btn{ display:inline-flex; align-items:center; gap:6px; padding:11px 22px; border-radius:999px; border:1px solid #cbd6e3; font-weight:700; font-size:13.5px; color:#00264d; }
.page-venues .page-btn:hover{ background:#00264d; color:#fff; border-color:#00264d; }
.page-venues .page-info{ font-size:13px; color:#5c6b80; font-weight:600; }

@media(max-width:980px){
  .page-venues .venue-grid{ grid-template-columns:repeat(2,1fr); }
}
@media(max-width:620px){
  .page-venues .wrap{ padding:8px; }
  .page-venues .venue-grid{ grid-template-columns:1fr; }
}

/* =====================================================================
   Venue detail (templates/public/venue_detail.html) — ported verbatim
   from the pes-venue-detail.html reference mockup: breadcrumb + photo
   hero + quick-facts bar + two-column body/sidebar + a "happening here"
   ticket carousel (reuses the site's existing global .ticket/.ticket-row/
   .ticket-stub components — see the shared block above — rather than
   duplicating a page-scoped copy, since this page's carousel needs to
   look identical to every other "upcoming events" carousel on the site).
   Hardcoded hex, same reasoning as .page-venues above.
   ===================================================================== */
.page-venue-detail .wrap{ max-width:1280px; margin:0 auto; padding:20px 40px; }
.page-venue-detail .back-bar{ background:#f6f8fb; border-bottom:1px solid #e2e8f0; }
.page-venue-detail .back-bar-inner{ display:flex; align-items:center; gap:8px; padding-top:13px; padding-bottom:13px; font-size:13px; font-weight:700; color:#5c6b80; flex-wrap:wrap; }
.page-venue-detail .back-bar-inner a:not(.admin-edit-link):hover{ color:#00264d; }
.page-venue-detail .back-bar-inner .sep{ color:#cbd6e3; }
.page-venue-detail .back-bar-inner .current{ color:#0a1626; font-weight:700; }

.page-venue-detail .venue-hero{ position:relative; height:360px; overflow:hidden; background:linear-gradient(135deg,#00264d,#004080); }
.page-venue-detail .venue-hero img{ width:100%; height:100%; object-fit:cover; }
.page-venue-detail .venue-hero::after{ content:""; position:absolute; inset:0; background:linear-gradient(0deg,rgba(0,38,77,.92),rgba(0,38,77,.35) 55%,rgba(0,38,77,.1)); }
.page-venue-detail .venue-hero-content{ position:absolute; left:0; right:0; bottom:0; z-index:2; padding:36px 0; }
.page-venue-detail .venue-hero-badges{ display:flex; gap:8px; margin-bottom:14px; }
.page-venue-detail .vh-badge{ font-size:11.5px; font-weight:700; color:#fff; background:rgba(255,255,255,.14); border:1px solid rgba(255,255,255,.25); padding:6px 13px; border-radius:999px; }
.page-venue-detail .venue-hero-content h1{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(30px,4vw,44px); color:#fff; line-height:1.1; }

.page-venue-detail .quick-facts-bar{ background:#e9eff6; border-bottom:1px solid #dbe5f0; }
.page-venue-detail .qf-row{ display:flex; flex-wrap:wrap; gap:16px 0; }
.page-venue-detail .qf-item{ display:flex; align-items:center; gap:11px; flex:1; min-width:170px; padding-right:20px; }
.page-venue-detail .qf-icon{ width:36px; height:36px; border-radius:9px; background:#fff; color:#00264d; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.page-venue-detail .qf-icon svg{ width:16px; height:16px; }
.page-venue-detail .qf-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:#004080; }
.page-venue-detail .qf-value{ display:block; font-size:14px; font-weight:700; color:#00264d; margin-top:1px; }

.page-venue-detail .eyebrow{ font-size:12px; font-weight:700; letter-spacing:.12em; text-transform:uppercase; color:#ef7e1a; display:flex; align-items:center; gap:9px; }
.page-venue-detail .eyebrow::before{ content:""; width:7px; height:7px; border-radius:50%; background:#ef7e1a; flex-shrink:0; }

.page-venue-detail .btn{ display:inline-flex; align-items:center; justify-content:center; gap:8px; padding:13px 26px; border-radius:999px; font-weight:700; font-size:14.5px; font-family:inherit; transition:.2s ease; white-space:nowrap; }
.browse-button.btn{ padding:10px 16px; border-radius:999px; font-weight:600; font-size:13.5px;}
.page-venue-detail .btn-solid{ background:#ef7e1a; color:#fff; box-shadow:0 10px 22px rgba(239,126,26,.28); }
.page-venue-detail .btn-solid:hover{ background:#ff9a45; transform:translateY(-2px); }
.page-venue-detail .btn-outline{ border:1.5px solid #cbd6e3; color:#00264d; }
.page-venue-detail .btn-outline:hover{ background:#e9eff6; }
.page-venue-detail .btn-sm{ padding:9px 18px; font-size:13px; }

.page-venue-detail section{ padding:64px 0; }
.page-venue-detail section.pale{ background:#f6f8fb; }
.page-venue-detail .section-head{ display:flex; justify-content:space-between; align-items:flex-end; gap:24px; margin-bottom:32px; flex-wrap:wrap; }
.page-venue-detail .section-head h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:clamp(24px,2.8vw,30px); margin-top:10px; color:#00264d; line-height:1.15; }

/* "Events at this venue" ticket carousel — nav arrows live in the
   section-head next to the "Browse all" button (.ticket-carousel-nav-row,
   same global classes/placement as every other ticket carousel on the
   site — index.html's Live events, event_detail.html's related events,
   guest_detail.html's related guests), not positioned over the row itself
   like the original pes-venue-detail.html reference mockup had them —
   consistency with the rest of the site won out over literal mockup
   fidelity here. Live/upcoming/past status pills reuse the same
   .status-pill{{ event.calendar_status }} idiom already used by
   event_detail.html's "More events like this" carousel. */
.page-venue-detail .status-pill{ align-self:flex-start; display:inline-flex; align-items:center; gap:7px; font-size:11px; font-weight:800; letter-spacing:.07em; text-transform:uppercase; padding:6px 13px; border-radius:999px; margin-bottom:10px; }
.page-venue-detail .status-pill.live{ background:#fef1e2; color:#ef7e1a; }
.page-venue-detail .status-pill.upcoming{ background:#e9eff6; color:#00264d; }
.page-venue-detail .status-pill.closed{ background:#f6f8fb; color:#5c6b80; border:1px solid #e2e8f0; }
.page-venue-detail .status-pill .dot{
  position:static; width:6px; height:6px; border-radius:50%; border:none; background:#ef7e1a;
  
  animation: pulse 1.6s infinite;
}

/* The site's shared global .ticket-body only styles an h3 title (every
   other page's ticket carousel uses h3) — this page follows the reference
   mockup's own h4 instead, so it needs its own page-scoped rule or it'd
   render as an unstyled browser-default heading. */
.page-venue-detail .ticket-body h4{ font-family:'Playfair Display',serif; font-weight:700; font-size:18px; color:#00264d; margin-bottom:8px; line-height:1.3; }

.page-venue-detail .ticket-stub .stub-cta{
  position:relative; z-index:1; width:30px; height:30px; border-radius:50%; background:#ef7e1a; color:#fff;
  display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:background .2s ease;
}
.page-venue-detail .ticket-stub .stub-cta svg{ width:14px; height:14px; }
.page-venue-detail .ticket-stub .stub-cta:hover{ background:#ff9a45; }

.page-venue-detail .venue-body-layout{ display:grid; grid-template-columns:1fr 320px; gap:56px; align-items:start; }
.page-venue-detail .venue-content-block{ margin-bottom:48px; }
.page-venue-detail .venue-content-block h2{ font-family:'Playfair Display',serif; font-weight:700; font-size:21px; color:#00264d; margin-bottom:16px; }
.page-venue-detail .rich-text p{ font-size:15px; line-height:1.75; color:#5c6b80; }

.page-venue-detail .amenity-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; }
.page-venue-detail .amenity{ display:flex; align-items:center; gap:12px; border:1px solid #e2e8f0; border-radius:12px; padding:14px 16px; }
.page-venue-detail .amenity-icon{ width:34px; height:34px; border-radius:9px; background:#e9eff6; color:#00264d; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.page-venue-detail .amenity-icon svg{ width:16px; height:16px; }
.page-venue-detail .amenity-label{ font-size:13px; font-weight:700; color:#0a1626; }

.page-venue-detail .photo-gallery{ display:grid; grid-template-columns:repeat(4,1fr); gap:12px; }
.page-venue-detail .photo-gallery a{ border-radius:12px; overflow:hidden; aspect-ratio:1/1; position:relative; background:linear-gradient(135deg,#00264d,#004080); cursor:zoom-in; }
.page-venue-detail .photo-gallery img{ width:100%; height:100%; object-fit:cover; transition:.3s; }
.page-venue-detail .photo-gallery a:hover img{ transform:scale(1.08); }

/* Full-screen lightbox opened by clicking a Gallery photo (see the
   data-lightbox-trigger click handler in venue_detail.html's extra_js) —
   zoom via the toolbar buttons (transform: scale on the <img>, no pan since
   only zoom in/out + prev/next + close were asked for), left/right cycles
   through venue.gallery.all, Esc/click-on-backdrop closes. */
.page-venue-detail .pg-lightbox{ position:fixed; inset:0; z-index:1000; background:rgba(10,22,38,.94); display:none; }
.page-venue-detail .pg-lightbox.show{ display:block; }
/* The stage fills the whole overlay and is the scroll container — a fixed-
   size box with overflow:hidden here would just clip a transform:scale()'d
   image to its unzoomed box (what was happening before: zooming looked like
   cropping in on a fixed little window instead of the picture getting
   bigger). Grid's place-items:center keeps the image centered while still
   letting overflow:auto reach every edge once it's scaled past the stage —
   flexbox centering has a long-standing bug where you can't scroll to
   content overflowing the "before" side; grid alignment doesn't. */
.page-venue-detail .pg-lightbox-stage{ position:absolute; inset:0; display:grid; place-items:center; overflow:auto; }
.page-venue-detail .pg-lightbox-stage img{ max-width:88vw; max-height:80vh; object-fit:contain; transition:transform .2s ease; cursor:zoom-in; }
.page-venue-detail .pg-lightbox-close,
.page-venue-detail .pg-lightbox-toolbar button,
.page-venue-detail .pg-lightbox-nav{
  width:42px; height:42px; border-radius:50%; background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.25);
  color:#fff; display:flex; align-items:center; justify-content:center; transition:.2s;
}
.page-venue-detail .pg-lightbox-close:hover,
.page-venue-detail .pg-lightbox-toolbar button:hover,
.page-venue-detail .pg-lightbox-nav:hover{ background:#ef7e1a; border-color:#ef7e1a; }
.page-venue-detail .pg-lightbox-close{ position:absolute; top:24px; right:24px; z-index:2; }
.page-venue-detail .pg-lightbox-toolbar{ position:absolute; top:24px; left:24px; z-index:2; display:flex; gap:8px; }
.page-venue-detail .pg-lightbox-nav{ position:absolute; top:50%; transform:translateY(-50%); width:48px; height:48px; z-index:2; }
.page-venue-detail .pg-lightbox-nav.prev{ left:24px; }
.page-venue-detail .pg-lightbox-nav.next{ right:24px; }
.page-venue-detail .pg-lightbox-count{ position:absolute; bottom:22px; left:50%; transform:translateX(-50%); color:rgba(255,255,255,.75); font-size:13px; font-weight:700; letter-spacing:.04em; }

@media(max-width:620px){
  .page-venue-detail .pg-lightbox-close,
  .page-venue-detail .pg-lightbox-toolbar button{ width:36px; height:36px; }
  .page-venue-detail .pg-lightbox-close{ top:14px; right:14px; }
  .page-venue-detail .pg-lightbox-toolbar{ top:14px; left:14px; }
  .page-venue-detail .pg-lightbox-nav{ width:38px; height:38px; }
  .page-venue-detail .pg-lightbox-nav.prev{ left:8px; }
  .page-venue-detail .pg-lightbox-nav.next{ right:8px; }
}

.page-venue-detail .location-card{ border:1px solid #e2e8f0; border-radius:16px; padding:24px; display:flex; justify-content:space-between; align-items:center; gap:20px; flex-wrap:wrap; }
.page-venue-detail .location-address{ font-size:13.5px; color:#5c6b80; line-height:1.6; max-width:40ch; }
.page-venue-detail .location-address b{ color:#0a1626; display:block; font-family:'Playfair Display',serif; font-size:16px; margin-bottom:4px; }

.page-venue-detail .venue-sidebar-card{ border:1px solid #e2e8f0; border-radius:18px; padding:26px; margin-bottom:20px; background:#fff; }
.page-venue-detail .venue-sidebar-card h3{ font-family:'Playfair Display',serif; font-weight:700; font-size:17px; color:#00264d; margin-bottom:16px; }
.page-venue-detail .vs-fact{ padding:13px 0; border-top:1px solid #e2e8f0; }
.page-venue-detail .vs-fact:first-of-type{ border-top:none; padding-top:0; }
.page-venue-detail .vs-fact-label{ display:block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:#5c6b80; margin-bottom:5px; }
.page-venue-detail .vs-fact-value{ font-size:14px; font-weight:700; color:#0a1626; }
.page-venue-detail .vs-contact-name{ font-size:13.5px; font-weight:700; color:#0a1626; margin-bottom:8px; }
.page-venue-detail .vs-contact-link{ display:flex; align-items:center; gap:8px; font-size:13px; color:#004080; font-weight:600; margin-bottom:8px; }
.page-venue-detail .vs-contact-link svg{ width:14px; height:14px; flex-shrink:0; }
.page-venue-detail .vs-share-icons{ display:flex; gap:8px; }
.page-venue-detail .vs-share-icons a{ width:32px; height:32px; border-radius:50%; background:#f6f8fb; border:1px solid #e2e8f0; display:flex; align-items:center; justify-content:center; color:#5c6b80; transition:.18s; }
.page-venue-detail .vs-share-icons a:hover{ background:#00264d; border-color:#00264d; color:#fff; }
.page-venue-detail .vs-share-icons svg{ width:14px; height:14px; }

@media(max-width:900px){
  .page-venue-detail .venue-body-layout{ grid-template-columns:1fr; }
  .page-venue-detail .amenity-grid{ grid-template-columns:repeat(2,1fr); }
  .page-venue-detail .photo-gallery{ grid-template-columns:repeat(2,1fr); }
  .page-venue-detail .qf-item{ min-width:45%; }
}
@media(max-width:620px){
  .page-venue-detail .wrap{ padding:8px; }
  .page-venue-detail .amenity-grid{ grid-template-columns:1fr; }
}
