/* find.css — /p/find (Find My JASPER). Design source: MEMBERSHIP_REQUIREMENTS §7 / §15.6-7.
   Phase 2A scope: map engine (inject/zoom/pan/reset), round selector, zone
   states, claim badges. Sidebar content is a placeholder until Phase 2B. */

* { 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); }
/* Form controls don't inherit font-family from the page by default (browser
   UA stylesheet gives them their own system font) — without this, any
   button/input/select added here silently falls back off Noto Sans Thai.
   Fixes it everywhere at once instead of patching each selector by hand
   (the seat buttons and zoom/reset controls were the ones actually missing
   it before this rule existed). */
button, input, select, textarea { font: inherit; color: inherit; }

/* ── Space-vibe background, scoped to the map only (2026-07-10 ask;
   relocated off the page background to .find-map, 2026-07-10 follow-up) —
   base .p-star/.p-shooting-star rules live in tokens.css (shared site-wide);
   this overrides position:fixed -> absolute since mountStarfield(mapWrap)
   (find.js) mounts these as children of .find-map (position:relative/
   overflow:hidden), not document.body. Wins on specificity (adds .is-scoped)
   over tokens.css's unscoped rule regardless of stylesheet load order. */
#p-starfield.is-scoped, #p-shooting-stars.is-scoped { position: absolute; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }

/* Pause the twinkling/shooting-star animations while the zoomed-in seat
   view's frosted-glass overlay (.find-seatplan, below) is showing
   (2026-07-11 perf fix). Measured impact: on Find My JASPER specifically,
   backdrop-filter forces the browser to re-sample everything beneath it
   every frame it's visible — continuously-animating stars underneath add to
   that per-frame cost for zero visual payoff, since they're already
   unreadable under the panel's blur + dark tint. Confirmed via rAF frame
   timing: 59fps/0 dropped frames with the map alone vs 54fps/multiple
   dropped frames once the frosted panel was open — pausing this doesn't
   change how anything looks, only stops wasted work under the hood. */
.find-map:has(.find-seatplan.is-visible) .p-star,
.find-map:has(.find-seatplan.is-visible) .p-shooting-star { animation-play-state: paused; }

.find-page {
  display: flex;
  /* dvh (not vh) — vh includes the area behind mobile browsers' collapsible
     address bar, so this fixed-height full-screen page overflowed by that
     much and forced an extra outer scroll (2026-07-08 fix). */
  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);
}

/* ── Phase-disabled state (§7.11/§12, §15.17) ────────────────────────────── */
.find-disabled {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  max-width: 360px;
  margin: 0 auto;
  padding: 40px 24px;
  height: calc(100vh - var(--navbar-total-h));
  height: calc(100dvh - var(--navbar-total-h));
  justify-content: center;
}
/* Legal footer fallback for the disabled state — no sidebar exists here to
   host it, so it falls back to page level with its own symmetric spacing. */
#find-root > .lg-footer-link { margin-top: 20px; padding-bottom: 20px; }
.find-disabled__icon {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-full);
  border: 2px solid var(--accent-gold);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  background: rgba(var(--accent-gold-rgb),0.08);
}
.find-disabled__title { font-size: 18px; font-weight: 700; }
.find-disabled__subtext { font-size: 13px; color: var(--text-secondary); }
.find-disabled__chip {
  margin-top: 6px;
  font-size: 11px;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 4px 12px;
}

/* ── Map area ─────────────────────────────────────────────────────────── */
.find-map {
  position: relative;
  flex: 1;
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  /* Same red --space-gradient as .find-page (owner 2026-10-23) — the old
     opaque bg-primary + teal glow hid the page's red entirely. */
  background: var(--space-gradient), var(--bg-primary);
  background-repeat: no-repeat;
}
.find-map.is-panning { cursor: grabbing; }

.find-map__viewport {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}
/* will-change only while actively panning/zooming/animating — GPU caches a
   bitmap of the layer at this point and just scales that texture for
   subsequent transforms, which is what causes visible blur at high zoom.
   Dropping the hint once interaction settles forces a normal, crisp
   re-render of the underlying vector paths. */
.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); } }
.find-map__viewport .zone.is-active path {
  stroke: rgb(var(--surface-rgb));
  stroke-width: 3;
  paint-order: stroke;
}

.find-dim-layer {
  /* var(--bg-primary), not pure #000000 (2026-07-11 fix) — this rect covers
     the ENTIRE SVG canvas (viewBox), including the blank space outside the
     venue silhouette. At the overview map's normal zoom that blank space is
     a sliver, easy to miss; zoomed into any zone near an edge/corner (where
     most of the visible viewport IS that blank space), pure black filled
     most of the screen and read as a broken/opaque background instead of
     an intentional dim effect. --bg-primary matches .find-map's own
     background, so the same dim effect now blends with the app's real
     theme instead of clashing as a flat black slab. */
  fill: var(--bg-primary);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.find-dim-layer.is-visible { opacity: 0.85; }
/* pointer-events:none — the count badge is a label painted OVER its zone but
   OUTSIDE the zone's own <g>, so without this it wins the hit-test and
   map.js's `e.target.closest('.zone')` resolves to null: a tap landing on the
   number itself silently did nothing (and hover was swallowed the same way).
   Sibling .find-dim-layer above already carries this for the same reason. */
.find-badge-layer { transition: opacity 0.3s ease; pointer-events: none; }

/* Zone-name layer of seat-plan.svg faded so the claim-count badges painted
   over it stay readable (owner 2026-10-20). Scoped to /p/find only — the
   homepage uses the same SVG at full strength. */
.find-map__viewport [data-decor="zone_label"] { opacity: 0.15; }

.find-badge text {
  /* fill set per-badge in map.js (black/white by box luminance) */
  font-family: var(--font-thai);
  font-size: 14px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}

/* ── Seat-plan floating panel — an independent HTML overlay covering the
   whole map area (not bound to the macro map's own pan/zoom transform).
   Carries its own mini pan/zoom engine for its (potentially large) seat
   grid. ─────────────────────────────────────────────────────────────── */
.find-seatplan {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: flex;
  flex-direction: column;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
  /* Frosted-glass effect (2026-07-11 ask) — the underlying full-venue map
     must stay visible (not swapped for an opaque background) but blurred,
     like a sheet of frosted glass laid over it. Reverted to the original
     rgba(15,15,30,0.55) tint ("current level of opacity" per the ask) —
     an earlier attempt made this opaque instead, which hid the map
     entirely; the actual bug was the missing blur, not the tint's alpha. */
  background: rgba(15, 15, 30, 0.55);
  /* 8px (was 12px, 2026-07-11 perf fix) — backdrop-filter is one of the
     more expensive CSS properties (continuous per-frame re-sampling of
     everything beneath it); a smaller radius costs less to compute while
     still reading as "frosted" at this panel's scale. */
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  overflow: hidden;
}
.find-seatplan.is-visible { opacity: 1; pointer-events: auto; }
/* Light mode (owner 2026-11-02): the dark tint above made the seat grid
   unreadable on the light theme — same light glass as the map-overlay
   family (.find-legend etc., see the light-mode block below). */
:root[data-theme="light"] .find-seatplan { background: rgba(241, 237, 249, 0.6); }

/* Zone seat-data spinner (2026-10-23) — centred over the map, fades in/out. */
.find-zone-loading {
  position: absolute; inset: 0; z-index: 5; padding: 0;
  opacity: 0; pointer-events: none; transition: opacity 0.35s ease;
}
.find-zone-loading.is-visible { opacity: 1; }

/* Now rendered as the first child of .find-seatplan__content, not a fixed
   strip above .find-seatplan__viewport (2026-07-10 ask) — it pans/zooms
   together with the seat grid like part of the venue floor plan, instead of
   staying pinned to the viewport's top regardless of pan/zoom. The old
   76px top padding was a position anchor clearing the back-button/zone-chip
   overlays above the old fixed strip; that concern doesn't apply once it's
   inside the scrollable content, so it's now just a small gap matching
   .find-seatplan__content's own 8px top padding, plus a bit more before the
   first seat row. */
.find-seatplan__stage { text-align: center; padding: 0 0 12px; }
.find-seatplan__stage-label {
  color: var(--like-red);
  font-weight: 700;
  /* 20% smaller (2026-07-10 ask): 11px -> 8.8px */
  font-size: 8.8px;
  letter-spacing: 0.25em;
}
.find-seatplan__stage-arc {
  /* 20% smaller (2026-07-10 ask): 70% -> 56%, 14px -> 11.2px, 6px -> 4.8px,
     3px -> 2.4px */
  width: 56%;
  height: 11.2px;
  margin: 4.8px auto 0;
  border-top: 2.4px solid var(--like-red);
  border-radius: var(--radius-full);
  opacity: 0.85;
}

.find-seatplan__viewport {
  flex: 1;
  position: relative;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
}
.find-seatplan__viewport.is-panning { cursor: grabbing; }
.find-seatplan__content {
  position: absolute;
  top: 0;
  left: 0;
  padding: 8px 16px;
  transform-origin: 0 0;
}
/* Same hybrid as .find-map__viewport — will-change only while interacting,
   so the resting view always re-renders crisp instead of scaling a cached
   GPU texture. */
.find-seatplan__content.is-interacting { will-change: transform; }
/* Brief eased transition for the double-click/double-tap zoom-to-seat
   camera move (2026-07-10 ask) — manual wheel/pinch/drag stays instant. */
.find-seatplan__content.is-animating { transition: transform 0.4s ease; }

.find-seatplan__row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.find-seatplan__row-label {
  width: 30px;
  flex-shrink: 0;
  text-align: right;
  font-size: 11px;
  color: rgba(var(--surface-rgb),0.45);
}
.find-seatplan__row-label:last-child { text-align: left; }
.find-seatplan__row-cells { display: grid; gap: 8px; }

.find-seat {
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  appearance: none;
  cursor: pointer;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  background: transparent;
  border: 1px solid transparent;
  padding: 0;
}
.find-seat__emoji { font-size: 1.39755em; display: flex; align-items: center; justify-content: center; line-height: 0; }
/* Pulsing placeholder while the seat's avatar emoji SVG is still in flight
   (emoji-icons.js's onload adds .is-loaded once it arrives) -- makes the
   otherwise-empty circle read as "loading" instead of just blank
   (2026-08-14). */
.find-seat__emoji:has(img.emoji-icon:not(.is-loaded)) {
  width: 0.75em;
  height: 0.75em;
  border-radius: var(--radius-full);
  background: rgba(var(--surface-rgb), 0.3);
  animation: find-emoji-pulse 1.1s ease-in-out infinite;
}
@keyframes find-emoji-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.8; }
}
.find-seat--available { opacity: 0.55; }
.find-seat__number { font-size: 10.35px; font-weight: 700; color: var(--fixed-bg-1); pointer-events: none; }
.find-seat--pending { opacity: 0.7; }
.find-seat--unverified { background: transparent !important; border-width: 3px; }
.find-seat.is-selected { box-shadow: 0 0 0 2px rgb(var(--surface-rgb)); }
.find-seat__badge {
  position: absolute;
  top: -3px;
  width: 13px;
  height: 13px;
  border-radius: var(--radius-full);
  background: var(--white);
  color: var(--fixed-bg-1);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 7px;
}
.find-seat__badge--pending { right: -3px; }
.find-seat__badge--forsale { left: -3px; background: #ffe33e; }
/* Health Status Check-In badge (§4.1) — same top-left slot as ⚡ for-sale
   (2026-07-26 decision: WTS listings don't exist post-concert, no real
   collision to design around). Per-status circle color; "other" (💊) gets
   a dedicated purple not used anywhere else on the site — no existing token
   covers it (approved 2026-07-26). */
.find-seat__badge--health { left: -3px; }
.find-seat__badge--health-covid { background: var(--like-red); }
.find-seat__badge--health-flu { background: var(--accent-gold); }
.find-seat__badge--health-other { background: #8C8C8C; }
:root[data-theme="light"] .find-seat__badge--health-covid { background: var(--status-like); }
:root[data-theme="light"] .find-seat__badge--health-flu { background: var(--brand-gold); }

/* Assist-needs seat badge (2026-08-18) — its own bottom-left slot, NOT the
   top-left ⚡ for-sale/Health slot: unlike Health (post-concert only), this
   feature is active pre-concert too, so it can genuinely coexist with a
   real for-sale listing on the same seat and needs its own spot. */
.find-seat__badge--assist { left: -3px; top: auto; bottom: -3px; background: var(--accent-gold); }

/* Admin-added health report marker (§4.1 Decision #11/#13/#19) — no ticket
   behind it, so it fully replaces the normal seat treatment: FontAwesome
   "user" glyph on slate fill instead of a price-tier/identity color. */
.find-seat--manual-report { background: #4A5578 !important; border-color: #4A5578 !important; }
.find-seat__manual-icon { width: 60%; height: 60%; color: var(--white); display: flex; align-items: center; justify-content: center; }
.find-seat__manual-icon svg { width: 100%; height: 100%; }

.find-seat--mini { background: rgba(var(--surface-rgb),0.05); border: 1px solid rgba(var(--surface-rgb),0.15); }
.find-seat__dots { display: flex; gap: 2px; }
.find-seat__dot { width: 5px; height: 5px; border-radius: var(--radius-full); background: rgba(var(--surface-rgb),0.2); }
.find-seat__dot.has-ticket { background: var(--accent-teal); }
:root[data-theme="light"] .find-seat__dot.has-ticket { background: var(--brand-teal); }

.find-legend {
  position: absolute;
  bottom: 14px;
  left: 14px;
  z-index: 9;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  max-width: min(calc(100% - 80px), 520px);
  row-gap: 4px;
  column-gap: 10px;
  /* More transparent + stronger blur (2026-07-10 ask) — was a near-opaque
     0.9 alpha that read as a solid tile sitting on the map rather than a
     glassy overlay. */
  background: rgba(5, 5, 5, 0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 7px 11px;
  font-size: 10px;
  /* Theme-following (2026-08-08 revert) — this whole map-overlay family
     (.find-legend/.find-zone-back-btn/.find-zone-chip/.find-reset-btn/
     .find-zoom-btn/.find-toolbar/.find-floating-cta) used to be pinned
     permanently dark regardless of data-theme, on the theory it had to
     stay legible over the colorful seat-map SVG. In light mode that read
     as a leftover dark-mode block instead of an intentional overlay, so
     it now follows the theme like every other surface — see the
     `:root[data-theme="light"]` block below for the light-mode fills. */
  color: var(--text-secondary);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.find-legend__item { display: flex; align-items: center; gap: 4px; white-space: nowrap; }
.find-legend__dot { width: 11px; height: 11px; border-radius: var(--radius-full); flex-shrink: 0; }
.find-legend__dot--verified { background: var(--accent-teal); } /* same family as the other legend dots (owner 2026-11-02); the seat panel's green tag stays */
.find-legend__dot--pending { background: var(--accent-teal); opacity: 0.7; }
.find-legend__dot--unverified { background: transparent; border: 2px solid var(--accent-teal); }
.find-legend__dot--available { background: transparent; border: 1px solid rgba(var(--surface-rgb),0.5); opacity: 0.6; }

.find-zone-back-btn {
  position: absolute;
  top: 16px;
  left: 16px;
  z-index: 10;
  appearance: none;
  border: 1px solid var(--border);
  background: rgba(5, 5, 5, 0.85);
  /* Theme-following (2026-08-08 revert) — see .find-legend above. */
  color: var(--text-primary);
  border-radius: var(--radius-lg);
  padding: 8px 14px;
  font-family: var(--font-thai);
  font-size: 12px;
  cursor: pointer;
}

.find-zone-chip {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 10;
  background: rgba(5, 5, 5, 0.85);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-xl);
  padding: 6px 18px;
  font-family: var(--font-thai);
  text-align: center;
  line-height: 1.2;
}
/* Theme-following (2026-08-08 revert) — see .find-legend above. */
.find-zone-chip__label { font-size: 11px; font-weight: 500; color: var(--text-secondary); }
.find-zone-chip__code { font-size: 24px; font-weight: 800; color: var(--text-primary); }

.find-reset-btn, .find-zoom-btn {
  position: absolute;
  right: 16px;
  z-index: 10;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  border: 1px solid var(--border);
  background: rgba(5, 5, 5, 0.85);
  color: var(--text-primary);
  cursor: pointer;
}
/* Icon sizing (2026-07-10 — SVG replaced the ＋／－／⤢ Unicode glyphs, which
   read as too small/faint at these button sizes). */
.find-reset-btn svg, .find-zoom-btn svg { width: 18px; height: 18px; }
/* :hover switches the background to the theme-following --bg-elevated (a
   deliberate "lights up on hover" affordance) — unlike the resting state,
   this one DOES need theme-following text, or white-on-white-elevated
   breaks in light mode on hover. */
@media (hover: hover) { .find-reset-btn:hover, .find-zoom-btn:hover { background: var(--bg-elevated); color: var(--text-primary); } }
.find-reset-btn { bottom: 16px; }
.find-zoom-btn--out { bottom: 64px; }
.find-zoom-btn--in { bottom: 112px; }

/* ── Map toolbar (round/day selector + reset) ───────────────────────────── */
.find-toolbar {
  position: absolute;
  top: 16px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 10;
  /* More transparent + stronger blur (2026-07-10 ask), matching the same
     treatment applied to .find-legend below. */
  background: rgba(5, 5, 5, 0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  padding: 6px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
/* Light-mode fills for the whole map-overlay family above (.find-legend,
   .find-zone-back-btn, .find-zone-chip, .find-reset-btn/.find-zoom-btn,
   .find-toolbar, .find-floating-cta below) — same alpha as each dark rule,
   swapped to --bg-elevated's light RGB (241,237,249) so these read as a
   light glass panel instead of a stray dark-mode block. Text on all of
   them already uses var(--text-primary)/var(--text-secondary), so it
   switches automatically once the background does. */
:root[data-theme="light"] .find-legend,
:root[data-theme="light"] .find-toolbar { background: rgba(241, 237, 249, 0.6); }
:root[data-theme="light"] .find-zone-back-btn,
:root[data-theme="light"] .find-zone-chip,
:root[data-theme="light"] .find-reset-btn,
:root[data-theme="light"] .find-zoom-btn { background: rgba(241, 237, 249, 0.85); }
:root[data-theme="light"] .find-floating-cta { background: rgba(241, 237, 249, 0.92); }
.find-round-tab {
  appearance: none;
  border: none;
  background: transparent;
  /* Theme-following (2026-08-08 revert) — sits directly on .find-toolbar's
     background, which now follows data-theme too (see .find-legend). */
  color: var(--text-secondary);
  border-radius: var(--radius-lg);
  padding: 6px 14px;
  cursor: pointer;
  text-align: center;
  line-height: 1.2;
  font-family: var(--font-thai);
}
.find-round-tab__main { display: block; font-size: 13px; font-weight: 700; }
.find-round-tab__sub { display: block; font-size: 10px; opacity: 0.8; }
.find-round-tab.is-active {
  background: var(--accent-gold);
  color: var(--gold-btn-text);
}
.find-round-tab.is-active .find-round-tab__main,
.find-round-tab.is-active .find-round-tab__sub { color: var(--gold-btn-text); }
/* Keeps dark mode's vivid gold + dark text in light mode too (2026-08-08 ask)
   — solid fill + in-box text has self-contained contrast, independent of
   the page background, so light mode's muted gold isn't needed here. */
:root[data-theme="light"] .find-round-tab.is-active,
:root[data-theme="light"] .find-round-tab.is-active .find-round-tab__main,
:root[data-theme="light"] .find-round-tab.is-active .find-round-tab__sub { background: var(--brand-gold); color: var(--brand-gold-ink); }

.find-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 20;
  pointer-events: none;
  white-space: nowrap;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  box-shadow: 0 4px 12px rgba(var(--shadow-rgb), calc(0.5 * var(--shadow-mult)));
  border-radius: var(--radius-sm);
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-primary);
}
.find-tooltip[hidden] { display: none; }

/* ── Sidebar / panel (§15.6 Seat Summary) ────────────────────────────────── */
.find-sidebar {
  width: 380px;
  flex-shrink: 0;
  background: var(--bg-secondary);
  border-left: 1px solid var(--border);
  position: relative;
  display: flex;
  flex-direction: column;
}
.find-sidebar__content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--surface-rgb),0.18) transparent;
}
.find-sidebar__content::-webkit-scrollbar { width: 8px; }
.find-sidebar__content::-webkit-scrollbar-track { background: transparent; }
.find-sidebar__content::-webkit-scrollbar-thumb {
  background: rgba(var(--surface-rgb),0.18);
  border-radius: var(--radius-xs);
}
@media (hover: hover) { .find-sidebar__content::-webkit-scrollbar-thumb:hover { background: rgba(var(--surface-rgb),0.3); } }

/* Legal footer — sits below the scrollable panel content, as a persistent
   strip at the end of the sidebar (not inside the scroll area, so it's
   never cleared by panel.js's re-renders). Slimmer than other pages'
   footer (2026-07-09 ask): flat 10px top/bottom, ~10% smaller text and
   side padding, since the panel has less room to spare. */
.find-sidebar .lg-footer-link {
  flex-shrink: 0;
  padding: 10px 14px;
  font-size: 10px;
  border-top: 1px solid var(--border);
}

.find-panel__head { font-size: 16px; font-weight: 700; }

.find-panel__search-wrap {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}
.find-panel__search-icon { font-size: 13px; flex-shrink: 0; }
.find-panel__search {
  flex: 1;
  min-width: 0;
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
  color: var(--text-primary);
  font-family: var(--font-thai);
  font-size: 13px;
}
.find-panel__search:focus { outline: none; }
.find-panel__search-wrap:focus-within { border-color: var(--focus-ring); box-shadow: 0 0 0 3px rgba(var(--accent-gold-rgb),0.12); }
.find-panel__search::placeholder { color: var(--text-secondary); }

/* ── Search dropdown (live suggestions) ─────────────────────────────────── */
.find-search-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 5;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* Capped + scrollable (2026-08-18) — unbounded, this could grow past
     600px (8 username groups + 8 seat matches + the "ดูทั้งหมด" button)
     and float straight over the round-tab/venue-bar buttons right below
     the search box, which reads as those buttons "colliding" with it. */
  max-height: 280px;
  overflow-y: auto;
  box-shadow: 0 8px 20px rgba(var(--shadow-rgb), calc(0.4 * var(--shadow-mult)));
}
.find-search-dropdown__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 13px;
}
@media (hover: hover) { .find-search-dropdown__item:hover { background: rgba(var(--surface-rgb),0.06); } }
.find-search-dropdown__item + .find-search-dropdown__item { border-top: 1px solid var(--border); }
.find-search-dropdown__name { font-weight: 600; }
.find-search-dropdown__count { font-size: 11px; color: var(--text-secondary); flex-shrink: 0; }
.find-search-dropdown__viewall {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 8px 12px;
  border: none;
  border-top: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.03);
  color: var(--accent-teal);
  font-family: var(--font-thai);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  cursor: pointer;
}
@media (hover: hover) { .find-search-dropdown__viewall:hover { background: rgba(var(--surface-rgb),0.08); } }
.find-search-dropdown__viewall-arrow { display: flex; font-size: 11px; margin-left: 2px; }

/* ── Search results panel (Enter key / ambiguous group) ──────────────────── */
.find-search-results { display: flex; flex-direction: column; gap: 8px; }
.find-search-result {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: var(--radius-lg);
  cursor: pointer;
  border: 1px solid transparent;
}
@media (hover: hover) { .find-search-result:hover { background: rgba(var(--surface-rgb),0.04); } }
.find-search-result__swatch { width: 10px; height: 10px; border-radius: var(--radius-full); flex-shrink: 0; }
.find-search-result__main { flex: 1; min-width: 0; }
.find-search-result__seat { font-size: 13px; font-weight: 600; }
.find-search-result__meta { font-size: 11px; color: var(--text-secondary); margin-top: 2px; }

.find-panel__venue-row {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.find-count-line__owned { font-weight: 700; font-size: 14.4px; }
.find-count-line__total { color: var(--text-primary); font-weight: 600; font-size: 12px; }
.find-panel__bar, .find-zone-row__bar {
  height: 6px;
  border-radius: var(--radius-xs);
  background: rgba(var(--surface-rgb),0.08);
  overflow: hidden;
}
.find-panel__bar-fill, .find-zone-row__bar-fill {
  height: 100%;
  border-radius: var(--radius-xs);
}
/* Overview summary bar matches its own count text color (--accent-gold);
   per-zone row bars keep their tier color (set inline per row) with teal
   as the fallback default. */
.find-panel__bar-fill { background: var(--accent-gold); }
.find-zone-row__bar-fill { background: var(--accent-teal); }
:root[data-theme="light"] .find-panel__bar-fill { background: var(--brand-gold); }
:root[data-theme="light"] .find-zone-row__bar-fill { background: var(--brand-teal); }

.find-panel__daytabs { display: flex; gap: 6px; }
.find-daytab {
  flex: 1;
  appearance: none;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-radius: var(--radius-md);
  padding: 6px 4px;
  font-size: 12px;
  font-family: var(--font-thai);
  cursor: pointer;
}
.find-daytab.is-active { background: var(--accent-gold); border-color: var(--accent-gold); color: var(--gold-btn-text); font-weight: 700; }
/* Keeps dark mode's vivid gold + dark text in light mode too (2026-08-08 ask)
   — solid fill + in-box text has self-contained contrast, independent of
   the page background, so light mode's muted gold isn't needed here. */
:root[data-theme="light"] .find-daytab.is-active { background: var(--brand-gold); border-color: var(--brand-gold); color: var(--brand-gold-ink); }

.find-panel__group-label {
  font-size: 11px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 6px 0 2px;
}
.find-panel__empty { font-size: 12px; color: var(--text-secondary); padding: 8px 0; }

/* Health Status Check-In — sidebar default-state takeover (§4.2).
   Restructured 2026-08-26 from a zone-first accordion to illness -> zone ->
   seat: summary tiles, then one card per illness whose zone chips filter
   that card only. Each card sets --hc/--hc-rgb once and every tinted piece
   inside reads it, so the illness colour is declared in exactly one place
   per illness and still flips with the theme (the source tokens do). */
.find-health-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 8px; }
.find-health-tile {
  display: block; width: 100%;
  background: var(--bg-elevated);
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  padding: 8px 6px;
  text-align: center;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
@media (hover: hover) { .find-health-tile:hover:not(:disabled) { border-color: rgba(var(--surface-rgb), 0.18); } }
/* Disabled = this illness has no card to scroll to (count 0, e.g. while a
   zone search is narrowing the list) — dimming it is what tells the reader
   the tile is inert rather than unresponsive. */
.find-health-tile:disabled { cursor: default; opacity: 0.55; }
.find-health-tile__num { font-size: 19px; font-weight: 700; line-height: 1.2; }
.find-health-tile__label { font-size: 11px; color: var(--text-secondary); }
.find-health-tile--covid .find-health-tile__num { color: var(--like-red); }
.find-health-tile--flu .find-health-tile__num { color: var(--accent-gold-text); }
.find-health-tile--other .find-health-tile__num { color: var(--accent-teal-text); }

.find-health-cards { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.find-health-card {
  background: var(--bg-elevated);
  border-left: 3px solid var(--hc);
  /* Square on purpose — a single-sided border with rounded corners leaves
     the accent stripe floating off the card's edge (DESIGN_SYSTEM §7). */
  border-radius: 0;
  padding: 9px 10px;
}
.find-health-card--covid { --hc: var(--like-red); --hc-rgb: var(--like-red-rgb); }
.find-health-card--flu { --hc: var(--accent-gold); --hc-rgb: var(--accent-gold-rgb); }
.find-health-card--other { --hc: var(--accent-teal-text); --hc-rgb: var(--accent-teal-rgb); }
.find-health-card__head { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.find-health-card__title { flex: 1; font-size: 13px; font-weight: 700; color: var(--text-primary); }
.find-health-card__meta { font-size: 11px; color: var(--text-secondary); }

.find-health-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.find-health-chip {
  display: flex; align-items: center; gap: 5px;
  padding: 4px 8px;
  border-radius: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font: 500 11px var(--font-thai);
  cursor: pointer;
}
@media (hover: hover) { .find-health-chip:hover { border-color: var(--hc); } }
.find-health-chip.is-active { background: rgba(var(--hc-rgb), 0.16); border-color: var(--hc); color: var(--text-primary); }
.find-health-chip__zone { font-weight: 700; }
/* Faint by default, weight left at normal (2026-08-26 ask) — the count is
   secondary to the zone code beside it, and the same number is restated by
   the seat list below, so it reads as a hint rather than a second label.
   rgba(--surface-rgb) rather than a text token: it is dark ink in light mode
   and white in dark, so one declaration is faint in both (same idiom as
   .find-zone-row__count-num above). */
.find-health-chip__count { font-weight: 400; color: rgba(var(--surface-rgb), 0.45); }
/* The alpha is NOT symmetric between themes: measured on the real chip,
   0.45 reads 4.49:1 in dark but 2.79:1 in light, because white-over-dark and
   dark-over-light do not fade at the same rate. 0.6 in light lands at
   4.27:1 — still fainter than the --text-secondary this replaced (4.68:1)
   but without the light-mode outlier. */
:root[data-theme="light"] .find-health-chip__count { color: rgba(var(--surface-rgb), 0.6); }
.find-health-chip.is-active .find-health-chip__count { color: var(--hc); }

.find-health-card__body { border-top: 1px solid var(--border); padding-top: 4px; }
.find-health-zonehead { font-size: 11px; color: var(--text-secondary); padding: 5px 0 3px; }
.find-health-seat { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-primary); padding: 4px 0 4px 10px; }
.find-health-seat__line { flex: 1; }
/* An admin report that names only a zone. Dimmed so the eye reads the real
   seat labels above it first — it is a report about the zone, not a seat. */
.find-health-seat__noseat { color: var(--text-secondary); }
.find-health-seat__src { display: inline-flex; font-size: 13px; flex-shrink: 0; }
.find-health-seat__src--self { color: var(--accent-teal-text); }
.find-health-seat__src--manual { color: var(--accent-gold-text); }
/* Sits at the right edge of the row — .find-health-seat__line has flex:1, so
   this is pushed there without a spacer. 24px box around a 13px glyph keeps
   the tap target usable on touch without making the row taller. */
.find-health-seat__link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  flex-shrink: 0;
  margin: -5px 0;
  border-radius: var(--radius-sm, 6px);
  color: var(--text-secondary);
  font-size: 13px;
  text-decoration: none;
}
@media (hover: hover) { .find-health-seat__link:hover { color: var(--accent-gold-text); background: rgba(var(--surface-rgb), 0.08); } }

/* Always visible, not hover-only — the row icons carry a tooltip, but a
   touch device has no hover, so the icon alone must never be the only
   explanation of what it means. */
.find-health-legend {
  display: flex; flex-wrap: wrap; gap: 14px;
  margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--border);
  font-size: 11px; color: var(--text-secondary);
}
.find-health-legend__item { display: flex; align-items: center; gap: 5px; }

.find-zone-row {
  display: flex;
  gap: 10px;
  padding: 8px;
  border-radius: var(--radius-lg);
  cursor: pointer;
  border: 1px solid transparent;
}
@media (hover: hover) { .find-zone-row:hover { background: rgba(var(--surface-rgb),0.04); } }
.find-zone-row.is-active { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.08); }
.find-zone-row__chip {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: #0a0a12;
}
.find-zone-row__main { flex: 1; min-width: 0; }
.find-zone-row__top { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 2px; }
.find-zone-row__code { font-weight: 600; }
.find-zone-row__count { font-size: 11px; margin-bottom: 4px; color: var(--text-secondary); }
.find-zone-row__count-label { color: var(--text-secondary); }
.find-zone-row__count-num { color: rgba(var(--surface-rgb),0.85); font-weight: 400; }

.find-cta {
  width: 100%;
  appearance: none;
  border: none;
  background: var(--accent-gold);
  color: var(--gold-btn-text);
  border-radius: var(--radius-lg);
  padding: 10px;
  font-family: var(--font-thai);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
/* Keeps dark mode's vivid gold + dark text in light mode too (2026-08-08 ask)
   — solid fill + in-box text has self-contained contrast, independent of
   the page background, so light mode's muted gold isn't needed here. */
:root[data-theme="light"] .find-cta { background: var(--brand-gold); color: var(--brand-gold-ink); }
.find-cta--inpanel { margin-top: 4px; }

/* ── Marketplace entry link — plain navigation to /p/find/market/ (a
   separate page, per Figma frame `find-marketplace`), not a panel mode. ── */
.mk-entry-btn {
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  text-decoration: none;
  border: 1px solid rgba(var(--accent-gold-rgb),0.4);
  background: rgba(var(--accent-gold-rgb),0.1);
  color: var(--accent-gold-text);
  border-radius: var(--radius-lg);
  padding: 10px;
  font-family: var(--font-thai);
  font-size: 15px;   /* 13px +15% (2026-07-09) — icon scales with it since it's inline text */
  font-weight: 700;
  cursor: pointer;
}
@media (hover: hover) { .mk-entry-btn:hover { background: rgba(var(--accent-gold-rgb),0.18); } }

/* Wraps the submenu-label + Marketplace/Sponsor/Assist buttons as one unit
   so it can be hidden together while a search is active (2026-08-18 ask).
   Its own flex/gap mirrors .find-sidebar__content's — being a nested div,
   it doesn't inherit the parent's gap between ITS children automatically,
   so without this the label/buttons would render flush against each other. */
.find-panel__submenu {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Small, muted group label above the Marketplace/Sponsor buttons — a
   sub-menu heading, deliberately not styled to compete with
   .find-panel__head below it (2026-07-12 ask: "add it, but not prominent"). */
.find-panel__submenu-label {
  font-size: 11px;
  color: var(--text-secondary);
  font-weight: 600;
  margin-bottom: 2px;
}

/* Homepage-style submenu tiles (owner 2026-10-23) — mirrors style.css's
   .panel-submenu-grid / .panel-entry-btn (style.css isn't loaded here). */
.find-panel__submenu-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.find-entry-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
  min-height: 64px;
  padding: 8px 6px;
  font: 600 12px var(--font-thai);
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  color: var(--text-primary);
  background: rgba(var(--surface-rgb), 0.06);
  border: 1px solid rgba(var(--surface-rgb), 0.18);
  border-radius: var(--radius-lg);
  cursor: pointer;
}
@media (hover: hover) { .find-entry-btn:hover { background: rgba(var(--surface-rgb), 0.12); } }
.find-entry-btn .emoji-icon { width: 20px; height: 20px; }
/* Icon chip like the homepage's .panel-entry-btn .flat-ico; each tile keeps
   its pre-grid colour (owner 2026-10-23): Marketplace gold, Sponsor teal,
   Assist neutral. */
.find-entry-btn .ui-ico {
  width: 30px; height: 30px; border-radius: 8px; align-items: center; justify-content: center; font-size: 16px;
  color: var(--btn-ic, var(--accent-teal-text));
  background: color-mix(in srgb, var(--btn-ic, var(--accent-teal-text)) 16%, transparent);
}
.find-entry-btn--market {
  --btn-ic: var(--accent-gold-text);
  border-color: rgba(var(--accent-gold-rgb),0.4);
  background: rgba(var(--accent-gold-rgb),0.1);
  color: var(--accent-gold-text);
}
@media (hover: hover) { .find-entry-btn--market:hover { background: rgba(var(--accent-gold-rgb),0.18); } }
.find-entry-btn--sponsor {
  --btn-ic: var(--accent-teal-text);
  border-color: rgba(var(--accent-teal-rgb),0.4);
  background: rgba(var(--accent-teal-rgb),0.1);
  color: var(--accent-teal-text);
}
@media (hover: hover) { .find-entry-btn--sponsor:hover { background: rgba(var(--accent-teal-rgb),0.18); } }
.find-entry-btn--assist { --btn-ic: var(--text-primary); }

/* Sponsor entry — same shape as .mk-entry-btn, teal instead of gold so the
   two buttons read as distinct destinations, and ~10% smaller text
   (15px -> 13.5px) since its label is shorter/less primary than
   Marketplace's (2026-07-12 ask). */
.mk-entry-btn--sponsor {
  border-color: rgba(var(--accent-teal-rgb),0.4);
  background: rgba(var(--accent-teal-rgb),0.1);
  color: var(--accent-teal-text);
  font-size: 13.5px;
}
@media (hover: hover) { .mk-entry-btn--sponsor:hover { background: rgba(var(--accent-teal-rgb),0.18); } }

/* Assist-needs summary entry (2026-08-18) — same shape as .mk-entry-btn,
   but a <button> (in-panel mode switch), never an <a> (the other two link to
   a separate /p/find/* sub-page). Deliberately plain/neutral, not gold or
   teal (2026-08-18 ask) — those are already Marketplace/Sponsor's colors,
   and the site's palette has only those two accents to draw from. */
.mk-entry-btn--assist {
  border-color: rgba(var(--surface-rgb),0.18);
  background: rgba(var(--surface-rgb),0.05);
  color: var(--text-primary);
  font-size: 13.5px;
}
@media (hover: hover) { .mk-entry-btn--assist:hover { background: rgba(var(--surface-rgb),0.1); } }

.find-panel__divider { height: 0; border-top: 1px solid var(--border); margin: 4px 0; }

/* Assist-needs info banner (2026-08-18) — same shape as Fan Forge's
   .nt-provenance-notice (icon-led, subtle tinted box, right under the page
   head), swapped to the site's existing blue tone (tickets.css
   .tk-btn--blue) instead of that notice's teal. Both now read the shared
   --accent-blue token (2026-08-18) — the literal they used had no light-mode
   value and measured 2.40:1 here; points members at /p/my to set their answer. */
.find-assist-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--text-secondary);
  padding: 10px 12px;
  border-radius: var(--radius-md);
  background: rgba(var(--accent-blue-rgb), 0.07);
  border: 1px solid rgba(var(--accent-blue-rgb), 0.2);
}
.find-assist-banner .emoji-icon { flex-shrink: 0; margin-top: 1px; }
.find-assist-banner__link { color: var(--accent-blue); font-weight: 700; text-decoration: none; }
@media (hover: hover) { .find-assist-banner__link:hover { text-decoration: underline; } }

/* Assist-needs summary panel (renderAssistSummary, panel.js) — filter chips
   (additive-OR across categories) + card list + "โหลดเพิ่ม" pagination. */
.find-assist-filter-row { display: flex; flex-wrap: wrap; gap: 6px; }
.find-assist-chip {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 11px;
  background: rgba(var(--surface-rgb),0.05);
  border: 1px solid rgba(var(--surface-rgb),0.15);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font: 600 11.5px var(--font-thai);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.find-assist-chip.is-active { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.1); color: var(--accent-gold-text); }
/* Hover affordance (2026-08-21) — these chips looked identical to static
   labels until clicked. Gold border on hover, same language as
   .find-assist-loadmore / .find-panel__back below. An already-active chip
   deepens its own tint instead, so hover still reads as "this responds"
   without looking like it just turned off. Guarded by (hover: hover) so a
   touch device doesn't keep the hover state stuck on the last chip tapped. */
@media (hover: hover) {
  .find-assist-chip:hover { border-color: rgba(var(--accent-gold-rgb),0.55); background: rgba(var(--surface-rgb),0.1); }
  .find-assist-chip.is-active:hover { border-color: var(--accent-gold); background: rgba(var(--accent-gold-rgb),0.2); }
  .find-assist-zone-select:hover { border-color: rgba(var(--accent-gold-rgb),0.55); }
}
.find-assist-list { display: flex; flex-direction: column; gap: 8px; }
.find-assist-card {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: rgba(var(--surface-rgb),0.02);
}
.find-assist-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.find-assist-card__name { font-size: 13px; font-weight: 700; }
.find-assist-card__seat { font-size: 11.5px; color: var(--text-secondary); flex-shrink: 0; }

/* Seat chips inside one person's card (2026-08-21) — a member holding all
   three rounds is one card with three chips here, instead of three cards
   repeating the same needs. Round + zone + seat live in ONE chip, OUTLINED
   rather than filled (2026-08-21 follow-up — a solid tier-color fill was too
   loud beside the needs text). `color` is set per chip in JS from
   zoneTierTextColor(); `border: 1px solid` with no color of its own inherits
   currentColor, so that one assignment moves the outline too. Rounds whose
   concert day has gone by fade (same 0.4 as my.css's .my-tl-item.is-past) and
   sort to the bottom; a card whose every round is past fades as a whole. Both
   are opacity-only, so nothing here needs a light-mode override. */
.find-assist-card__seats { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
.find-assist-seat {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border: 1px solid;
  border-radius: var(--radius-pill);
  background: rgba(var(--surface-rgb),0.04);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
.find-assist-seat__no { font-weight: 700; }
/* Flex trims the spaces around a flex item's own text, so the separator
   gets its breathing room from margin, not from the ' · ' string. */
.find-assist-seat__sep { opacity: 0.55; margin: 0 4px; }
.find-assist-seat.is-past, .find-assist-card.is-past { opacity: 0.4; }

/* Day chips + zone dropdown row (2026-08-21). Options are built from the
   data actually present, so this row is skipped entirely when there is only
   one round/zone to choose from. */
.find-assist-filter-row--day { align-items: center; }
.find-assist-zone-select {
  margin-left: auto;
  padding: 6px 10px;
  background: rgba(var(--surface-rgb),0.05);
  border: 1px solid rgba(var(--surface-rgb),0.15);
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  font: 600 11.5px var(--font-thai);
  cursor: pointer;
}
/* An open <select>'s option list is OS-rendered and ignores the box styling
   above — without this it inherits white-on-white (CLAUDE.md, binding). */
.find-assist-zone-select option { background: var(--bg-elevated); color: var(--text-primary); }
.find-assist-need-row { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; margin-top: 4px; }
.find-assist-need-row__severity { font-size: 11.5px; color: var(--text-secondary); margin: 2px 0 6px 20px; }
.find-assist-loadmore {
  display: block; width: 100%;
  margin-top: 10px;
  padding: 10px;
  background: rgba(var(--surface-rgb),0.05);
  border: 1px solid rgba(var(--surface-rgb),0.15);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  font: 600 12.5px var(--font-thai);
  cursor: pointer;
}
@media (hover: hover) { .find-assist-loadmore:hover { border-color: var(--accent-gold); } }

/* Remark box — bordered/legend-label frame (2026-08-07), same pattern as
   p/giveaway.css .nt-detail-box, reused verbatim so this page (which
   doesn't load giveaway.css) gets the same look. Appended after the share
   row in both the zone-selected and seat-selected panel views. */
.nt-detail-box {
  position: relative;
  margin-top: 14px;
  padding: 14px 12px 10px;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: rgba(var(--surface-rgb),0.02);
}
.nt-detail-box-label {
  position: absolute;
  top: -8px; left: 10px;
  padding: 0 6px;
  /* Was var(--bg-secondary), corrected to var(--modal-bg) not --bg-elevated
     (see p/giveaway.css's identical component for the full explanation).
     Fixed 2026-08-08. */
  background: var(--modal-bg);
  font-size: 9px;
  color: var(--text-secondary);
}
.nt-detail-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.nt-detail-list__item { font-size: 10.8px; color: var(--text-secondary); line-height: 1.5; }

/* Share row (copy-link + LINE/Facebook/Instagram/X) — shared component, see
   share.css/share.js. Always the last row of the zone-selected / seat-
   selected panel; this page's own spacing on top of the shared style. */
.find-sidebar__content .share-row { margin-top: 4px; }

/* ── Zone-selected panel ─────────────────────────────────────────────────── */
.find-panel__back {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border);
  background: rgba(var(--surface-rgb),0.06);
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  font-family: var(--font-thai);
  font-size: 10.4px;
  cursor: pointer;
  padding: 4.8px 9.6px 4.8px 8px;
  align-self: flex-start;
}
@media (hover: hover) { .find-panel__back:hover { background: rgba(var(--surface-rgb),0.1); } }
.find-zone-head { display: flex; align-items: center; gap: 8px; }
.find-zone-head__swatch { width: 18px; height: 18px; border-radius: var(--radius-sm); flex-shrink: 0; }
.find-zone-head__code { font-size: 16px; font-weight: 700; }
.find-zone-head__tier { font-size: 11px; color: var(--text-secondary); }
.find-zone-price { font-size: 12px; color: var(--text-secondary); }

.find-zone-direction__label { font-size: 11px; color: var(--text-secondary); }
.find-zone-direction__card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  /* `clip`, not `hidden` (2026-08-15 fix) — `overflow: hidden` on this
     auto-height block, containing a mix of a plain block child (the stage
     label) and a flex-display child (the arrow/facing row), was collapsing
     the box's own computed height to ~2px (its border only) instead of
     auto-sizing to its ~85px of actual content, hiding nearly the entire
     card every time regardless of zone/text length — `hidden` establishes a
     scrollable region (scrollport) even with no visible scrollbar, and this
     mixed block+flex child shape tripped a browser auto-height/scrollport
     miscalculation. `clip` gives the identical rounded-corner visual clip
     with no scrollport concept at all, and reliably auto-sizes correctly. */
  overflow: clip;
  text-align: center;
}
.find-zone-direction__stage {
  padding: 8px;
  font-size: 12px;
  font-weight: 600;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
}
.find-zone-direction__row { display: flex; align-items: stretch; }
.find-zone-direction__arrow {
  flex-shrink: 0;
  padding: 10px 14px;
  font-size: 20px;
  border-right: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
}
.find-zone-direction__facing {
  flex: 1;
  min-width: 0; /* flex items default to min-width:auto (content width) — without
    this, the flex-shrink:0 arrow box beside it can push this past the card's
    edge and get silently cropped by .find-zone-direction__card's overflow:
    hidden instead of wrapping, on any layout where the row's natural width
    exceeds the sidebar's available width. */
  padding: 10px;
  font-size: 11.9px;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  white-space: normal;
  word-break: break-word;
}
.find-zone-days { display: flex; gap: 8px; }
.find-zone-day {
  flex: 1;
  text-align: center;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 4px;
}
.find-zone-day__label { font-size: 11px; color: var(--text-secondary); }
.find-zone-day__date { white-space: nowrap; }
.find-zone-day__count { font-size: 18px; font-weight: 700; margin-top: 2px; }
.find-zone-hint { font-size: 11px; color: var(--text-secondary); text-align: center; }

/* ── Seat-selected panel (Phase 2C, §15.6 "Seat selected") ──────────────── */
.find-seat-panel__dots-label { font-size: 11px; color: var(--text-secondary); display: block; margin-bottom: 6px; }
.find-seat-panel__dots-row { display: flex; gap: 8px; }
.find-seat-panel__dot {
  width: 14px;
  height: 14px;
  border-radius: var(--radius-full);
  background: rgba(var(--surface-rgb),0.08);
  border: 1px solid var(--border);
}
.find-seat-panel__days { display: flex; flex-direction: column; gap: 8px; }
.find-seat-panel__day {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 8px;
}
.find-seat-panel__day-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.find-seat-panel__day-label { font-size: 11px; color: var(--text-secondary); }
.find-seat-panel__day-round { font-weight: 700; }
.find-seat-panel__empty { font-size: 12px; color: var(--text-secondary); }
.find-seat-panel__owner { display: flex; align-items: center; gap: 8px; }
.find-seat-panel__avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  border: 2px solid var(--accent-teal);
  background: var(--bg-secondary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  flex-shrink: 0;
}
.find-seat-panel__avatar:has(img.emoji-icon:not(.is-loaded)) {
  animation: find-emoji-pulse 1.1s ease-in-out infinite;
}
.find-seat-panel__name-col { display: flex; flex-direction: column; gap: 2px; }
.find-seat-panel__name { font-size: 13px; font-weight: 600; }
.find-seat-panel__country { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 6px; font-size: 8.1px; color: var(--text-secondary); }
.find-seat-panel__x { display: inline-flex; align-items: center; gap: 4px; overflow-wrap: anywhere; }
.find-seat-panel__sep { opacity: 0.6; }
.find-seat-panel__x--locked { cursor: help; }
.find-seat-panel__x svg { width: 10px; height: 10px; flex-shrink: 0; }
.find-seat-panel__tags { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.find-seat-panel__tag {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: rgba(var(--surface-rgb),0.06);
  color: var(--text-secondary);
}
.find-seat-panel__tag--verified { background: rgba(var(--status-green-rgb),0.16); color: var(--success-text); } /* green like Admin Console / My (2026-10-28) */
.find-seat-panel__tag--pending { background: rgba(var(--accent-gold-rgb),0.15); color: var(--accent-gold-text); }
.find-seat-panel__tag--forsale { background: rgba(var(--accent-gold-rgb),0.15); color: var(--accent-gold-text); }
.find-seat-panel__tag--health-covid { background: rgba(var(--like-red-rgb), 0.15); color: var(--like-red); }
.find-seat-panel__tag--health-flu { background: rgba(var(--accent-gold-rgb),0.15); color: var(--accent-gold-text); }
.find-seat-panel__tag--health-other { background: rgba(140, 140, 140, 0.15); color: #B0B0B0; }
/* Text-only override (2026-08-08 fix, literal-hex sweep) — #9B7FE8 measured
   2.76:1 in light mode. Same hue, darkened to ~3.3:1. The seat-map dot
   (.find-seat__badge--health-other) keeps #9B7FE8 in both themes — it's a
   small color-coded swatch, not read as text. */
:root[data-theme="light"] .find-seat-panel__tag--health-other { color: #555555; }
/* Assist-needs tag (2026-08-18) — plain/neutral like the base tag, not gold:
   pending/forsale/health-flu already use gold here, and gold was ruled out
   for this feature's other /p/find surfaces (the summary entry button) in
   the same session — reusing it here would also make this tag visually
   indistinguishable from 3 unrelated ones. */
.find-seat-panel__tag--assist { background: rgba(var(--surface-rgb),0.1); color: var(--text-primary); }

.find-seat-panel__interest-btn {
  flex-shrink: 0;
  appearance: none;
  border: 1px solid var(--accent-gold);
  background: rgba(var(--accent-gold-rgb),0.1);
  color: var(--accent-gold-text);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 11px;
  font-weight: 700;
  cursor: pointer;
}
@media (hover: hover) { .find-seat-panel__interest-btn:hover { background: rgba(var(--accent-gold-rgb),0.2); } }

/* ── Floating registration CTA (§15.8) ────────────────────────────────────── */
.find-floating-cta {
  /* Shifted down 10% (56px -> 50.4px) and scaled down 10% overall
     (max-width/padding/border-radius below) — 2026-07-10 ask. */
  position: absolute;
  bottom: 50.4px;
  left: 16px;
  z-index: 10;
  max-width: 234px;
  /* Theme-following (2026-08-08 revert) — light fill in the
     :root[data-theme="light"] block above .find-toolbar. */
  background: rgba(5, 5, 5, 0.92);
  border: 1px solid var(--accent-gold);
  border-radius: 10.8px;
  padding: 10.8px;
  backdrop-filter: blur(6px);
}
.find-floating-cta__header { display: flex; align-items: flex-start; gap: 7.2px; margin-bottom: 7.2px; }
.find-floating-cta__text { font-size: 10.8px; flex: 1; }
.find-floating-cta__actions .find-cta { padding: 7.2px; font-size: 10.8px; }
.find-floating-cta__dismiss {
  flex-shrink: 0;
  appearance: none;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 11.7px;
  line-height: 1;
  padding: 0;
}

/* ── Mobile (MOBILE_STRATEGY §B2) ────────────────────────────────────────
   Map and panel stack in normal document flow, same as the main site: the
   map takes most of the initial viewport, the panel peeks ~30% at the
   bottom, and the user scrolls the page down to read the rest — no custom
   drag gesture, no position:fixed. */
/* Hidden entirely outside the mobile query below — map/panel are
   side-by-side on desktop with nothing below the fold to scroll to. Must
   come BEFORE the media query in source order so the mobile override below
   (equal specificity) actually wins on mobile instead of being shadowed by
   this base rule. */
.find-scroll-hint { display: none; }
/* Fluid shrink from 380px (>=1024px) down to 320px (769px) — linear
   interpolation between the two endpoints, saturates via clamp() outside
   that range so it's a no-op above 1024px and below 769px (where the
   768px breakpoint below takes over with an explicit 100%). */
@media (max-width: 1024px) and (min-width: 769px) {
  .find-sidebar { width: clamp(320px, 139.059px + 23.529vw, 380px); }
}
@media (max-width: 768px) {
  /* dvh paired with vh (2026-08-22) — this breakpoint was the one place in
     the file that skipped the rule its own base .find-page rule states above
     (line 39): on iOS `vh` always means the LARGE viewport, i.e. the height
     with the address bar hidden, so this box stayed ~98pt taller than the
     live viewport for as long as the bar was showing and the last of the
     page sat underneath it. `dvh` follows the bar. No effect off iOS —
     desktop resolves vh/dvh/svh/lvh to the same number (measured). */
  .find-page { flex-direction: column; height: auto; min-height: calc(100vh - var(--navbar-total-h)); min-height: calc(100dvh - var(--navbar-total-h)); }

  /* 65vh (was 58vh, before that 65/55/60vh — back to the original value per
     2026-07-10 ask; **70% of what is actually VISIBLE**, per owner's ruling
     2026-08-27 — was 65%, then 60%, now 70%; and always a share of the
     VISIBLE height, never of the reported viewport).
     Two separate reasons for the cap:
     (a) 65% of a SHORT viewport leaves the sidebar under 30% of the screen,
         so its first row — the search field — ends up within ~40px of the
         bottom edge. That is 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 without reporting anything
         through env(safe-area-inset-bottom) — that inset reads 0 there, so
         the page cannot detect the overlay. The fix is to keep the search
         field out of that band instead of trying to measure it.
     (b) A share, not a subtraction. Subtracting a fixed number of pixels was
         the 2026-08-27 morning's approach and it broke the moment a second
         platform appeared: the constant was derived on Android (852px tall)
         and iOS's viewport is 575px, so the same subtraction left the map at
         57% of the screen on one and pinned to its 240px floor — 42% — on the
         other.
     (b2) The share is taken over `100dvh - --inapp-reserve`, i.e. the height
         the visitor can actually SEE, because that is what "60% of the
         screen" means to the person holding the phone. A share of the
         *reported* viewport would still differ per device, since the app
         hides 222px of it on Android and 31px on iOS — the first attempt at
         this fix did exactly that and the owner caught it (45% vs 55% on
         screen). `--inapp-reserve` is the non-collapsing companion of
         --inapp-bottom precisely so this size does not change mid-scroll.
     (c) No blank space is added either way — this only changes how the same
         height splits between map and panel. */
  .find-map { flex: none; width: 100%; height: 70vh; height: clamp(240px, calc((100dvh - var(--inapp-reserve)) * 0.70), 70dvh); }
  /* X in-app browser on Android only: fixed 500px map — owner ruling 2026-10-23. iOS keeps the 70% share. */
  :root.is-inapp:not(.is-inapp-ios) .find-map { height: 500px; }


  .find-sidebar {
    position: relative;
    /* Pulled up over the map's own bottom edge by exactly the corner
       radius (2026-07-10 ask) — without this, the sidebar's rounded top
       corners cut into their own background, revealing a sliver of the
       PAGE's background color (not the map's) in the two corner triangles.
       Since map and sidebar backgrounds don't match, that sliver read as an
       obvious seam between "two separate layers", most visible when the
       map was zoomed. Overlapping instead means whatever shows through the
       rounded cutout is the map's own content, not a gap. z-index: 15
       (2026-07-10 follow-up — was 5) — the map's own overlay buttons go up
       to z-index 10 (.find-reset-btn/.find-zoom-btn), and the reset button
       in particular sits low enough (bottom:16px) to physically reach into
       this 20px overlap band, so 5 let it visually poke above the panel
       instead of being covered by it. Panel must always render above the
       map, full stop — this now beats every map-side element, not just the
       plain background. */
    z-index: 15;
    margin-top: -20px;
    width: 100%;
    height: auto;
    max-height: none;
    min-height: 40vh;
    min-height: 40dvh;
    border-left: none;
    border-top: 1px solid var(--border);
    border-radius: var(--radius-4xl) var(--radius-4xl) 0 0;
  }
  .find-sidebar__content {
    flex: none;
    overflow-y: visible;
  }

  /* Date/round selector and control buttons shrink ~15% on mobile, then an
     additional 10% on top of that (2026-07-10 ask) — only their own size
     (padding/font/dimensions), never their anchor offsets (top/left/right/
     bottom stay exactly as on desktop). */
  .find-toolbar { padding: 4.5px; gap: 6.3px; }
  .find-round-tab { padding: 4.5px 10.8px; }
  .find-round-tab__main { font-size: 9.9px; }
  .find-round-tab__sub { font-size: 7.65px; }

  /* Back to a compact corner overlay (2026-07-10 revert) — the full-width
     docked bar it briefly became wasn't wanted either. Stays translucent +
     blurred (see the base .find-legend rule) instead of the old near-opaque
     fill, so it no longer reads as a solid block sitting on the seats.
     bottom: 34px (was 14px, the desktop default — 2026-07-10 follow-up) —
     the sidebar now overlaps 20px into the map's bottom edge and renders
     above it (z-index: 15), so anything left at the old offset would sit
     behind the panel. Shifted up by exactly that 20px, same edge spacing
     as before relative to the panel's new top edge. */
  .find-legend { bottom: 34px; padding: 6px 9px; font-size: 8.5px; row-gap: 3px; column-gap: 8px; }
  .find-legend__dot { width: 9px; height: 9px; }

  /* CTA sat at the desktop bottom:56px offset, which put its bottom edge
     5px into the legend's mobile band (34px-61px, see above) — shifted up
     to clear it with a clean gap (2026-07-10 ask), then shifted back down
     10% (80px -> 72px, 2026-07-10 follow-up) alongside the base rule's own
     10% downward shift + 10% size reduction. */
  .find-floating-cta { bottom: 72px; }

  /* Whole block scaled down an additional 10% (2026-07-10 ask) — button
     size 34px -> 31px AND the vertical spacing between them recalculated
     from scratch (31px button + 8px gap = 39px per slot) instead of
     reusing the old 48px slot spacing, which was sized for the original
     40px desktop button and never actually scaled down with it, so the
     3-button stack read as loosely spaced on mobile.
     bottom offsets shifted up by 20px (2026-07-10 follow-up), same reason
     as .find-legend above — the panel now overlaps the map's bottom edge. */
  .find-reset-btn, .find-zoom-btn { width: 31px; height: 31px; }
  .find-reset-btn svg, .find-zoom-btn svg { width: 15px; height: 15px; }
  .find-reset-btn { bottom: 36px; }
  .find-zoom-btn--out { bottom: 75px; }
  .find-zoom-btn--in { bottom: 114px; }
  /* Additional 10% (2026-07-10 ask), on top of the existing mobile shrink. */
  .find-zone-back-btn { padding: 6.3px 10.8px; font-size: 9px; }

  /* Stage block (label + arc) shrinks ~15% on mobile already, then an
     additional 20% on top of that (2026-07-10 ask) — padding-top (its
     position anchor from the panel's top edge) stays 76px; only the
     label/arc's own dimensions shrink. */
  .find-seatplan__stage-label { font-size: 7.6px; }
  .find-seatplan__stage-arc { width: 47.6%; height: 9.6px; margin-top: 4px; border-top-width: 2px; }

  /* Floating "scroll for details" hint (2026-07-10 ask) — mobile only, map
     + panel stack in normal document flow here so the panel starts below
     the fold with no other affordance telling users to scroll for it. */
  .find-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);
    /* Brighter + more transparent, stronger blur (2026-07-10 ask) — the
       original dark fill (rgba(20,21,39)) sat too close to the page's own
       dark background and read as blending in rather than floating above it. */
    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;
  }
  /* Faded out once the panel's own top edge scrolls into view (toggled by
     an IntersectionObserver in find.js watching .find-sidebar directly —
     more reliable than a scroll-position threshold guessed in CSS/JS). */
  .find-scroll-hint.is-hidden { opacity: 0; pointer-events: none; }
  .find-scroll-hint__arrow { display: inline-block; animation: find-scroll-hint-bounce 1.4s ease-in-out infinite; }
}
@keyframes find-scroll-hint-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(3px); }
}

/* 3D seat view entry links (impact-3d-link.js): dimmed until the phase opens. */
.impact3d-locked { opacity: 0.45; pointer-events: none; }
.find-entry-btn--3d { --btn-ic: var(--accent-gold-text); grid-column: 1 / -1; }
.find-3d-btn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: rgba(var(--surface-rgb), 0.04);
  color: var(--text-primary);
  font: 600 13px var(--font-thai);
  text-decoration: none;
}
@media (hover: hover) { .find-3d-btn:hover { background: rgba(var(--surface-rgb), 0.08); } }
