:root {
  --ink: #0b0e14; --surface: #151a23; --raised: #1c2330; --line: #232b36;
  --text: #e6edf3; --muted: #8b96a5; --accent: #4d8dff;
  --t30: #3fb950; --t45: #d29922; --t60: #f85149;
}
* { box-sizing: border-box; }
html, body { max-width: 100%; overflow-x: hidden; }
body {
  margin: 0; background: var(--ink); color: var(--text);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  height: 100vh; display: flex; flex-direction: column;
}
header {
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
  padding: 12px 20px; border-bottom: 1px solid var(--line); background: var(--surface);
}
h1 { font-size: 18px; margin: 0; letter-spacing: -0.01em; }
.booking-bar { display: flex; gap: 14px; margin-left: auto; }
.booking-bar label { font-size: 12px; color: var(--muted); }
input, select, button {
  background: var(--raised); color: var(--text);
  border: 1px solid var(--line); border-radius: 6px; padding: 6px 9px; font: inherit; font-size: 13px;
}
button { cursor: pointer; }
input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

/* Prose layout shared by the static pages (about/privacy/terms/accessibility).
   The body rule above sets height 100vh and a flex column for the map layout.
   A prose page is an ordinary scrolling document, so neutralize that here
   rather than duplicating a second body rule in four HTML files. Without this
   the prose column was sized by the flex context and clipped on narrow
   viewports instead of wrapping. */
body.prose-body {
  height: auto; min-height: 100vh; display: block; overflow-x: hidden;
}
html:has(.prose-body), body.prose-body { max-width: 100%; }
.prose-page {
  display: block; width: 100%; max-width: 640px; margin: 0 auto;
  padding: 40px 20px 80px; height: auto; overflow: visible;
  box-sizing: border-box; overflow-wrap: break-word;
}
.prose-page h1 { font-size: 26px; margin: 0 0 6px; }
.prose-page .prose-updated { color: var(--muted); font-size: 13px; margin: 0 0 28px; }
.prose-page h2 { font-size: 17px; margin: 28px 0 8px; }
.prose-page p { margin: 0 0 14px; color: var(--text); }
.prose-page a { color: var(--accent); }
.prose-page .back-link { display: inline-block; margin-bottom: 24px; font-size: 14px; }
.prose-page .prose-footer {
  margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 13px; color: var(--muted);
}
.prose-page .prose-footer a { color: var(--muted); }
.prose-page .prose-footer a:hover, .prose-page .prose-footer a:focus-visible { color: var(--text); }

/* ---- Filter bar ----
   Desktop: one dense, wrapping flex row that fills the available width next
   to the map. The fieldset/legend grouping stays in the markup (for
   semantics and for the mobile sheet below) but is visually flattened here
   with `display: contents`, so its controls become direct flex children of
   .filter-bar instead of sitting inside a bordered box. Thin vertical
   dividers (.fbar-sep) stand in for the boxes to keep related controls
   scannable. The max-width: 860px media query below turns all of this back
   into the bordered, legend-labeled, single-column sheet used on mobile. */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  padding: 8px 20px; border-bottom: 1px solid var(--line);
}
#f-search { width: 320px; flex: 0 1 320px; }
.filter-row, .filter-scroll, .filter-group { display: contents; }
.filter-group legend { display: none; }
.filter-sheet-footer { display: none; }
.fbar-sep { width: 1px; align-self: stretch; background: var(--line); flex: 0 0 auto; }

/* Every filter control is a self-contained pill with its label inside, so
   wrapping never separates a label from its control. */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--raised); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 12px; font-size: 13px; color: var(--text); white-space: nowrap;
}
.pill.toggle { cursor: pointer; }

/* Visual hierarchy: search and price (the primary controls) are full-weight.
   Everything after the first .fbar-sep (Availability/Amenities/Quality) is
   secondary, so it is rendered a notch smaller and quieter to keep the eye
   on the primary row first. */
.filter-bar .fbar-sep ~ .filter-group .pill.toggle,
.filter-bar .fbar-sep ~ .filter-group .dd-trigger {
  font-size: 12px; padding: 5px 10px; opacity: 0.82;
}
.filter-bar .fbar-sep ~ .filter-group .pill.toggle:hover,
.filter-bar .fbar-sep ~ .filter-group .dd-trigger:hover { opacity: 1; }
.pill select { background: transparent; border: none; padding: 0; font: inherit; color: inherit; }
.pill select:focus-visible { outline: none; }

/* Dropdown trigger pill (custom multi-select, see dropdown.js) */
.dd-trigger {
  border-radius: 999px; background: var(--raised); border: 1px solid var(--line);
  color: var(--text); padding: 6px 12px; font-size: 13px;
}
.dd-trigger[aria-expanded="true"] { border-color: var(--accent); }
.dd-trigger-label { color: var(--muted); }
.dd-trigger-value { color: var(--text); font-weight: 600; }
.dd-wrap { position: relative; display: inline-block; }
.dd-panel {
  position: fixed; z-index: 20; width: 260px; max-height: 320px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); display: flex; flex-direction: column;
  overflow: hidden;
}
.dd-panel[hidden] { display: none; }
.dd-head { display: flex; gap: 8px; align-items: center; padding: 8px; border-bottom: 1px solid var(--line); }
.dd-filter { flex: 1; min-width: 0; }
.dd-clear { flex-shrink: 0; }
.dd-options { overflow-y: auto; padding: 4px; }
.dd-option {
  display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: 6px;
  font-size: 13px; cursor: pointer; min-height: 32px;
}
.dd-option:hover { background: var(--raised); }
.dd-empty { padding: 12px 8px; color: var(--muted); font-size: 13px; margin: 0; }

/* Price tier: always-visible segmented control, primary control + map legend. */
.tier-segmented { display: inline-flex; gap: 6px; }
.tier-seg {
  border-radius: 999px; border: 1px solid var(--line); background: var(--raised);
  color: var(--text); padding: 6px 12px; font-size: 13px; font-weight: 600;
}
.tier-seg[aria-pressed="true"][data-tier="30"] { background: var(--t30); border-color: var(--t30); color: #0b0e14; }
.tier-seg[aria-pressed="true"][data-tier="45"] { background: var(--t45); border-color: var(--t45); color: #0b0e14; }
.tier-seg[aria-pressed="true"][data-tier="60"] { background: var(--t60); border-color: var(--t60); color: #0b0e14; }

#f-sort, #f-clear { margin-left: auto; }
#f-clear:first-of-type { margin-left: 0; }

main { flex: 1; display: grid; grid-template-columns: 400px 1fr; min-height: 0; }
#list { overflow: hidden; border-right: 1px solid var(--line); }
.count { padding: 12px 16px; margin: 0; color: var(--muted); font-size: 13px; }
#map { min-height: 0; }
.card {
  padding: 9px 16px; border-bottom: 1px solid var(--line); cursor: pointer;
  border-left: 2px solid transparent; transition: background-color 0.1s ease, border-left-color 0.1s ease;
}
.card:hover { background: var(--surface); border-left-color: var(--line); }
.card:focus { outline: none; }
.card:focus-visible { background: var(--surface); border-left-color: var(--accent); outline: none; }
.card h3 { margin: 0 0 2px; font-size: 14.5px; }
.meta { margin: 0; color: var(--muted); font-size: 12.5px; }
.tiers { margin: 5px 0 0; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* Price tier and health grade both used green/amber/red and were easy to
   confuse (e.g. a red mark reading as "expensive" vs "grade C"). They are
   now different in KIND, not just placement: price tier is a solid filled
   pill (loudest element on a card); health grade is an outlined chip that
   is never filled. Do not "fix" this by filling the grade chip. */
.tier { padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 700; color: #0b0e14; }
.tier-30 { background: var(--t30); } .tier-45 { background: var(--t45); } .tier-60 { background: var(--t60); }
.no-book { font-size: 11px; color: var(--muted); }
.match-reason { margin: 4px 0 0; font-size: 12px; color: var(--muted); font-style: italic; }

.grade {
  display: inline-flex; align-items: center; gap: 3px; padding: 1px 7px;
  border-radius: 999px; font-size: 10.5px; font-weight: 600; background: transparent; border: 1px solid;
}
.grade::before { content: "GRADE"; font-size: 8.5px; letter-spacing: 0.04em; opacity: 0.8; }
.grade-a { color: var(--t30); border-color: var(--t30); }
.grade-b { color: var(--t45); border-color: var(--t45); }
.grade-c { color: var(--t60); border-color: var(--t60); }

/* Detail-panel health grade chip becomes a toggle for inspection detail,
   reusing the reviews-toggle/reviews-section collapsible pattern. */
.health-toggle { border: none; background: transparent; padding: 0; cursor: pointer; font: inherit; }
.health-section { margin-top: 14px; border: 1px solid var(--line); border-radius: 8px; }
.health-section .reviews-toggle { width: 100%; }
.health-body { padding: 0 12px 12px; font-size: 13px; }
.health-body .state { padding: 12px 0; text-align: left; }
.health-band { margin: 0 0 8px; }
.health-meta { margin: 0 0 8px; color: var(--muted); }
.violations { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.violation { padding: 6px 0; border-top: 1px solid var(--line); }
.violation-flag {
  display: inline-flex; align-items: center; padding: 1px 7px; margin-right: 6px;
  border-radius: 999px; font-size: 10.5px; font-weight: 600; background: transparent; border: 1px solid;
}
.violation-flag.critical { color: var(--t60); border-color: var(--t60); }
.violation-flag.not-critical { color: var(--muted); border-color: var(--line); }
.prior-count { margin: 8px 0 0; color: var(--muted); font-size: 12px; }

.rating { font-size: 12px; color: var(--text); }
/* Quieter than the cuisine/neighborhood .meta line above it: transit is
   supporting info on a card, not a primary fact. */
.meta.transit { font-size: 11.5px; opacity: 0.75; margin-top: 2px; }
.editorial { font-size: 13.5px; font-style: italic; color: var(--muted); margin: 6px 0; }
.phone a { color: var(--accent); text-decoration: none; }
.phone a:hover { text-decoration: underline; }
.hours { list-style: none; margin: 8px 0; padding: 0; font-size: 12.5px; color: var(--muted); }
.hours li { padding: 2px 0; }
.hours li.today { color: var(--text); font-weight: 600; }
#detail {
  position: fixed; right: 0; top: 0; bottom: 0; width: 400px; overflow-y: auto;
  background: var(--surface); border-left: 1px solid var(--line); padding: 20px; z-index: 5;
}
.detail-img { width: 100%; height: 180px; object-fit: cover; border-radius: 8px; }
.summary { font-size: 14px; }
.weeks { font-size: 12.5px; color: var(--muted); }
.warn { color: var(--t45); font-size: 13px; }
.actions { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.btn {
  display: block; text-align: center; text-decoration: none; padding: 10px;
  border: 1px solid var(--line); border-radius: 6px; color: var(--text); font-size: 14px;
}
.btn.primary { background: var(--accent); border-color: var(--accent); font-weight: 600; }
.close { position: absolute; top: 14px; right: 14px; }
.state { padding: 40px 16px; text-align: center; color: var(--muted); }

/* First-paint loading skeleton for the list, shown while /api/restaurants
   is in flight so the panel is never a blank void. Purely decorative
   (aria-hidden), replaced wholesale once real cards render. */
.skeleton-card { padding: 9px 16px; border-bottom: 1px solid var(--line); }
.skeleton-line { height: 10px; border-radius: 4px; background: var(--raised); }
.skeleton-line.title { width: 55%; height: 12px; margin-bottom: 7px; }
.skeleton-line.meta { width: 75%; margin-bottom: 6px; }
.skeleton-line.tiers { width: 40%; }
@media (prefers-reduced-motion: no-preference) {
  .skeleton-line { animation: skeleton-pulse 1.3s ease-in-out infinite; }
}
@keyframes skeleton-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }

/* Slim footer: lives at the bottom of the LIST column so it scrolls with
   the results and never steals height from the map. On mobile it sits
   inside the same #list bottom sheet, so it is only reachable once the
   sheet is dragged/tapped to full height, keeping the peek/half states
   uncluttered. */
/* Pinned to the bottom of the results column rather than appended after the
   last card. With 600+ cards an end-of-list footer is unreachable in practice,
   and the two empty states returned early without rendering it at all, so the
   legal links vanished exactly when the list was shortest. */
/* #list is the flex column; #cards is the only scroller inside it, so the
   footer is a fixed-size sibling that stays visible in every state. A sticky
   footer inside the scroller did not pin reliably, and appending it after the
   last card put it 600+ cards out of reach. */
#list { display: flex; flex-direction: column; }
#cards { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.list-footer {
  flex: 0 0 auto; z-index: 2;
  display: flex; flex-wrap: wrap; gap: 3px 10px; align-items: center;
  padding: 7px 16px; font-size: 11px; color: var(--muted);
  background: var(--ink); border-top: 1px solid var(--line);
}
.list-footer a { color: var(--muted); text-decoration: none; }
.list-footer a:hover, .list-footer a:focus-visible { color: var(--text); text-decoration: underline; }
.reviews-section { margin-top: 14px; border: 1px solid var(--line); border-radius: 8px; }
.reviews-toggle {
  width: 100%; display: flex; justify-content: space-between; align-items: center;
  background: transparent; border: none; padding: 10px 12px; cursor: pointer; color: var(--text); font: inherit;
}
.reviews-summary { color: var(--muted); font-size: 13px; }
.reviews-body { padding: 0 12px 12px; }
.reviews-body .state { padding: 12px 0; text-align: left; }
.review { padding: 10px 0; border-top: 1px solid var(--line); }
.review-meta { margin: 0 0 4px; font-size: 12.5px; }
.review-text { margin: 0; font-size: 13.5px; }
.muted { color: var(--muted); }

/* Map hover tooltip, restyled off Mapbox's default white popup. */
.rw-tooltip .mapboxgl-popup-content {
  background: var(--surface); color: var(--text); border: 1px solid var(--line);
  border-radius: 8px; padding: 8px 10px; font-size: 12.5px; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
}
.rw-tooltip .mapboxgl-popup-tip { border-top-color: var(--surface); border-bottom-color: var(--surface); }
.rw-tooltip-name { font-weight: 600; margin: 0 0 2px; }
.rw-tooltip-meta { margin: 0; color: var(--muted); }

/* Mobile "Filters" sheet controls: trigger, close, and confirm. All three
   only make sense inside the full-screen mobile sheet, so all three are
   hidden on desktop and only revealed in the max-width: 860px block below. */
#f-mobile-toggle, #f-mobile-close, #f-mobile-show { display: none; }

/* Bottom-sheet drag handle and "Back to map" affordance: mobile-only
   concepts, no meaning in the desktop split-pane layout below. */
.sheet-header { display: none; }
.back-to-map { display: none; }
.active-chips { display: none; }

@media (prefers-reduced-motion: no-preference) {
  .dd-panel, #detail, .filters-sheet { transition: opacity 0.12s ease; }
  @media (max-width: 860px) {
    #list, #detail { transition: height 0.22s ease; }
  }
}

@media (max-width: 860px) {
  /* Header controls (Date/Party/Time) do not need to sit on one line at
     this width: let them wrap instead of forcing the header wider than
     the viewport. margin-left: auto is dropped because an auto-margin
     flex item does not shrink to wrap; it just pushes past the edge. */
  /* Vertical space is the scarce resource on a phone. Verified at a real
     390x844 viewport, the header plus search plus Filters previously consumed
     about 560px of 844 before any map or result was visible. Everything here
     exists to claw that back: the title shrinks, the booking controls sit on
     one scrollable row rather than wrapping onto three, and the search and
     Filters share a row instead of stacking. */
  header { padding: 8px 12px; gap: 8px; row-gap: 6px; }
  header h1 { font-size: 15px; }
  .booking-bar {
    flex-wrap: nowrap; margin-left: 0; width: 100%; gap: 6px;
    overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .booking-bar::-webkit-scrollbar { display: none; }
  .booking-bar .pill, .booking-bar .dd-wrap { flex: 0 0 auto; }
  .booking-bar .pill { padding: 5px 10px; font-size: 12.5px; }
  .booking-bar input[type="date"] { font-size: 12.5px; padding: 2px 4px; }

  /* Search and the Filters button share one row. */
  .filter-bar { padding: 8px 12px; gap: 8px; flex-wrap: nowrap; align-items: center; }
  #f-search { flex: 1 1 auto; width: auto; min-width: 0; }
  #f-mobile-toggle { flex: 0 0 auto; }

  main { display: block; position: relative; height: 100%; }
  #map { position: absolute; inset: 0; height: auto; }
  #list {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 15;
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--surface); border-top: 1px solid var(--line);
    border-radius: 14px 14px 0 0; box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
    height: 96px; /* peek */
  }
  #list[data-snap="half"] { height: 50vh; }
  #list[data-snap="full"] { height: 85vh; }
  #cards { overflow-y: auto; flex: 1; min-height: 0; }

  .sheet-header {
    display: flex; align-items: center; gap: 8px; flex-shrink: 0;
    padding: 6px 12px 2px; background: var(--surface);
  }
  .sheet-drag {
    display: flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; background: transparent; border: none;
    touch-action: none; cursor: grab;
  }
  .handle-bar { display: block; width: 36px; height: 4px; border-radius: 2px; background: var(--line); }
  .sticky-count { margin: 0; flex: 1; }

  #detail {
    display: flex; flex-direction: column; overflow: hidden;
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: 100%;
    z-index: 25; border-radius: 14px 14px 0 0; padding: 0;
    box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
    height: 50vh; /* half, matches "opens at half height" requirement */
  }
  #detail[data-snap="peek"] { height: 96px; }
  #detail[data-snap="full"] { height: 85vh; }
  /* #detail{display:flex} above (an ID selector) otherwise beats the UA
     default [hidden]{display:none} stylesheet rule, which only has
     attribute-selector specificity. Without this the detail sheet renders
     open (empty) on load even though the hidden attribute is set. */
  #detail[hidden] { display: none; }
  #detail-content { overflow-y: auto; flex: 1; min-height: 0; padding: 0 20px 20px; }
  #detail .close { display: none; } /* superseded by .back-to-map on mobile */
  .back-to-map {
    display: inline-flex; align-items: center; gap: 4px; margin-left: 4px;
    min-height: 44px; padding: 0 12px; background: transparent; border: none;
    color: var(--accent); font-size: 14px; font-weight: 600;
  }

  .filter-bar { padding: 10px 12px; }
  .filter-row { display: none; }
  #f-mobile-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; border-radius: 999px;
  }

  body.filters-open .filter-row {
    display: flex; position: fixed; inset: 0; z-index: 30; background: var(--ink);
    flex-direction: column; align-items: stretch; padding: 16px 16px 0; overflow: hidden;
  }
  .filter-scroll { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; padding-bottom: 16px; }
  .filter-group {
    display: block; border: 1px solid var(--line); border-radius: 10px; padding: 10px 10px 12px; margin: 0;
  }
  .filter-group legend {
    display: block; padding: 0 6px; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
  }
  .filter-group > * { margin: 4px 4px 4px 0; }
  .fbar-sep { display: none; }
  .filter-sheet-footer { display: flex; }
  body.filters-open .active-chips {
    display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 12px; flex-shrink: 0;
  }
  .chip {
    display: inline-flex; align-items: center; gap: 4px; min-height: 32px;
    border-radius: 999px; border: 1px solid var(--accent); background: transparent;
    color: var(--text); padding: 4px 10px; font-size: 12.5px;
  }
  .filter-sheet-footer {
    flex-shrink: 0; padding: 12px 0 16px; background: var(--ink);
    border-top: 1px solid var(--line); display: flex; gap: 10px;
  }
  body.filters-open .filter-row .pill,
  body.filters-open .filter-row .dd-wrap,
  body.filters-open .filter-row .tier-segmented { min-height: 44px; }
  #f-clear { min-height: 44px; }
  body.filters-open .filter-row #f-mobile-close,
  body.filters-open .filter-row #f-mobile-show { display: inline-flex; flex: 1; }

  .card { min-height: 44px; }
  .btn, button, select, input { min-height: 44px; }
  .tier, .grade, .rating { min-height: 0; }
}

@media (max-width: 360px) {
  html, body { overflow-x: hidden; }
  #detail-content, .filter-scroll { padding-left: 12px; padding-right: 12px; }
}
