/* Auburn Library Finder — app shell. Mobile-first; leans on branding tokens with safe fallbacks. */

:root {
  /* Map onto the Auburn brand tokens (colors_and_type.css is loaded in index.html); fallbacks match
     the real brand hexes so a missed stylesheet degrades on-brand, not to FindIt's old approximations. */
  --fv-navy: var(--auburn-navy, #22326E);
  --fv-accent: var(--auburn-orange, #EB5F2E);
  --fv-ink: var(--fg, #1A1F33);
  --fv-muted: var(--auburn-gray, #65695A);
  --fv-line: var(--border, #E2E1DA);
  /* Surface must read the city tokens' --surface (white), NOT branding's --bg: tokens.css
     shadows --bg with the tan app base (css-plan semantics), so mapping surface to --bg
     turns every card/sheet tan — the collision that flattened the sheet's depth. */
  --fv-surface: var(--surface, #ffffff);
  /* The tan "app base" — the pill-row track (.fv-tabs), the find input, chips, cover placeholders,
     etc. read this. Source is tokens.css's --bg (deliberately tan here, #F6F5F0), NOT branding's --bg
     (white). Was previously `var(#F6F5F0, --bg-alt)` — malformed (var()'s first arg must be a custom
     property, not a color), which made --fv-bg invalid so every `background: var(--fv-bg)` fell back
     to transparent and lost its tan. Dark mode still needs a rework; this is the light-by-default base. */
  --fv-bg: var(--bg, #F6F5F0);
  --fv-radius: 14px;

  /* Re-theme <library-item-card> (light-by-default) to match this app's brand tokens — it has no
     ambient knowledge of --fv-*, same reasoning as website-footer's --contact-card-* overrides. */
  --library-item-text: var(--fv-ink);
  --library-item-muted: var(--fv-muted);
  --library-item-accent: var(--fv-navy);
  --library-item-cover-bg: var(--fv-bg);
  --library-item-badge-in-bg: #d4edda;
  --library-item-badge-in-text: #12703c;
  --library-item-badge-out-bg: #ffe8cc;
  --library-item-badge-out-text: #8a4b00;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100vh;   /* fallback for browsers without small-viewport units */
  height: 100svh;  /* small viewport = toolbar-shown height; the shell never exceeds the visible area */
  overflow: hidden; /* the document itself must never scroll — the shell owns all scrolling
                       internally (.screen-body, .directory-scroll, …). Without this, Chrome
                       Android resolves root % height against the LARGE viewport, making the body
                       taller than the visible area so the page scrolls and pushes the bottom nav
                       below the fold. */
  background: var(--fv-bg);
  color: var(--fv-ink);
  font-family: var(--font-body, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
}

/* The facility viewer is one routed screen inside the City Viewer shell (.app > .screen) — it
   fills the screen's flex column; the shell owns the viewport and the bottom nav below it. */
fv-shell {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

/* ── Header ─────────────────────────────────────────────────────────────── */
/* Overlays the map: transparent with navy text while the map shows (a soft fade keeps the title
   legible over floor-plan linework); goes solid navy when the sheet covers the map. */
.fv-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 5;
  padding: calc(env(safe-area-inset-top, 0) + 0.9rem) 1.1rem 0.9rem;
  background: linear-gradient(to bottom, rgba(246, 245, 240, 0.92) 55%, rgba(246, 245, 240, 0));
  color: var(--fv-navy);
  transition: background-color 0.25s ease, color 0.25s ease;
}
/* Text column flexes so the account button lands hard right (back ‹ | text | avatar). */
.fv-header__text { flex: 1 1 auto; min-width: 0; }
.fv-header__title { margin: 0; font-size: 1.3rem; font-weight: 700; letter-spacing: 0.01em; color: inherit; }
.fv-header__sub { margin: 0.15rem 0 0; font-size: 0.85rem; color: inherit; opacity: 0.85; }
.fv-header.is-solid {
  background: var(--fv-navy);
  color: #fff;
}

/* ── Offline notice ─────────────────────────────────────────────────────── */
/* Pill under the header while the service worker is serving saved (last-known-good) data —
   shown/hidden by fv-shell from the fv:connectivity event (see api.js). */
.fv-offline {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + 4.4rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  padding: 0.35rem 0.9rem;
  border-radius: 999px;
  background: var(--fv-navy);
  color: #fff;
  font-size: 0.8rem;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

/* ── Map ────────────────────────────────────────────────────────────────── */
.fv-map-wrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  background: #dfe5ee;
}
fv-map {
  display: block;
  position: relative; /* anchors the aerial attribution pill */
  width: 100%;
  height: 100%;
  overflow: hidden;   /* panned/zoomed aerial imagery must not spill past the map area */
}
fv-map svg { width: 100%; height: 100%; display: block; }
.fv-map-empty {
  display: grid;
  place-items: center;
  height: 100%;
  color: var(--fv-muted);
  font-size: 0.9rem;
}

/* Server-rendered floor plan: style the classes the SVG ships with (otherwise paths default to black).
   Shell fill is transparent (not just a light color) so a floor underlay drawn behind it — which
   spans roughly the whole canvas — isn't hidden beneath it. Applies with or without an underlay. */
fv-map svg path.shell {
  fill: transparent;
  stroke: #9aa3b2;
  stroke-width: 2.5;
  pointer-events: none;
}
/* Aerial basemap (data-basemap='aerial' — floors with no authored underlay, composed
   client-side from /aerial-tiles/viewport): tiles may extend past the floor plan's canvas
   rect, and letting them overflow keeps the imagery full-bleed while panning — the fv-map
   host's overflow:hidden is what actually clips. */
fv-map svg[data-basemap='aerial'] { overflow: visible; }
fv-map svg[data-basemap='aerial'] image.aerial-tile { pointer-events: none; }
/* The shell over imagery is a property boundary, not a building outline — white with a soft
   halo reads on any photography. */
fv-map svg[data-basemap='aerial'] path.shell {
  stroke: #ffffff;
  stroke-opacity: 0.9;
  stroke-dasharray: 8 5;
  filter: drop-shadow(0 0 1.5px rgba(0, 0, 0, 0.55));
}
fv-map .fv-map-attr {
  position: absolute;
  right: 0.5rem;
  bottom: 0.5rem;
  font-size: 0.62rem;
  color: #fff;
  background: rgba(20, 26, 38, 0.55);
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  pointer-events: none;
}
fv-map svg path.space {
  fill: #ffffff;
  stroke: #c2c9d6;
  stroke-width: 1;
}
/* Private rooms: drawn as recessed background context (no label, not interactive). */
fv-map svg path.space.muted {
  fill: #e7ebf1;
  stroke: #d2d8e1;
}
fv-map svg path.space.is-tappable { cursor: pointer; }
/* Hidden = highlight-only wayfinding zones (private non-room spaces, e.g. drawn shelf areas):
   invisible and inert until a catalog search resolves to them and `.selected` lights them up. */
fv-map svg path.space.hidden {
  fill: none;
  stroke: none;
  pointer-events: none;
}
fv-map svg path.space.selected,
fv-map svg path.space.hidden.selected {
  fill: #ffe3cc;
  stroke: var(--fv-accent);
  stroke-width: 2;
}
fv-map svg text.room-label {
  fill: var(--fv-muted);
  font-size: 10px;
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
}
/* Landmark icons (restroom, entrance, elevator, stairs, water, parking) — stroke-based symbols
   themed via `color` (they draw with currentColor); size is fixed by the API's width/height on
   the <use> element, not by CSS. */
fv-map svg .space-icon {
  color: var(--fv-navy);
  pointer-events: none;
}
/* Reference-image underlay (outdoor facilities) — strictly a backdrop, never interactive. */
fv-map svg .underlay {
  pointer-events: none;
}

/* ── Bottom sheet ───────────────────────────────────────────────────────── */
.fv-sheet {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  /* Three height states, set via data-state (see fv-shell.setSheetState):
       half   — results height; map stays visible for highlight feedback
       full   — tall lists / forms
       detail — covers the map for a full entity view; the grip and rounded corners stay visible
     Definite heights (not max-height) so the surface scrolls internally and result changes never
     resize the sheet — that's what caused the jumping while typing. flex-shrink:0 is essential —
     without it a flex parent shrinks the sheet to fit a short result list, so the height tracks
     content (jumps) instead of staying pinned. State changes are tweened as pixel values by
     fv-shell (#tweenSheet) — CSS auto-transitions proved unreliable. */
  flex: none;
  height: 46%;
  /* The persistent bottom nav sits under the sheet: it owns the safe-area padding, and the sheet
     loses its shadow to sit flush on the bar (design 2b). */
  padding: 0.5rem 1rem 1rem;
  background: var(--fv-surface);
  border-radius: 18px 18px 0 0;
  transition: height 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.fv-sheet[data-state="full"] { height: 80%; }
/* Locate (item + find): content-sized within [half … ~two-thirds] — tall enough that the item's
   action row clears the fold (the fixed 46% clipped it), short enough that the map highlight
   stays visible. Safe from the "jumping" the definite heights above guard against: the locate
   view's content is one item's static location card, not a live-changing result list. */
.fv-sheet[data-state="locate"] {
  height: auto;
  min-height: 46%;
  max-height: 68%;
}
/* Detail covers the map: the sheet rises above the header, leaving a sliver of map behind the
   grip + rounded corners so it still reads as a sheet you can pull down. */
.fv-sheet[data-state="detail"] {
  position: relative;
  z-index: 6;
  height: calc(100% - env(safe-area-inset-top, 0px) - 18px);
}
@media (prefers-reduced-motion: reduce) { .fv-sheet { transition: none; } }

.fv-grip {
  display: block;
  width: 100%;
  padding: 0.35rem 0 0.1rem;
  margin: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* The grip is a drag handle (fv-shell #bindSheetDrag) — without this the browser claims a
     vertical touch-drag as page scroll and the sheet never moves. */
  touch-action: none;
}
.fv-grip::before {
  content: "";
  display: block;
  width: 2.25rem;
  height: 0.28rem;
  margin: 0 auto;
  border-radius: 999px;
  background: var(--fv-line);
}

.fv-tabs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.25rem;
  padding: 0.25rem;
  background: var(--fv-bg);
  border-radius: 999px;
}
.fv-tabs button {
  padding: 0.5rem 0.25rem;
  font: inherit;
  font-weight: 600;
  font-size: 0.92rem;
  color: var(--fv-muted);
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}
.fv-tabs button.is-active {
  color: var(--fv-navy);
  background: var(--fv-surface);
  box-shadow: 0 1px 3px rgba(10, 20, 40, 0.12);
}

.fv-surface {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* ── Find ───────────────────────────────────────────────────────────────── */
.fv-search__input {
  width: 100%;
  padding: 0.7rem 0.9rem;
  font: inherit;
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius);
  background: var(--fv-bg);
}
.fv-search__hint { margin: 0.5rem 0.2rem 0.75rem; font-size: 0.82rem; color: var(--fv-muted); }

.fv-results { display: flex; flex-direction: column; gap: 0.5rem; }
.fv-result {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.7rem;
  width: 100%;
  padding: 0.6rem;
  font: inherit;
  text-align: left;
  background: var(--fv-surface);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius);
  cursor: pointer;
}
/* library-item-card (catalog search rows) — same visual box as .fv-result, but NOT the .fv-result
   class itself: that rule's display:grid would fight the card's own internal flex layout for its
   single shadow-DOM child (a 3-column grid with one item leaves the 1fr/auto columns unsized).
   The card owns cover/title/author/badge layout internally (see item-card.js); this rule only
   contributes the outer box — width/padding/background/border/cursor — matching .fv-result. */
library-item-card[data-key] {
  display: block;
  width: 100%;
  padding: 0.6rem;
  font: inherit;
  text-align: left;
  background: var(--fv-surface);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius);
  cursor: pointer;
}
/* Visible keyboard focus for the button-like results (catalog rows + space cards). */
.fv-result:focus-visible,
library-item-card[data-key]:focus-visible,
.fv-space-card:focus-visible {
  outline: 2px solid var(--fv-accent);
  outline-offset: 2px;
}
.fv-result__spine {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 3.1rem;
  padding: 0.2rem;
  font-size: 0.6rem;
  font-weight: 700;
  line-height: 1.1;
  text-align: center;
  color: #fff;
  background: var(--fv-navy);
  border-radius: 4px;
}
/* Catalog cover (Syndetics), with a faint-book placeholder when there's no cover. */
.fv-result__cover {
  width: 2.6rem;
  height: 3.6rem;
  object-fit: cover;
  border-radius: 3px;
  background: var(--fv-bg);
  box-shadow: 0 1px 2px rgba(10, 20, 40, 0.18);
}
.fv-result__cover--ph {
  display: block;
  background-color: #e7ecf4;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa3b2' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 19.5A2.5 2.5 0 0 1 6.5 17H20'/%3E%3Cpath d='M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 45%;
}
.fv-result__body { display: flex; flex-direction: column; min-width: 0; }
.fv-result__title { font-size: 0.95rem; }
.fv-result__author { font-size: 0.82rem; color: var(--fv-muted); }
.fv-result__title,
.fv-result__author { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fv-result__format { font-size: 0.74rem; font-weight: 600; color: var(--fv-navy); }

/* Edition/format chips — the detail views' "Other editions" switcher (the old non-tappable
   grouped-result card is gone: a multi-edition work renders as one shared card and edition
   choice happens in the detail view). */
.fv-fmts { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 0.4rem; }
.fv-fmt {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.3rem 0.65rem; font: inherit; font-size: 0.78rem; font-weight: 600;
  color: var(--fv-navy); background: var(--fv-bg); border: 1px solid var(--fv-line);
  border-radius: 999px; cursor: pointer;
}
.fv-fmt__dot { width: 0.5rem; height: 0.5rem; border-radius: 999px; }
.fv-fmt__dot.is-in { background: #12703c; }
.fv-fmt__dot.is-out { background: #c77400; }
/* Digital editions: availability is unknown until selected (live lookup), so a neutral navy dot. */
.fv-fmt__dot.is-digital { background: var(--fv-navy); }

.fv-badge { font-size: 0.72rem; font-weight: 700; padding: 0.2rem 0.5rem; border-radius: 999px; white-space: nowrap; }
.fv-badge--in { color: #12703c; background: #d4edda; }
.fv-badge--out { color: #8a4b00; background: #ffe8cc; }

/* ── Item detail (a selected catalog result — mockup 04) ────────────────── */
.fv-item-head { display: flex; gap: 0.85rem; align-items: flex-start; }
.fv-item-head__cover { width: 3.6rem; height: 5rem; }
.fv-item-head .fv-result__spine { width: 3.6rem; height: 5rem; font-size: 0.72rem; }
.fv-item-head__body { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.fv-item-head__title { font-size: 1.15rem; line-height: 1.25; color: var(--fv-navy); }
.fv-item-head__status { align-self: flex-start; }

.fv-itemloc {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  background: var(--fv-bg);
  border-radius: var(--fv-radius);
}
.fv-itemloc__row { display: flex; gap: 0.6rem; align-items: flex-start; }
.fv-itemloc__icon { flex: none; width: 1.1rem; height: 1.1rem; margin-top: 0.1rem; color: var(--fv-accent); }
.fv-itemloc__icon svg { width: 100%; height: 100%; display: block; }
.fv-itemloc__body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1; }
.fv-itemloc__label { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fv-muted); }
.fv-itemloc__value { font-size: 0.95rem; font-weight: 600; color: var(--fv-ink); }
.fv-itemloc__sub { font-size: 0.8rem; color: var(--fv-muted); }
.fv-itemloc__count { font-size: 0.8rem; font-weight: 400; color: var(--fv-muted); text-transform: none; letter-spacing: normal; }
.fv-itemloc__call {
  align-self: center; padding: 0.35rem 0.7rem; font-size: 0.8rem; font-weight: 700;
  color: var(--fv-navy); background: var(--fv-surface); border: 1px solid var(--fv-line); border-radius: 10px;
}
.fv-item-copies { font-size: 0.8rem; color: var(--fv-muted); }

/* ── Item full details (level 3 — covers the map) ───────────────────────── */
.fv-item-hero { display: flex; gap: 1rem; align-items: flex-start; }
.fv-item-hero__cover {
  width: 6.5rem; max-height: 9.5rem; object-fit: cover; border-radius: 6px;
  background: var(--fv-bg); box-shadow: 0 2px 8px rgba(10, 20, 40, 0.22);
}
.fv-item-hero__spine { width: 6.5rem; height: 9rem; font-size: 0.85rem; }
.fv-item-full__title { font-size: 1.25rem; line-height: 1.25; color: var(--fv-navy); }
.fv-item-summary { margin: 0; font-size: 0.9rem; line-height: 1.6; color: var(--fv-ink); }
.fv-item-facts {
  display: flex; flex-direction: column; gap: 0.45rem;
  padding: 0.85rem 1rem; background: var(--fv-bg); border-radius: var(--fv-radius);
}
.fv-item-fact { display: flex; gap: 0.6rem; font-size: 0.85rem; }
.fv-item-fact b { flex: 0 0 7.5rem; color: var(--fv-muted); font-weight: 600; }
.fv-item-fact span { min-width: 0; overflow-wrap: anywhere; }
.fv-item-editions { display: flex; flex-direction: column; gap: 0.35rem; }
.fv-eedition {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  padding: 0.55rem 0.8rem; background: var(--fv-bg); border-radius: var(--fv-radius);
}
.fv-eedition__body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.fv-eedition__fmt { font-size: 0.9rem; font-weight: 600; color: var(--fv-navy); }

.fv-loading, .fv-empty { padding: 0.75rem 0.2rem; color: var(--fv-muted); font-size: 0.9rem; }

/* Transient toast (util.js toast()) — the app's alert() replacement. */
.fv-toast {
  position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0) + 1.25rem);
  transform: translate(-50%, 0.75rem); opacity: 0;
  z-index: 60; max-width: min(88vw, 26rem);
  padding: 0.7rem 1rem; font-size: 0.88rem; line-height: 1.4; text-align: center;
  color: #fff; background: var(--fv-navy); border-radius: 12px;
  box-shadow: 0 4px 18px rgba(10, 20, 40, 0.3);
  transition: transform 0.25s ease, opacity 0.25s ease;
  pointer-events: none;
}
.fv-toast.is-shown { transform: translate(-50%, 0); opacity: 1; }
.fv-toast--error { background: #8a1c1c; }
.fv-error { padding: 0.5rem 0.7rem; border-radius: var(--fv-radius); background: #fdecea; color: #8a1c1c; font-size: 0.85rem; }
.fv-stub { font-style: italic; }

/* ── Header back button + map gestures ──────────────────────────────────── */
.fv-header { display: flex; align-items: center; gap: 0.6rem; }
.fv-back {
  display: grid; place-items: center; width: 2rem; height: 2rem; flex: none;
  color: var(--fv-navy); font-size: 1.5rem; line-height: 1; text-decoration: none;
  background: var(--fv-surface); border-radius: 999px;
  box-shadow: 0 1px 4px rgba(10, 20, 40, 0.18);
}
.fv-header.is-solid .fv-back { color: #fff; background: rgba(255, 255, 255, 0.14); box-shadow: none; }
/* Account button rides the facility header like the back button: white pill over the map,
   translucent on the solid navy band (app-shell.css only covers .top-bar placement). */
.fv-header.is-solid .account-button { color: #fff; background: rgba(255, 255, 255, 0.14); box-shadow: none; }
.fv-header.is-solid .account-button.is-avatar { color: var(--fv-navy); background: #fff; }
/* The ‹ glyph's ink sits low in its line box — nudge just the glyph to optically center it. */
.fv-back__chev { display: block; line-height: 1; transform: translateY(-1px); }
/* No text selection on the map — dragging to pan would otherwise highlight the room labels. */
fv-map { overflow: hidden; touch-action: none; cursor: grab; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; }
fv-map:active { cursor: grabbing; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.fv-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.6rem 1rem; font: inherit; font-weight: 600; text-decoration: none;
  color: var(--fv-navy); background: var(--fv-bg); border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius); cursor: pointer;
}
.fv-btn--primary { color: #fff; background: var(--fv-navy); border-color: var(--fv-navy); }
.fv-btn--accent { color: #fff; background: var(--fv-accent); border-color: var(--fv-accent); }
.fv-btn--sm { padding: 0.35rem 0.7rem; font-size: 0.82rem; }
.fv-btn--ghost { background: transparent; color: var(--fv-muted); }
.fv-btn.is-done { color: #12703c; background: #d4edda; border-color: #d4edda; }
.fv-btn:disabled { opacity: 0.6; cursor: default; }
.fv-link { padding: 0; font: inherit; font-weight: 600; color: var(--fv-navy); background: none; border: 0; cursor: pointer; }

/* ── Directory ──────────────────────────────────────────────────────────── */
/* The Places screen (fv-directory) now renders with the shared city components:
   .top-bar (app-shell.css) + .directory-list / .directory-card (lists.css). */
fv-directory {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
fv-directory .directory-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* ── Barcode scanning (drawer sign-in) ──────────────────────────────────── */
/* Barcode field + camera-scan button; the full-screen scanner overlay sits above everything. */
.fv-scanrow { display: flex; gap: 0.4rem; }
.fv-scanrow input { flex: 1; min-width: 0; }
.fv-scan { flex: none; padding: 0.5rem 0.7rem; }
.fv-scan svg { width: 1.25rem; height: 1.25rem; display: block; }
.fv-scanner {
  position: fixed; inset: 0; z-index: 50;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1rem;
  background: rgba(8, 14, 30, 0.92);
  padding: 1.5rem;
}
.fv-scanner__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.fv-scanner__frame {
  position: relative; width: min(78vw, 22rem); aspect-ratio: 2.2 / 1;
  border: 3px solid #fff; border-radius: 12px;
  box-shadow: 0 0 0 100vmax rgba(8, 14, 30, 0.55);
}
.fv-scanner__hint { position: relative; margin: 0; color: #fff; font-size: 0.9rem; text-shadow: 0 1px 3px rgba(0,0,0,0.6); }
.fv-scanner__cancel { position: relative; }

/* ── Events ─────────────────────────────────────────────────────────────── */
.fv-events { display: flex; flex-direction: column; }
.fv-event { display: grid; grid-template-columns: 3rem 1fr; gap: 0.8rem; padding: 0.7rem 0; border-bottom: 1px solid var(--fv-line); }
.fv-event__date { text-align: center; color: var(--fv-navy); }
.fv-event__date b { display: block; font-size: 1.25rem; line-height: 1.1; }
.fv-event__date span { display: block; font-size: 0.7rem; color: var(--fv-muted); }
.fv-event__date .fv-event__month { color: var(--fv-accent); font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
.fv-event__body { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.fv-event__cat { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fv-accent); }
/* Meta line: time · location, quiet by design — the location is tappable but reads as metadata,
   not an accent-colored link (per the design mockups). */
.fv-event__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; font-size: 0.8rem; color: var(--fv-muted); }
.fv-event__time { display: inline-flex; align-items: center; gap: 0.25rem; }
.fv-event__dot { color: var(--fv-line); }
.fv-event__loc {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0; font: inherit; font-size: 0.8rem; font-weight: 600; color: var(--fv-muted);
  background: none; border: 0; cursor: pointer;
}
.fv-event__loc.is-static { cursor: default; font-weight: 400; }
button.fv-event__loc:hover, button.fv-event__loc:focus-visible { color: var(--fv-navy); text-decoration: underline; }
.fv-event__time svg, .fv-event__loc svg { width: 0.9rem; height: 0.9rem; flex: none; }

/* ── Vendor events (City Market — docs/design/vendor-events.html) ─────────── */
/* Series banner atop the Events list — the entry point into the vendor view. */
.fv-vendor-banner {
  display: flex; justify-content: space-between; align-items: center; gap: 0.6rem;
  width: 100%; margin-bottom: 0.75rem; padding: 0.65rem 0.85rem;
  font: inherit; text-align: left; cursor: pointer;
  color: var(--fv-navy); background: var(--fv-bg);
  border: 1px solid var(--fv-line); border-radius: var(--fv-radius);
}
.fv-vendor-banner:hover, .fv-vendor-banner:focus-visible { border-color: var(--fv-navy); }
.fv-vendor-banner span { font-size: 0.8rem; font-weight: 600; color: var(--fv-accent); white-space: nowrap; }

/* Date chips are buttons in the vendor view (the amenity chips are static). */
button.fv-chip { font: inherit; font-size: 0.78rem; font-weight: 600; cursor: pointer; }
button.fv-chip:hover, button.fv-chip:focus-visible { border-color: var(--fv-navy); }
.fv-chip.is-active { color: #fff; background: var(--fv-navy); border-color: var(--fv-navy); }

.fv-vendors { display: flex; flex-direction: column; margin-top: 0.75rem; }
/* A vendor row (event view, search result, "who's here"): optional thumb + text column. */
.fv-vendor { display: flex; gap: 0.65rem; align-items: flex-start; padding: 0.6rem 0; border-bottom: 1px solid var(--fv-line); }
.fv-vendor__thumb { width: 44px; height: 44px; flex: none; object-fit: cover; border-radius: 8px; border: 1px solid var(--fv-line); background: var(--fv-bg); }
.fv-vendor__body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.fv-vendor__body strong { font-size: 0.92rem; }
.fv-vendor__desc { font-size: 0.8rem; color: var(--fv-muted); }
.fv-vendor__cancelled { color: var(--fv-accent); font-weight: 600; margin-top: 0.5rem; }
.fv-chips + .fv-vendor__cancelled { margin-top: 0.75rem; }
.fv-vendor .fv-chips { margin-top: 0.35rem; }
/* The picked date's resolved stall — the search result's location line. */
.fv-vendor__where { display: inline-flex; align-items: center; gap: 0.25rem; margin-top: 0.35rem; font-size: 0.8rem; font-weight: 600; color: var(--fv-navy); }
.fv-vendor__where svg { width: 0.9rem; height: 0.9rem; flex: none; }
.fv-vendor .fv-link { font-size: 0.8rem; }

/* ── Coming-soon screen (direct ?facility=id to an unpublished viewer) ───────── */
.fv-coming { flex: 1 1 auto; display: grid; place-items: center; padding: 2rem; }
.fv-coming__card { max-width: 22rem; display: flex; flex-direction: column; align-items: center; gap: 1rem; text-align: center; }
.fv-coming__badge {
  padding: 0.3rem 0.8rem; font-size: 0.78rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: #fff; background: var(--fv-accent); border-radius: 999px;
}
.fv-coming p { margin: 0; color: var(--fv-muted); }

/* ── Find: result groups + space cards ──────────────────────────────────────── */
.fv-group + .fv-group { margin-top: 1rem; }
.fv-group__title { margin: 0 0 0.5rem; font-size: 0.76rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--fv-muted); }
.fv-space-list { display: flex; flex-direction: column; gap: 0.5rem; }
.fv-space-item { display: flex; flex-direction: column; gap: 0.4rem; }
.fv-space-item .fv-space-action { align-self: flex-start; }
.fv-reserve-back { margin-top: 0.75rem; }

/* Space / item detail + reservation panel (card + availability + form + submit, single view). */
.fv-detail { display: flex; flex-direction: column; gap: 0.85rem; }
.fv-detail__back { align-self: flex-start; }
.fv-detail__action { align-self: flex-start; }
.fv-detail__actions { display: flex; gap: 0.6rem; }
.fv-detail__actions .fv-btn:first-child:not(:only-child),
.fv-detail__actions .fv-btn--accent { flex: 1; }
.fv-detail__submit { width: 100%; }
.fv-success { padding: 0.85rem 1rem; border-radius: var(--fv-radius); background: #eafaf0; color: #12703c; font-size: 0.92rem; line-height: 1.5; }

/* facility-space-card is a shadow-DOM element themed via these custom props (they pierce shadow). */
:root {
  --color-primary: var(--fv-navy);
  --color-primary-dark: #0c1c36;
  --color-surface: var(--fv-surface);
  --color-border: var(--fv-line);
  --color-text: var(--fv-ink);
  --color-muted: var(--fv-muted);
}

/* ── Info tab: rich content + parks amenities ───────────────────────────────── */
.fv-rich { font-size: 0.9rem; line-height: 1.55; color: var(--fv-ink); }
.fv-rich > :first-child { margin-top: 0; }
.fv-rich > :last-child { margin-bottom: 0; }
.fv-rich a { color: var(--fv-accent); }
.fv-rich img { max-width: 100%; height: auto; border-radius: 8px; }
.fv-tabhtml:not(:last-child) { margin-bottom: 1rem; padding-bottom: 1rem; border-bottom: 1px solid var(--fv-line); }

.fv-amenities { display: flex; flex-direction: column; gap: 0.85rem; }
.fv-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.fv-chip { padding: 0.25rem 0.6rem; font-size: 0.78rem; font-weight: 600; color: var(--fv-navy); background: var(--fv-bg); border: 1px solid var(--fv-line); border-radius: 999px; }
/* Amenity chips carry their SpaceType icon (24×24 inner markup; the markup's own
   fill/stroke attributes reference currentColor — size it, don't restyle it). */
.fv-chip--amenity { display: inline-flex; align-items: center; gap: 0.3rem; }
.fv-chip__icon { width: 0.9rem; height: 0.9rem; flex-shrink: 0; }
.fv-amenity-meta { display: flex; flex-direction: column; gap: 0.35rem; }
.fv-amenity-row { display: flex; gap: 0.6rem; font-size: 0.88rem; }
.fv-amenity-row b { flex: 0 0 4.5rem; color: var(--fv-muted); font-weight: 600; }
.fv-amenity-row a { color: var(--fv-navy); }
