/* map.css — /p/map (Venue Map). Mirrors find.css's map+sidebar layout
   (pan/zoom map reused from map.js's mountMap(), same .zone/.is-active
   pattern) but stripped of round tabs / seat-level drill-down / claim
   badges — this page has one flat list of booths/gates/facilities. */

* { box-sizing: border-box; }
body { margin: 0; background: var(--space-gradient), var(--bg-primary); background-repeat: no-repeat; background-attachment: fixed; color: var(--text-primary); font-family: var(--font-thai); }
button, input, select, textarea { font: inherit; color: inherit; }

.vm-page {
  display: flex;
  height: calc(100vh - var(--navbar-total-h));
  height: calc(100dvh - var(--navbar-total-h));
  background: var(--space-gradient), var(--bg-primary);
  background-repeat: no-repeat;
  background-attachment: fixed;
  color: var(--text-primary);
  font-family: var(--font-thai);
}

/* Scoped starfield (mountStarfield(mapWrap) triggers .is-scoped mode) — the
   base rule in p/tokens.css is position:fixed/inset:0, which covers the
   WHOLE viewport above the map's own SVG since .vm-map doesn't establish a
   stacking context on its own. This confines it to the map box instead, so
   the SVG (appended after, later in DOM order) paints on top. Same override
   find.css already carries for the identical setup. */
#p-starfield.is-scoped, #p-shooting-stars.is-scoped { position: absolute; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }

/* ── Map area (reuses map.js's .zone/.is-active hooks, same as find.css) ── */
.vm-map {
  position: relative;
  flex: 1;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  background:
    radial-gradient(circle at 30% 20%, rgba(var(--accent-teal-rgb),0.06), transparent 60%),
    var(--bg-primary);
  /* Mobile only in practice (desktop's .vm-page has a fixed height, so
     there's no page-level scroll for scrollIntoView to act on there) —
     without this, selecting an item from the sidebar list scrolls .vm-map
     flush to y:0, right underneath the sticky navbar (nav.css's .ppw-nav,
     56px tall, z-index:1000 — far above the toolbar's z-index:2), hiding
     the zoom/reset/rotate buttons behind it. Reported as "ปุ่ม Control
     หาย" (the control buttons disappeared) after searching + selecting
     from the panel on mobile — found live, root cause via nav.css's own
     `position: sticky; top: 0` rule, not a bug in this page's own JS. */
  scroll-margin-top: var(--navbar-total-h);
}
.vm-map.is-panning { cursor: grabbing; }
/* mountMap() (map.js) hardcodes these class names on the elements it
   creates — .find-map__viewport, .find-dim-layer, .find-badge-layer — so
   this page's CSS must target those exact names, not a vm-* rename, or the
   engine's own elements (most importantly the dim-overlay rect, which
   defaults to fully opaque with no rule to zero it out) go unstyled. */
.find-map__viewport { position: absolute; top: 0; left: 0; transform-origin: 0 0; }
/* venue-map.js's rotate-by-90° group — animated smoothly, independent of
   the pan/zoom transform above (which is instant except for map.js's own
   .is-animating class on camera moves). */
/* No transform-origin override here — the rotate() value itself already
   carries its own explicit center point (rotate(deg cx cy)), set by
   venue-map.js; adding a CSS transform-origin on top would double-apply
   the offset. */
.vm-rotate-group { transition: transform 0.35s ease; }
.find-map__viewport.is-interacting { will-change: transform; }
.find-map__viewport.is-animating { transition: transform 0.4s ease; }
.find-map__viewport svg { display: block; }
.find-map__viewport .zone { cursor: pointer; transition: filter 0.15s ease, opacity 0.15s ease; }
@media (hover: hover) { .find-map__viewport .zone:hover { filter: brightness(1.25); } }
/* Colored glow + a darker-shade outline on the whole group's silhouette,
   not a stroke on every child path — find.css's original rect/path stroke
   rule was written for seat-plan.svg's single-path zones; venue-map.svg's
   groups are 10-20+ paths per icon (background + outlined letters/glyphs),
   so that same rule stroked every one of them white and produced a messy
   inner outline. outline (not SVG stroke) renders around the group's
   bounding box, which suits these roughly-rectangular icon cards without
   touching a single path inside. --zone-glow/--zone-outline are set
   per-element by venue-map.js (selectItem()) to that item's own color
   (full + darkened) — both fall back to white/dark gray if unset. */
/* CSS `outline` always follows the element's rectangular bounding box, not
   its actual rendered shape — looked like a plain box slapped over a
   rounded/irregular icon. drop-shadow() instead hugs the real alpha
   silhouette, so the border traces the icon's own outline: 4 zero-blur
   drop-shadows nudged in each direction stack into a ~2px silhouette-
   hugging stroke, plus 2 blurred ones for the glow on top. */
.find-map__viewport .zone.is-active {
  filter:
    drop-shadow(1px 0 0 var(--zone-outline, #333))
    drop-shadow(-1px 0 0 var(--zone-outline, #333))
    drop-shadow(0 1px 0 var(--zone-outline, #333))
    drop-shadow(0 -1px 0 var(--zone-outline, #333))
    drop-shadow(0 0 4px var(--zone-glow, var(--white)))
    drop-shadow(0 0 10px var(--zone-glow, var(--white)));
}
/* Dims EVERYTHING once something is active — every other icon AND the base
   floor-plan art (the IMPACT group's arena outline/seat-zone colors) — so
   the selection isn't just "one thing highlighted among many equally-lit
   things" but the whole map recedes around it. Excludes the engine's own
   utility layers (dim-layer/badge-layer), which have their own opacity
   rules and must not be dragged into this one.
   Targets .vm-rotate-group > *, not svg > * — venue-map.js's rotate-by-90°
   feature wraps every one of the SVG's children (zones, dim-layer,
   badge-layer, all of it) inside that one group, so `svg > *` only ever
   matches the wrapper itself and dims the WHOLE map — including the active
   zone nested inside it — as one undifferentiated blob. Found live:
   "selecting something fades everything, including the thing selected." */
.find-map__viewport:has(.zone.is-active) .vm-rotate-group > *:not(.zone.is-active):not(.find-dim-layer):not(.find-badge-layer) {
  opacity: 0.35;
  transition: opacity 0.2s ease;
}
/* pointer-events: none is load-bearing, not decorative — this rect covers
   the ENTIRE svg canvas (see map.js's setDim()), so without it every click
   anywhere on the map was silently swallowed by this invisible overlay
   before it ever reached a .zone underneath, opacity:0 or not. Missing
   from the first draft of this file (find.css's own copy has it) — found
   live when no map icon would select at all. */
.find-dim-layer { opacity: 0; pointer-events: none; transition: opacity 0.3s ease; }
.find-dim-layer.is-visible { opacity: 0.85; }
.find-badge-layer { opacity: 1; }
/* Background seat-plan art (zone-A1…zone-SN, same ids as seat-plan.svg,
   never given class="zone" here — not selectable, just labeled) gets a
   subtle glow on hover to pair with venue-map.js's "ที่นั่งภายในฮอลล์"
   tooltip, so the area reads as "labeled" rather than dead/inert. */
.find-map__viewport [id^="zone-"] { transition: filter 0.15s ease; }
@media (hover: hover) { .find-map__viewport [id^="zone-"]:hover { filter: brightness(1.12) drop-shadow(0 0 3px rgba(var(--white-rgb),0.35)); } }
/* Hall background (#dcf8ff) — base fade set in venue-map.svg's own <style>
   (fill-opacity: .9, a 10% fade); this only adds the hover feedback (15%)
   since :hover isn't expressible from inside a plain SVG stylesheet block
   the way this page's other interactive states are. Higher specificity
   than the SVG-internal single-class rule, so it wins on hover.
   ⚠️ Class number, not semantic — Illustrator renumbers every .cls-N on
   each fresh export (v1: cls-29, v2: cls-36, v305: cls-35), so this MUST
   be re-checked (search the SVG's own <style> block for `fill:#dcf8ff`)
   any time venue-map.svg is replaced with a newer export, even if nothing
   else about the hall floor visually changed. */
@media (hover: hover) { .find-map__viewport .cls-35:hover { fill-opacity: 0.85; } }

.vm-toolbar {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 2;
  display: flex;
  gap: 8px;
}
/* Literal dark rgba, not rgba(var(--surface-rgb)) — using --surface-rgb
   (255,255,255 in dark mode) here washed these out into pale white-ish
   circles floating over the dark map.

   ⚠️ This literal is the DARK-MODE value only. It needs the matching
   `:root[data-theme="light"]` override below — copied from find.css:462-467,
   which is what this page was meant to be consistent with all along. The
   original comment here claimed it matched find.css "for consistency", but
   it had copied only the dark background and not the light-mode override
   that goes with it: the icon kept `var(--text-primary)`, which flips to
   near-black, so in light mode all four controls were #1A1830 on #08081F —
   measured **1.21:1**, invisible, live on production until 2026-08-21.
   Either both halves follow the theme (this rule) or neither does
   (p/lightbox.js) — never one of each. See DESIGN_SYSTEM.md §7.9. */
.vm-zoom-btn, .vm-reset-btn {
  width: 30px;
  height: 30px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: rgba(5, 5, 5, 0.85);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.vm-zoom-btn svg, .vm-reset-btn svg { width: 15px; height: 15px; }
@media (hover: hover) { .vm-zoom-btn:hover, .vm-reset-btn:hover { background: var(--bg-elevated); } }
/* Light mode: same values find.css uses for its own map toolbar, so the two
   map pages read identically. */
:root[data-theme="light"] .vm-zoom-btn,
:root[data-theme="light"] .vm-reset-btn { background: rgba(241, 237, 249, 0.85); }
@media (hover: hover) { :root[data-theme="light"] .vm-zoom-btn:hover, :root[data-theme="light"] .vm-reset-btn:hover { background: rgba(241, 237, 249, 1); } }

/* ── Sidebar ──────────────────────────────────────────────────────────── */
/* Solid token, not rgba(var(--surface-rgb)) — --surface-rgb is 255,255,255
   in dark mode (meant for small translucent accents, not a whole panel),
   so a 0.4-alpha wash here read as a milky gray panel over the dark space
   background instead of a clean dark sidebar. find.css's .find-sidebar
   uses the same solid var(--bg-secondary). */
.vm-sidebar {
  width: clamp(300px, 22vw, 380px);
  flex-shrink: 0;
  border-left: 1px solid var(--border);
  background: var(--bg-secondary);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.vm-sidebar__content { flex: 1; padding: 16px; display: flex; flex-direction: column; gap: 14px; }

.vm-title { font-size: 18px; font-weight: 700; margin: 0; }
.vm-title__icon { width: 1em; height: 1em; vertical-align: -0.15em; }
.vm-subtitle { font-size: 12px; color: var(--text-secondary); margin: 0; }

.vm-search {
  position: relative;
  display: flex;
  align-items: center;
}
.vm-search svg { position: absolute; left: 10px; width: 16px; height: 16px; color: var(--text-secondary); pointer-events: none; }
.vm-search input {
  width: 100%;
  padding: 10px 12px 10px 34px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: 14px;
}
.vm-search input::placeholder { color: var(--text-secondary); }
.vm-search-hint { font-size: 10.5px; color: var(--text-secondary); opacity: 0.75; margin: -4px 2px 0; line-height: 1.4; }

.vm-filters { display: flex; flex-wrap: wrap; gap: 6px; }
.vm-pill {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
}
/* The INVERSE of the two fixed surfaces above: here the background follows
   the theme (`--accent-teal`), so the text must follow it too. `--teal-btn-text`
   exists for exactly this pairing — it is #072626 in dark and var(--white) in
   light, because light-mode's `--accent-teal` is a dark saturated teal.
   Until 2026-08-21 this read a literal `#06232a`, which is fine on dark
   mode's bright teal (8.29:1) and only 4.01:1 on light mode's dark teal —
   under the 4.5:1 this 12px label needs. Do not re-inline a literal here. */
.vm-pill.is-active { background: var(--accent-teal); border-color: var(--accent-teal); color: var(--teal-btn-text); font-weight: 600; }

.vm-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; overflow-y: auto; }
.vm-list__item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-lg);
  border: 1px solid transparent;
  background: var(--bg-elevated);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
@media (hover: hover) { .vm-list__item:hover { filter: brightness(1.15); } }
/* --item-color/--item-color-rgb are set inline per button by venue-map.js
   (renderList()) from that item's own VENUE_ITEMS.color — per-item, not
   per-category (two items in "ทั่วไป" can be different colors on the
   source floor plan), and matches the glow/outline the same item gets on
   the map, so the sidebar row and the map icon read as one selection. */
.vm-list__item.is-active {
  border-color: var(--item-color, var(--accent-teal));
  background: rgba(var(--item-color-rgb, var(--accent-teal-rgb)), 0.14);
}
.vm-list__dot { width: 9px; height: 9px; border-radius: var(--radius-full); flex-shrink: 0; }
.vm-list__name { font-size: 13.5px; font-weight: 600; }
.vm-list__empty { font-size: 13px; color: var(--text-secondary); text-align: center; padding: 20px 0; }

/* ── Detail card — floats snug against the map's bottom-left corner, not
   the sidebar (2026-08-20 ask, moved here from right-center per a later
   ask) — it describes the thing you just picked on the map, so it reads
   better anchored to the map than at the bottom of a scrolling list. */
/* ⚠️ The background literal below is the DARK-MODE value only — it needs the
   `:root[data-theme="light"]` override underneath, the same way find.css
   re-pins its own floating map panels (.find-legend/.find-toolbar) in light
   mode. Until 2026-08-21 there was no override: the card stayed
   rgba(8,8,24,0.9) in light while `__name` inherited `--text-primary` and
   `__desc`/`__nearby`/`__close` read `--text-secondary`, so those flipped to
   near-black ink on a near-black card — **1.05:1** for the booth name (i.e.
   invisible) and 2.44:1 for the rest, live on production. Either both halves
   follow the theme (this rule) or neither does (p/lightbox.js), never one of
   each. See DESIGN_SYSTEM.md §7.9. */
.vm-detail {
  position: absolute;
  left: 12px;
  bottom: 12px;
  z-index: 3;
  width: min(280px, calc(100% - 24px));
  border: 1px solid var(--item-color, var(--border));
  border-radius: var(--radius-xl);
  padding: 14px 34px 14px 14px;
  background: rgba(5, 5, 5, 0.9);
  backdrop-filter: blur(6px);
}
:root[data-theme="light"] .vm-detail { background: rgba(241, 237, 249, 0.92); }
.vm-detail__close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.vm-detail__close svg { width: 18px; height: 18px; }
@media (hover: hover) { .vm-detail__close:hover { color: var(--text-primary); } }
.vm-detail__name { font-size: 15px; font-weight: 700; margin: 0 0 4px; }
.vm-detail__desc { font-size: 13px; color: var(--text-secondary); margin: 0; line-height: 1.5; }
.vm-detail__nearby { list-style: disc; margin: 8px 0 0; padding-left: 14px; font-size: 10px; color: var(--text-secondary); }

/* ── Hover tooltip — this page's own equivalent of find.js's .find-tooltip
   (both SVG-hover-driven, the documented exception to attachTooltip()'s
   fixed element→text API). Cursor-follow, positioned by venue-map.js. */
/* Same fixed-surface pairing as .vm-detail and the toolbar buttons above —
   the literal background is the dark-mode value and needs the light-mode
   override below, or `--text-primary`/`--border` flip to near-black ink on
   a near-black tooltip. Found 2026-08-21 by scripts/check-fixed-surface.mjs
   on its first run, in the same file as the two bugs that prompted writing
   it — the hover tooltip only appears while the cursor is over a map icon,
   which is why three rounds of manual light-mode checking never caught it. */
.vm-tooltip {
  position: absolute;
  z-index: 4;
  pointer-events: none;
  background: rgba(5, 5, 5, 0.92);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 5px 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
}
:root[data-theme="light"] .vm-tooltip { background: rgba(241, 237, 249, 0.95); }

/* Thai script carries no spaces, so the browser's line-breaking can insert
   a break mid-word inside a single link's own label when this narrow
   sidebar can't fit it on one line — reads as "ตกคำ" (a garbled word
   break). white-space:nowrap keeps each link's full text as one unbreakable
   unit; the row itself still wraps normally between links/separators, so a
   link that doesn't fit drops to its own new line whole, never split. */
.vm-sidebar .lg-footer-link { padding: 14px 16px 20px; font-size: 11.5px; }
.vm-sidebar .lg-footer-link a { white-space: nowrap; }

/* Mobile-only "scroll for details" hint — hidden on desktop, where map and
   sidebar sit side by side and there's nothing to scroll to. Same visual
   pattern as find.css's .find-scroll-hint. */
.vm-scroll-hint { display: none; }

/* Phase-disabled card (map_interactive = false) — mirrors md.css's
   .md-coming-soon; this page's own .vm-page never mounts in that state, so
   the card needs its own centring rather than inheriting the map layout. */
.vm-disabled {
  max-width: 560px; margin: 0 auto; text-align: center; color: var(--text-secondary);
  font-size: 15px; padding: 60px 24px; background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: var(--radius-3xl);
}

@media (max-width: 900px) {
  .vm-page { flex-direction: column; height: auto; }
  /* Capped so the sidebar below always keeps 320px (2026-08-27) — same
     reasoning and same shape as p/find.css's .find-map rule: 60% of a short
     viewport left the search field 24-52px from the bottom edge (measured at
     540 / 609px), which is inside the band an in-app browser parks its own
     floating controls in. The X app on Android opens the site as a bottom
     sheet and overlays a back / URL-pill / reload row on top of the page
     without shrinking the viewport, and env(safe-area-inset-bottom) reads 0
     there, so the page cannot measure the overlay — the fix is to keep the
     search field out of that band. min() rather than a max-height media
     query so there is no threshold to guess and no jump right above it: the
     gap holds at 128px flat across viewport heights 540 / 609 / 701, and
     800px (a real phone viewport) is untouched at 60%. */
  /* 55%, against /p/find's 70% (2026-08-27). Same formula, different ratio —
     the ratio is a per-page decision, the formula is not.
     This page's sidebar stacks a hint line above its search field, so the same
     ratio does not fit. The binding constraint is not the search field itself
     (the owner accepted it sitting close to the bar) but the floating scroll
     hint, which must keep an equal, comfortable gap on every device and
     therefore cannot be allowed to land on top of the field. Measured, gap
     between search field and hint: 65% → -26px Android / -42px iPhone
     (overlapping), 60% → +6 / -14 (still overlapping on iPhone), 55% → +37 /
     +15 (clear on both). 55% is the highest ratio where the hint clears the
     field on every in-app device. */
  .vm-map { flex: none; width: 100%; height: 55vh; height: clamp(220px, calc((100dvh - var(--inapp-reserve)) * 0.55), 55dvh); }

  .vm-sidebar { width: 100%; border-left: none; border-top: 1px solid var(--border); }

  /* Detail card 10% smaller on mobile (on request) — width and internal
     text scaled together so it reads as a smaller card, not a squeezed one. */
  .vm-detail { width: min(252px, calc(100% - 24px)); padding: 12.6px 30.6px 12.6px 12.6px; }
  .vm-detail__name { font-size: 13.5px; }
  .vm-detail__desc { font-size: 11.7px; }
  .vm-detail__nearby { font-size: 9px; }

  .vm-scroll-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    position: fixed;
    left: 50%;
    bottom: calc(16px + var(--safe-bottom) + var(--inapp-bottom));
    transform: translateX(-50%);
    padding: 8px 16px;
    border: 1px solid rgba(var(--surface-rgb),0.3);
    border-radius: var(--radius-pill);
    background: rgba(var(--surface-rgb),0.14);
    backdrop-filter: blur(16px) saturate(1.4);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
    box-shadow: 0 6px 18px rgba(var(--shadow-rgb), calc(0.35 * var(--shadow-mult)));
    color: var(--text-primary);
    font: 600 12px var(--font-thai);
    cursor: pointer;
    z-index: 50;
    opacity: 1;
    transition: opacity 0.3s ease;
  }
  .vm-scroll-hint.is-hidden { opacity: 0; pointer-events: none; }
  .vm-scroll-hint__arrow { display: inline-block; animation: vm-scroll-hint-bounce 1.4s ease-in-out infinite; }
}
@keyframes vm-scroll-hint-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(3px); }
}

/* Red-fog theme (events/jasper-temptation-2026, 2026-10-12): the map's floating
   controls, detail card and tooltip were navy / lavender literals. Neutral here,
   scoped so PPW is untouched. Marker dot colours are map data and stay. */
:root[data-motif-pattern="red-fog"][data-project] .vm-zoom-btn,
:root[data-motif-pattern="red-fog"][data-project] .vm-reset-btn { background: rgba(10, 10, 10, 0.85); }
:root[data-motif-pattern="red-fog"][data-project] .vm-detail { background: rgba(10, 10, 10, 0.9); }
:root[data-motif-pattern="red-fog"][data-project] .vm-tooltip { background: rgba(10, 10, 10, 0.92); }
:root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-zoom-btn,
:root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-reset-btn { background: rgba(247, 245, 245, 0.85); }
@media (hover: hover) { :root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-zoom-btn:hover, :root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-reset-btn:hover { background: rgba(247, 245, 245, 1); } }
:root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-detail { background: rgba(247, 245, 245, 0.92); }
:root[data-motif-pattern="red-fog"][data-project][data-theme="light"] .vm-tooltip { background: rgba(247, 245, 245, 0.95); }
