/* MouseScout.
 *
 * Light, compact, and built for a phone held one-handed in a Disneyland queue.
 * The structure is SeatScout's; the look deliberately isn't, so the two (and
 * JP NTFY) can never be mistaken for each other on the home screen.
 *
 * Safe areas: the manifest is `display: standalone` and the page sets
 * `viewport-fit=cover`, so an installed iOS app draws under the clock, the
 * Dynamic Island and the home indicator. Nothing reserves that space — every
 * edge that touches the screen pays its own inset through these variables.
 * `env(safe-area-inset-*)` is always 0px without viewport-fit=cover, so the
 * padding silently does nothing if that meta tag is ever dropped.
 *
 * The insets are indirected through variables so the layout can be exercised
 * in a desktop browser by overriding them.
 */
:root {
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);

  /* Daylight castle: lavender-white ground, indigo castle, turret pink.
     Light where SeatScout (aubergine/gold) and JP NTFY (forest green) are
     dark, so a glance at the icon never lands you in the wrong app.

     Contrast (WCAG 2.2, checked 2026-10-04): text 14.9:1 on --bg; --muted
     5.6:1 on --panel-2; --accent 5.7:1 as a link and 6.4:1 under white text;
     --ok/--warn/--err all >= 5:1 on white and on their tints; --field-line
     4.2:1 for control outlines (1.4.11 asks 3:1). --pink is 2.2:1, so it is
     decoration only (icon, accents), never text. Green stays a *status*
     colour (a table opened), never brand. */
  color-scheme: light;
  --bg: #f4f1ff;
  --panel: #ffffff;
  --panel-2: #efebff;
  --line: #dcd5f7;          /* panel edges: decorative */
  --field-line: #7d74b8;    /* inputs and buttons: must meet 3:1 */
  --text: #1c1a3d;
  --muted: #5d5888;
  --accent: #4f4fc9;
  --accent-ink: #ffffff;    /* text on an indigo fill */
  --pink: #ff7aa2;
  --ok: #0f7552;
  --ok-tint: #eaf6f0;
  --warn: #9a5b00;
  --err: #c4314b;
  --taken: #cfc8ee;

  /* Apple HIG / WCAG 2.5.8: nothing interactive smaller than this. */
  --tap: 44px;
  --radius: 12px;
}

* { box-sizing: border-box; }

/* Any class that sets `display` outranks the browser's own
   `[hidden] { display: none }`, so an element can be hidden in the DOM and
   still on screen. That is exactly what happened to `.showing` (display:flex)
   in the format filter: rows were marked hidden, the count agreed, and the
   page showed them anyway. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
}

a { color: var(--accent); }

/* --------------------------------------------------------------------- */
/* Chrome                                                                 */
/* --------------------------------------------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: calc(10px + var(--safe-top)) calc(16px + var(--safe-right))
           10px calc(16px + var(--safe-left));
  background: var(--panel);
  border-bottom: 3px solid var(--pink);
  position: sticky;
  top: 0;
  z-index: 20;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 650;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.brand .logo { width: 22px; height: 22px; }

.topbar nav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-left: auto;
}
.topbar nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 12px;
  border-radius: 9px;
  color: var(--muted);
  text-decoration: none;
  font-size: 15px;
}
.topbar nav a.active, .topbar nav a:hover {
  background: var(--panel-2);
  color: var(--text);
}

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 20px calc(16px + var(--safe-right))
           calc(40px + var(--safe-bottom)) calc(16px + var(--safe-left));
}

h1 { font-size: 24px; margin: 0 0 4px; }
h2 { font-size: 18px; margin: 28px 0 10px; }
.muted { color: var(--muted); }
.small { font-size: 14px; }

/* --------------------------------------------------------------------- */
/* Panels, forms, buttons                                                 */
/* --------------------------------------------------------------------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 14px;
}

label { display: block; font-size: 14px; color: var(--muted); margin-bottom: 6px; }

input[type=text], input[type=password], input[type=date], input[type=number], input[type=time],
input[type=email], select, textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 10px 12px;
  background: var(--panel-2);
  border: 1px solid var(--field-line);
  border-radius: 10px;
  color: var(--text);
  font-size: 16px;      /* 16px or iOS zooms the page on focus */
}
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.field { margin-bottom: 14px; }
.row { display: flex; gap: 12px; flex-wrap: wrap; }
.row > * { flex: 1 1 160px; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 16px;
  border: 1px solid var(--field-line);
  border-radius: 10px;
  background: var(--panel-2);
  color: var(--text);
  font-size: 15px;
  font-weight: 550;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.danger { color: var(--err); }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

.actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

.flash {
  padding: 12px 14px;
  border-radius: 10px;
  margin-bottom: 14px;
  border: 1px solid var(--line);
  background: var(--panel);
}
.flash.ok { border-color: var(--ok); background: var(--ok-tint); }
.flash.error { border-color: var(--err); background: #fdeef1; }
.ok-text { color: var(--ok); }
.err-text { color: var(--err); }
.warn-text { color: var(--warn); }

.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
}
.pill.on { border-color: var(--ok); color: var(--ok); }
.pill.off { border-color: var(--line); }
.pill.hot { border-color: var(--ok); background: var(--ok-tint); color: var(--ok); }
a.pill.hot { text-decoration: none; }
a.pill.hot:hover { background: #d6eee3; }

/* --------------------------------------------------------------------- */
/* Lists and tables                                                       */
/* --------------------------------------------------------------------- */
.watch { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }

/* Poster thumbnail. Fixed box so a missing or slow image never reflows the
   card, and a film-strip placeholder when there is no artwork — Fandango
   serves its own grey default, which reads as a broken poster rather than as
   "no poster". */
.poster {
  flex: 0 0 44px;
  width: 44px;
  height: 66px;
  border-radius: 6px;
  object-fit: cover;
  background: var(--panel-2);
  border: 1px solid var(--line);
}
.poster.placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}
/* A 404 after the page loaded: fall back to the same empty box. */
.poster.broken { visibility: hidden; }
.watch .body { flex: 1 1 260px; min-width: 0; }
.watch h3 { margin: 0 0 4px; font-size: 17px; }

.seatlist { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 0; }
.seatchip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--line);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.seatchip button {
  all: unset;
  cursor: pointer;
  color: var(--muted);
  /* The chip itself is small, so the remove control carries the tap target. */
  min-width: var(--tap);
  min-height: var(--tap);
  margin: -10px -10px -10px 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.seatchip button:hover { color: var(--err); }

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 600; }
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --------------------------------------------------------------------- */
/* Seat map                                                               */
/* --------------------------------------------------------------------- */
.maparea {
  position: relative;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background: #05070a;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  /* A bounded viewport. Without this the zoomed map is 1600px tall, the
     container grows to fit it, and the map "scrolls" by scrolling the whole
     page — which reads as the map being cut off. Capped, it becomes a window
     you pan around inside. */
  max-height: min(72vh, 620px);
  /* Touch panning and pinch-zoom are left to the browser: it does both better
     than script can, with momentum. The pointer handlers only take over for a
     mouse, which has no native equivalent of a drag-to-pan. */
  touch-action: pinch-zoom pan-x pan-y;
}
.maparea svg { display: block; width: 100%; height: auto; }

/* Which seat the cursor is on. Pointer-events off so it can never sit between
   the cursor and the seat it is naming — that would swallow the click. */
.seat-tip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  padding: 3px 8px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font-size: 13px;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
  white-space: nowrap;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .45);
}

/* Only offer the grab cursor when there is somewhere to grab to. */
.maparea.pannable { cursor: grab; }
.maparea.panning { cursor: grabbing; }
.maparea.painting, .maparea.painting .seat-hit { cursor: crosshair; }
.maparea.mode-pan .seat-hit { cursor: grab; }
.maparea.mode-pan.panning .seat-hit { cursor: grabbing; }

/* Segmented control for what a drag does. */
.modes { display: inline-flex; }
.modes .mode {
  min-height: var(--tap);
  padding: 0 14px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  font: inherit;
  font-size: 15px;
  font-weight: 550;
  cursor: pointer;
}
.modes .mode:first-child { border-radius: 10px 0 0 10px; }
.modes .mode:last-child { border-radius: 0 10px 10px 0; margin-left: -1px; }
.modes .mode[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
/* Two long labels ("All showings" / "Specific showings") sit within 344px only
   if they may give up padding rather than overflow the panel. */
.modes { max-width: 100%; }
.modes .mode { flex: 0 1 auto; min-width: 0; }

/* The start times you're watching. Chips rather than a list of checkboxes:
   there are a dozen-odd of them, they're short, and the shape reads as "these
   are the ones on" at a glance. */
.timelist {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 12px 0 0;
}
.time-chip {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-height: var(--tap);
  padding: 4px 12px;
  border: 1px solid var(--accent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.time-chip strong { font-size: 15px; font-weight: 600; line-height: 1.1; }
.time-chip span { font-size: 11px; color: var(--muted); line-height: 1.1; }
.time-chip.off {
  border-color: var(--line);
  border-style: dashed;
  background: transparent;
  color: var(--muted);
}
.time-chip.off strong { text-decoration: line-through; }

/* Dragging without pointer capture would otherwise start a text/image
   selection across the map. */
.maparea { user-select: none; -webkit-user-select: none; }

/* Zoomed to 1:1, where a seat's padded hit rect is ~45px — an actual tap
   target. Fitting 39 seats across a 344px phone would give each one 4px, so
   "see the whole room" and "tap a seat" genuinely cannot both be true at once;
   this is the toggle between them rather than a pretence that they can. */
.maparea.zoomed svg { width: 1600px; max-width: none; }

/* Seats are drawn twice: a visible rect, and an invisible one padded out to a
   real tap target. Without the second, selecting a seat on a phone is a test
   of aim rather than intent. */
/* In pan mode the seats stop claiming touches, so a thumb-drag starting on a
   seat scrolls the map natively instead of painting. */
.maparea.mode-pan .seat-hit { touch-action: auto; }

.seat-hit {
  fill: transparent;
  cursor: pointer;
  /* A touch that begins on a seat is a selection, not a scroll. Without this
     the browser claims the gesture and pans the map out from under the thumb
     before the first pointermove arrives. Touches starting on empty map still
     pan, which is how you move around. */
  touch-action: none;
}
.seat { pointer-events: none; }
.seat.free { fill: var(--accent); }
.seat.taken { fill: var(--taken); }
.seat.sel { fill: var(--ok); stroke: #fff; stroke-width: 2.5px; }
/* Selection is also carried by the ring above and the chip list below, so the
   state never depends on colour alone. */
.seat.accessible { rx: 9px; }

/* Accessible seats carry a symbol, not just a rounder corner — shape alone was
   too subtle, and these are seats you must not book by mistake. The glyph is
   never the tap target; the invisible hit rect above it is. */
.seat-icon { pointer-events: none; fill: var(--bg); stroke: var(--bg); }
.seat-icon.on-taken { fill: var(--muted); stroke: var(--muted); }

.legend { display: flex; gap: 14px; flex-wrap: wrap; margin: 10px 0 0; font-size: 13px; }
.legend span { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); }
.legend i { width: 13px; height: 13px; border-radius: 3px; display: inline-block; }

.stepper { display: inline-flex; align-items: center; gap: 0; }
.stepper button {
  width: var(--tap); height: var(--tap);
  font-size: 20px; line-height: 1;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line); cursor: pointer;
}
.stepper button:first-child { border-radius: 10px 0 0 10px; }
.stepper button:last-child { border-radius: 0 10px 10px 0; }
.stepper output {
  min-width: 56px; height: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  background: var(--bg); font-variant-numeric: tabular-nums; font-weight: 600;
}

.rowpicker { display: flex; flex-wrap: wrap; gap: 6px; }
.rowpicker button {
  min-width: var(--tap); min-height: var(--tap);
  border-radius: 9px; border: 1px solid var(--line);
  background: var(--panel-2); color: var(--text);
  font-weight: 600; cursor: pointer;
}
.rowpicker button[aria-pressed="true"] { border-color: var(--ok); color: var(--ok); }

.sticky-save {
  position: sticky;
  bottom: 0;
  margin-top: 14px;
  padding: 12px 0 calc(12px + var(--safe-bottom));
  background: linear-gradient(180deg, transparent, var(--bg) 22%);
}

/* --------------------------------------------------------------------- */
/* Narrow screens — checked down to 344px (Galaxy Z Fold)                 */
/* --------------------------------------------------------------------- */
@media (max-width: 560px) {
  /* These rules re-state the safe-area padding: overriding padding here
     without it is how the header ends up under the clock. */
  .topbar {
    padding: calc(8px + var(--safe-top)) calc(12px + var(--safe-right))
             8px calc(12px + var(--safe-left));
    gap: 8px;
  }
  main {
    padding: 16px calc(12px + var(--safe-right))
             calc(32px + var(--safe-bottom)) calc(12px + var(--safe-left));
  }
  .topbar nav { width: 100%; margin-left: 0; }
  .topbar nav a { flex: 1 1 auto; justify-content: center; padding: 0 8px; }
  h1 { font-size: 21px; }
  .row > * { flex: 1 1 100%; }
  .actions .btn { flex: 1 1 auto; }
}

/* --------------------------------------------------------------------- */
/* Status page                                                            */
/* --------------------------------------------------------------------- */
.health-item { padding: 14px 16px; margin-bottom: 10px; }
.health-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.health-item .detail { margin: 2px 0 0; }
.access-note {
  margin-top: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--line);
  font-size: 13px;
  color: var(--muted);
}
.health-item .lastran { margin-top: 6px; font-size: 12px; }

.pill.pending { border-color: var(--line); color: var(--muted); }
.pill.warn { border-color: var(--warn); color: var(--warn); }
.pill.err { border-color: var(--err); color: var(--err); }

/* --------------------------------------------------------------------- */
/* Film search (new watch, step 1)                                        */
/* --------------------------------------------------------------------- */
.results {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 30;
  margin-top: 4px;
  max-height: 340px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .5);
}

.result {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  /* Comfortably past a 44px tap target: this list is the primary way in, and
     it's usually driven by thumb on a phone. */
  min-height: 60px;
  padding: 10px 12px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.result:last-child { border-bottom: 0; }
.result:hover, .result.active { background: var(--line); }
.result img, .result i {
  width: 34px;
  height: 50px;
  flex: 0 0 34px;
  border-radius: 4px;
  object-fit: cover;
  background: var(--bg);
  display: block;
}
.result-empty { padding: 14px 12px; }


/* Which showing's availability the picker is displaying. */
.preview-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.preview-bar label { margin: 0; white-space: nowrap; }
.preview-bar select { width: auto; min-width: 190px; flex: 0 1 auto; }

/* --------------------------------------------------------------------- */
/* Step 2 — one card per cinema, its formats listed inside                */
/* --------------------------------------------------------------------- */
.theater h3 { font-size: 17px; }

.showing {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}
.showing:first-of-type { border-top: 0; padding-top: 4px; }
.showing .body { flex: 1 1 240px; min-width: 0; }

#format-filter .rowpicker button {
  /* Format names are words, not single letters like the row picker's. */
  padding: 0 14px;
  font-size: 14px;
  font-weight: 550;
}

/* One booking link per showing in the History detail column. */
.matchlist { margin: 0; padding: 0; list-style: none; }
.matchlist li { padding: 2px 0; }
.matchlist li + li { border-top: 1px solid var(--line); }
.matchlist .seats { margin-left: 8px; }

/* --------------------------------------------------------------------- */
/* Date calendar — a range with holes in it                               */
/* --------------------------------------------------------------------- */
.calendar {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 4px;
}
/* Flexes down rather than forcing a fixed 7 x 44px grid, which overflowed a
   344px screen by 5px once padding was counted. */
.cal-month { flex: 1 1 292px; max-width: 340px; min-width: 0; }
.cal-month h4 {
  margin: 0 0 6px;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
}
/* First month's heading row: name left, the month arrows right beside it. */
.cal-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 6px;
}
.cal-head h4 { margin: 0; flex: 1 1 auto; }
.cal-nav { min-width: var(--tap); padding: 0; }
.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}
.cal-dow {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 22px;
  font-size: 11px;
  color: var(--muted);
}
.cal-day {
  /* Height holds the 44px tap target even when width has to give — a squat
     cell is far better than a page that scrolls sideways. */
  width: 100%;
  min-width: 0;
  height: var(--tap);
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.cal-day[aria-pressed="true"],
.cal-day.in {
  background: rgba(53, 214, 155, .16);
  border-color: var(--ok);
  color: var(--ok);
  font-weight: 600;
}
/* Range mode: a day outside the current range. Tappable — that's the whole
   point — so it keeps its border, just no state colour. */
.cal-day.out {
  background: transparent;
  color: var(--muted);
}
/* The first tap of a new range, waiting for its other end. */
.cal-day.range-edge {
  background: rgba(242, 169, 59, .18);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
/* Skipped days are struck through as well as dimmed, so the state doesn't
   rest on colour alone. */
.cal-day.off {
  background: var(--panel-2);
  border-color: var(--line);
  color: var(--muted);
  text-decoration: line-through;
}
.cal-day:hover { border-color: var(--accent); }

/* ---- MouseScout additions ---------------------------------------------- */
fieldset.field { border: 0; padding: 0; margin: 0 0 14px; min-width: 0; }
fieldset.field legend { padding: 0; margin-bottom: 6px; color: var(--muted); font-size: 14px; }
.date-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
.date-row input { flex: 1 1 auto; min-width: 0; min-height: 44px; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  padding: 0 14px; border: 1px solid var(--field-line); border-radius: 999px; cursor: pointer;
}
.chip input { width: 18px; height: 18px; margin: 0; }
.chip:has(input:checked) { border-color: var(--accent); color: var(--accent); background: #e9e7ff; }
.chip input { accent-color: var(--accent); }
select, input[type=time], input[type=date], input[type=number] { min-height: 44px; }
.open-target {
  display: flex; flex-direction: column; gap: 6px; padding: 16px; margin-bottom: 12px;
  border: 1px solid var(--accent); border-radius: 14px; text-decoration: none; color: inherit;
}
.open-name { font-size: 19px; font-weight: 600; }
.open-when { color: var(--muted); }
.open-cta { margin-top: 6px; font-weight: 600; color: var(--accent); min-height: 24px; }
