/*
 * CrowdPour venue dashboard.
 *
 * Standalone from the app's constants/Theme.ts — a web page cannot import
 * a React Native module, and a build step to share a dozen colors would
 * cost more than it saves. Brand values below are copied from Colors
 * there; if the brand changes, change both. Purple is primary, pink is an
 * accent only.
 *
 * CHART COLOR IS COMPUTED, NOT CHOSEN. The heat ramp is a single-hue
 * purple sequential scale validated with the dataviz validator against
 * this card surface: lightness monotone, every adjacent gap >= 0.06, hue
 * spread 15 degrees. Its lightest step is allowed to recede into the
 * surface because the scale is sequential — "near zero" should look like
 * nothing. Do not hand-tune these six values; re-run the validator.
 */
:root {
  --bg:            #0A0A1A;
  --bg-raised:     #111126;
  --card:          #1F1F33;
  --card-2:        #262640;
  --border:        #2C2C44;
  --border-soft:   #24243A;

  --primary:       #A855F7;
  --primary-ink:   #12021F;
  --primary-soft:  rgba(168, 84, 247, 0.14);

  --text:          #F2F2F5;
  --text-2:        #C9C5D7;
  --muted:         #8B86A0;

  --good:          #32D296;
  --bad:           #EF4444;
  --accent:        #FFB020;

  /* Validated sequential ramp — see the header note. */
  --heat-1: #2A2350;
  --heat-2: #41307F;
  --heat-3: #5B3FAD;
  --heat-4: #7A54D4;
  --heat-5: #9B70E8;
  --heat-6: #C39BFA;

  --r-lg: 16px;
  --r-md: 12px;
  --r-sm: 9px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background:
    radial-gradient(900px 480px at 88% -12%, rgba(168, 84, 247, 0.10), transparent 65%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.hidden { display: none !important; }
.spacer { flex: 1; }

/* ── Top bar ────────────────────────────────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 12px;
  padding: 0 22px; height: 60px;
  background: rgba(17, 17, 38, 0.86);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--border-soft);
}
.brand {
  font-size: 19px; font-weight: 800; letter-spacing: 0.08em;
  background: linear-gradient(96deg, #A855F7, #EC4899);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.topbar-sep { width: 1px; height: 20px; background: var(--border); }
.venue-name { font-weight: 600; font-size: 14px; }
/* ── Combobox ───────────────────────────────────────────────────────
 * Replaces <select>, which renders as the OS control and ignores CSS.
 * Everything the native element gave for free is re-implemented in
 * app.js — keyboard, roles, type-ahead. See createCombo.
 */
.combo { position: relative; }
.combo-button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  font: inherit; font-size: 14px; font-weight: 600; text-align: left;
  color: var(--text); background: var(--bg-raised);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 10px 12px; cursor: pointer;
  transition: border-color 0.14s, background 0.14s;
}
.combo-button:hover { background: var(--card-2); }
.combo-button:focus-visible {
  outline: none; border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
.combo-caption { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo-chevron { width: 12px; height: 12px; color: var(--muted); flex: none; transition: transform 0.16s; }
.combo-button[aria-expanded="true"] .combo-chevron { transform: rotate(180deg); }

.combo-list {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  min-width: 100%; max-height: 288px; overflow-y: auto;
  background: #15152B; border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 5px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.55);
}
.combo-option {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 11px; border-radius: 7px; cursor: pointer;
  font-size: 13.5px; white-space: nowrap;
}
.combo-option > span:first-child { flex: 1; }
/* Hover is a ghost wash so it never competes with the selection. */
.combo-option.active { background: var(--card-2); }
.combo-option[aria-selected="true"] { color: var(--primary); font-weight: 600; }
.combo-option[aria-selected="true"]::after {
  content: '✓'; font-size: 13px; font-weight: 700;
}
.combo-hint { color: var(--muted); font-size: 12px; font-weight: 400; }
.combo-option[aria-selected="true"] .combo-hint { color: var(--muted); }

/* In the top bar the picker sizes to its content rather than spanning. */
.combo-venue { width: auto; min-width: 170px; max-width: 240px; }
.combo-venue .combo-button { padding: 7px 11px; }
.who { color: var(--muted); font-size: 13px; }

/* ── Buttons & fields ───────────────────────────────────────────── */
.btn {
  font: inherit; font-size: 13.5px; font-weight: 600;
  border-radius: var(--r-sm); padding: 9px 14px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--text);
  transition: background 0.14s, border-color 0.14s, opacity 0.14s;
  white-space: nowrap;
}
.btn:disabled { opacity: 0.5; cursor: default; }
.btn-primary { background: var(--primary); color: var(--primary-ink); }
.btn-primary:not(:disabled):hover { background: #B76CF8; }
.btn-quiet { border-color: var(--border); color: var(--text-2); }
.btn-quiet:not(:disabled):hover { background: var(--card-2); color: var(--text); }
.btn-link { color: var(--muted); padding: 6px 2px; }
.btn-link:hover { color: var(--primary); }
.btn-block { width: 100%; display: block; margin-top: 10px; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field > span {
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted);
}
.field input, input {
  font: inherit; font-size: 14px; color: var(--text);
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 10px 12px; width: 100%;
  transition: border-color 0.14s, box-shadow 0.14s;
}
.field input:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft);
}
/* Spinner arrows are a fiddly hit target for a number typed once a week,
   and they render differently in every browser. */
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Every input, not just the ones inside `.field`. The hours fields were
   not, so they fell back to Chromium's default #757575 — lighter than this
   and a neutral gray against a purple-tinted page, which made an example
   look more like a saved value than it should. */
.field input::placeholder, input::placeholder { color: #6A6580; }

/* Presets as a segmented control — nobody fights a dropdown for "30 days". */
.segmented {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-md);
}
.segmented button {
  font: inherit; font-size: 13px; font-weight: 600;
  border: none; background: transparent; color: var(--muted);
  padding: 7px 13px; border-radius: 9px; cursor: pointer;
  transition: background 0.14s, color 0.14s;
}
.segmented button:hover { color: var(--text-2); }
.segmented button.on { background: var(--primary); color: var(--primary-ink); }

/* ── Sign in ────────────────────────────────────────────────────── */
.signin-shell { max-width: 420px; margin: 12vh auto; padding: 0 20px; }
.signin { padding: 28px; }
.signin .field { margin-bottom: 14px; }
.signin-title { font-size: 21px; margin: 0 0 6px; }

.msg { font-size: 13px; margin: 12px 0 0; }
.msg-error { color: var(--bad); }
.msg-ok { color: var(--good); }

/* ── Layout ─────────────────────────────────────────────────────── */
.wrap { max-width: 1120px; margin: 0 auto; padding: 24px 22px 72px; }

.toolbar {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 20px; flex-wrap: wrap;
}
.freshness { color: var(--muted); font-size: 12.5px; }

/* Refetch holds the previous render rather than flashing a skeleton. */
#dashBody { transition: opacity 0.16s; }
#dashBody.loading { opacity: 0.45; }

.card {
  background: var(--card);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-lg);
  padding: 22px;
  margin-bottom: 18px;
}
.card-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 4px;
}
.card-head h2 { font-size: 15.5px; margin: 0; letter-spacing: -0.01em; }
.card-head .btn-link, .card-head .pill { margin-left: auto; }
.blurb { color: var(--text-2); font-size: 13px; margin: 0 0 18px; max-width: 68ch; }
.footnote {
  color: var(--muted); font-size: 12px; line-height: 1.6;
  max-width: 78ch; margin: 26px auto 0; text-align: center;
}
.hint { color: var(--muted); font-size: 12.5px; margin: 10px 0 0; }
.hint-quiet { color: #6A6580; }
.hint b { color: var(--text-2); font-weight: 600; }

.pill {
  font-size: 12px; font-weight: 600; color: var(--primary);
  background: var(--primary-soft); border-radius: 999px; padding: 4px 11px;
}

/* ── Hero: exactly one on the page ──────────────────────────────── */
.hero-grid {
  display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 2fr);
  gap: 18px; margin-bottom: 18px;
}
.hero {
  margin: 0;
  background:
    linear-gradient(150deg, rgba(168, 84, 247, 0.16), transparent 62%),
    var(--card);
  display: flex; flex-direction: column; justify-content: center;
}
.eyebrow {
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 8px;
}
/* Proportional figures: tabular-nums makes a big number look loose. */
.hero-value {
  font-size: 52px; line-height: 1; font-weight: 700;
  letter-spacing: -0.03em; margin: 0 0 10px;
}
.hero-sub { color: var(--muted); font-size: 12.5px; margin: 10px 0 0; }

.stat-stack { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; margin: 0; }
/* 140px rather than 160: at 390px wide, two tiles plus the gap miss
   fitting by four pixels and every tile drops to its own row. */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; }
/*
 * ALIGNED FROM THE TOP, not centered.
 *
 * These were `justify-content: center`, and the tiles in a row do not
 * hold the same number of rows — one carries a delta, one a caption, one
 * both. Centering each independently put every icon and every label at a
 * different height, so a row read as three cards that had been dropped
 * rather than laid out. From the top they line up whatever hangs below.
 */
.stat {
  background: var(--card); border: 1px solid var(--border-soft);
  border-radius: var(--r-lg); padding: 18px;
  display: flex; flex-direction: column; justify-content: flex-start;
}
.stat-row .stat { background: var(--card-2); border-color: transparent; }
/*
 * THE BAR NEVER TAKES THE POINTER.
 *
 * Each column has a full-height transparent hit area behind it, but the
 * bar is appended after — and in SVG, later paints on top. Crossing onto
 * the bar itself therefore fired `pointerleave` on the hit area and the
 * tooltip vanished; moving a pixel re-entered it and the tooltip came
 * back. The result was a readout that flickered everywhere except the
 * empty space above a bar, which is the one place nobody points.
 *
 * Fixed here rather than by reordering the appends, because it stays true
 * however the chart is drawn later.
 */
.plot .bar, .plot .grid, .plot .tick { pointer-events: none; }

/* A glyph tile per metric: at a glance the eye finds "the money one" or
   "the people one" without reading four labels. Muted by default and
   colored only where the metric has a natural identity. */
.stat-icon {
  width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center; margin-bottom: 12px;
  background: var(--primary-soft); color: var(--primary);
  /* `flex: none` so a tile with a long caption cannot squash the badge
     and take the row out of alignment again from the other direction. */
  flex: none;
}
.stat-icon svg { display: block; }
.stat-locked .stat-icon { background: var(--border-soft); color: var(--muted); }
.stat-icon.accent { background: rgba(255, 176, 32, 0.14); color: var(--accent); }
.stat-icon.good { background: rgba(50, 210, 150, 0.14); color: var(--good); }

.stat .label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 7px;
}
.stat .value { font-size: 25px; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; }
.stat .sub { font-size: 12px; color: var(--muted); margin-top: 5px; line-height: 1.4; }

/* Direction color, neutral at zero so "no change" reads as neither. */
.delta { font-size: 12.5px; font-weight: 600; margin-top: 6px; display: inline-flex; gap: 4px; }
.delta.up { color: var(--good); }
.delta.down { color: var(--bad); }
.delta.flat { color: var(--muted); font-weight: 500; }
.hero .delta { font-size: 14px; }

/* ── Plots ──────────────────────────────────────────────────────── */
/*
 * The value axis sits OUTSIDE the scrolling area.
 *
 * Drawn inside the plot it scrolls away with the bars — and these charts
 * open at their right edge, so on a narrow screen the reader would land
 * on a chart with no scale at all. A fixed column keeps the numbers
 * present however far the plot is scrolled.
 */
.plot-frame { display: flex; align-items: flex-start; }
.axis-col { flex: 0 0 46px; display: block; }
.plot-wrap { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; }
.plot { display: block; width: 100%; }
.plot .grid { stroke: var(--border); stroke-width: 1; shape-rendering: crispEdges; }
/*
 * Ticks live in BOTH svgs — x labels inside the scrolling plot, y values
 * and day names in the fixed axis column. Scoping this to `.plot` alone
 * left the axis column at SVG's default fill, which is black, and black
 * on this surface is invisible rather than merely wrong.
 */
.plot .tick, .axis-col .tick { fill: var(--muted); font-size: 10.5px; font-variant-numeric: tabular-nums; }
/* A floor, so a future mark added to either svg cannot inherit black. */
.plot, .axis-col { fill: var(--muted); }
.plot .bar { fill: url(#barFill); }
.plot .bar-empty { fill: var(--border); }
.plot .hit { fill: transparent; cursor: pointer; }
.plot .hit:hover + .bar, .plot .hit:focus-visible + .bar { fill: var(--heat-6); }
.plot .hit:focus-visible { outline: none; }
.plot .note-dot { fill: var(--accent); }
.plot .cell { rx: 2.5; }

.heat-legend {
  display: flex; align-items: center; gap: 8px;
  color: var(--muted); font-size: 11.5px; margin-top: 12px;
}
.heat-legend .swatches { display: flex; gap: 2px; }
.heat-legend i { width: 22px; height: 9px; border-radius: 2px; display: block; }

.tooltip {
  position: fixed; z-index: 50; pointer-events: none;
  background: #15152B; border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 9px 12px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);
  font-size: 12.5px; max-width: 260px;
}
.tooltip .tip-value { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.tooltip .tip-label { color: var(--muted); margin-top: 1px; }
.tooltip .tip-note { color: var(--accent); margin-top: 5px; }

/* Every chart has a table twin, so no value is gated behind a hover. */
.table-view { margin-top: 16px; max-height: 340px; overflow: auto; }
.table-view table { width: 100%; border-collapse: collapse; font-size: 13px; }
.table-view th, .table-view td {
  text-align: right; padding: 7px 10px;
  border-bottom: 1px solid var(--border-soft);
  font-variant-numeric: tabular-nums;
}
.table-view th:first-child, .table-view td:first-child { text-align: left; font-variant-numeric: normal; }
.table-view thead th {
  position: sticky; top: 0; background: var(--card);
  color: var(--muted); font-size: 11px; letter-spacing: 0.05em;
  text-transform: uppercase; font-weight: 600;
}

/* ── Share of downtown ──────────────────────────────────────────── */
.share-row { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.share-figure { display: flex; align-items: baseline; gap: 10px; }
.share-value { font-size: 38px; font-weight: 700; letter-spacing: -0.03em; }
.share-track {
  flex: 1; min-width: 200px; height: 10px;
  background: var(--heat-1); border-radius: 999px; overflow: hidden;
}
.share-fill {
  height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--heat-3), var(--heat-6));
  transition: width 0.35s ease;
}

/* ── Door counts & notes ────────────────────────────────────────── */
.entry-form {
  display: grid; gap: 12px;
  grid-template-columns: 168px 150px minmax(0, 1fr) auto;
  align-items: end;
}
.entry-actions { display: flex; gap: 8px; }
.entry-list { margin-top: 16px; }
.entry-row {
  display: grid; grid-template-columns: 104px 150px minmax(0, 1fr);
  gap: 14px; padding: 11px 0; font-size: 13px;
  border-bottom: 1px solid var(--border-soft);
}
.entry-row:last-child { border-bottom: none; }
.entry-date { color: var(--text-2); font-variant-numeric: tabular-nums; }
.entry-door { color: var(--primary); font-weight: 600; }
.entry-note { color: var(--text); }
.empty { color: var(--muted); font-size: 13px; }

/* ── Narrow screens ─────────────────────────────────────────────────
 * An owner checks this on a phone behind the bar. The page must never
 * scroll sideways — a wide plot scrolls inside .plot-wrap, but the page
 * itself drifting is broken.
 */
@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; }
  .stat-stack { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .entry-form { grid-template-columns: 1fr 1fr; }
  .entry-form .field-grow { grid-column: 1 / -1; }
  .entry-actions { grid-column: 1 / -1; }
  .entry-actions .btn { flex: 1; }
}

@media (max-width: 560px) {
  .topbar { height: auto; flex-wrap: wrap; gap: 8px 10px; padding: 12px 16px; }
  .topbar-sep, .who { display: none; }
  .brand { font-size: 17px; }
  .wrap { padding: 16px 14px 56px; }
  .card { padding: 16px; border-radius: 14px; }
  .toolbar { gap: 8px; }
  .segmented { flex: 1 1 100%; justify-content: space-between; }
  .segmented button { flex: 1; padding: 8px 6px; }
  .freshness { flex: 1 1 100%; }
  .toolbar .btn { flex: 1; }
  .hero-value { font-size: 42px; }
  .stat-stack { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .stat { padding: 14px; }
  .stat .value { font-size: 21px; }
  .entry-form { grid-template-columns: 1fr; }
  .entry-row { grid-template-columns: 1fr; gap: 2px; }
  .share-value { font-size: 30px; }
}

/* ── Notices, insight, empty state ──────────────────────────────── */
.notice {
  border-radius: var(--r-md); padding: 12px 16px; margin-bottom: 16px;
  font-size: 13.5px; line-height: 1.5;
  border: 1px solid transparent;
}
.notice-warn {
  background: rgba(255, 176, 32, 0.10);
  border-color: rgba(255, 176, 32, 0.32);
  color: #FFD79A;
}

/* The sentence that says what happened, for a reader who did not come
   here to study a chart. Sits above the figures it describes. */
.insight {
  font-size: 17px; font-weight: 600; letter-spacing: -0.01em;
  line-height: 1.45; margin: 0 0 18px; padding-left: 14px;
  border-left: 3px solid var(--primary);
  color: var(--text);
}

.empty-card { border-color: var(--border); }
.empty-list { margin: 0 0 18px; padding-left: 20px; color: var(--text-2); font-size: 13.5px; }
.empty-list li { margin-bottom: 8px; line-height: 1.55; }
.empty-list b { color: var(--text); font-weight: 600; }
.dimmed { opacity: 0.45; }

/* ── Tour ───────────────────────────────────────────────────────── */
.tour-scrim {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 20px;
  background: rgba(6, 6, 16, 0.72);
  backdrop-filter: blur(3px);
}
.tour-card {
  width: min(440px, 100%);
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 26px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}
.tour-step {
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted); margin: 0 0 10px;
}
.tour-title { font-size: 21px; margin: 0 0 10px; letter-spacing: -0.02em; }
.tour-blurb { color: var(--text-2); font-size: 14px; line-height: 1.6; margin: 0 0 20px; }
.tour-dots { display: flex; gap: 6px; margin-bottom: 20px; }
.tour-dots i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--border); display: block; transition: background 0.16s, width 0.16s;
}
.tour-dots i.on { background: var(--primary); width: 20px; border-radius: 3px; }
.tour-actions { display: flex; align-items: center; gap: 8px; }

/* ── Print ──────────────────────────────────────────────────────────
 * Owners forward these to a partner, a landlord, or an accountant. The
 * page is dark on screen and that is right there; on paper it wastes a
 * cartridge and reads worse, so print gets ink on white.
 */
@media print {
  body { background: #fff; color: #111; }
  .topbar, .toolbar, #notesCard, .table-toggle, .tour-scrim, .tooltip,
  .btn, .combo-chevron { display: none !important; }
  .card, .stat, .hero {
    background: #fff !important; border: 1px solid #ccc !important;
    break-inside: avoid; page-break-inside: avoid; margin-bottom: 12px;
  }
  .wrap { max-width: none; padding: 0; }
  .hero-grid { grid-template-columns: 1fr 2fr; }
  .blurb, .stat .sub, .footnote, .plot .tick, .axis-col .tick { color: #555; fill: #555; }
  .hero-value, .stat .value, .card-head h2, .insight { color: #111; }
  .insight { border-left-color: #666; }
  /* A chart that scrolls on screen must show whole on paper. */
  .plot-wrap { overflow: visible !important; }
  .plot { min-width: 0 !important; }
  /* Table twins are the print-friendly form — show them all. */
  .table-view { display: block !important; max-height: none; }
  .table-view th, .table-view td { border-bottom: 1px solid #ddd; }
  .table-view thead th { background: #fff; color: #333; }
}

/* ── Locked (paid) metrics ──────────────────────────────────────────
 * Shown with their name and a lock rather than removed. A hole where a
 * figure should be reads as a fault in the product; a named lock is
 * honest about what exists and is the only upgrade prompt needed.
 */
.stat-locked { position: relative; border-style: dashed; border-color: var(--border); }
.stat-locked .value { color: var(--muted); }
.stat-lock {
  display: inline-block; font-size: 10px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--primary); background: var(--primary-soft);
  border-radius: 999px; padding: 3px 8px;
}
.stat-locked .stat-lock { position: absolute; top: 12px; right: 12px; }

.card-locked { border-style: dashed; }
.locked-note {
  display: flex; align-items: center; gap: 10px;
  color: var(--text-2); font-size: 13.5px;
}

@media print {
  /* A locked metric is not a figure — it should not print as one. */
  .stat-locked, .locked-note { display: none !important; }
}

/* An off state reads as neutral, not as an error — turning the email off
   is a legitimate choice, not a fault. */
.pill-off { color: var(--muted); background: rgba(139, 134, 160, 0.14); }


/* ── Custom date range ─────────────────────────────────────────────
   Anchored under the segmented control rather than centered over the
   page. Picking a range adjusts what is already on screen; a modal
   would stop the page to ask a small question. */
.range-pop {
  position: absolute; z-index: 40; margin-top: 8px;
  width: 268px; padding: 14px;
  background: var(--card-2); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: 0 18px 40px rgba(0, 0, 0, 0.38);
  display: flex; flex-direction: column; gap: 10px;
}
.range-shortcuts { display: flex; flex-wrap: wrap; gap: 6px; }
.range-shortcuts button {
  font: inherit; font-size: 12px; cursor: pointer;
  padding: 5px 9px; border-radius: 999px;
  background: var(--card); color: var(--text);
  border: 1px solid var(--border-soft);
}
.range-shortcuts button:hover { border-color: var(--primary); color: var(--primary); }
/* The calendar. Hand-built for the same reason the venue picker is not a
   <select>: the native control is a different thing in every browser. */
.cal { user-select: none; }
.cal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px;
}
.cal-month { font-size: 13px; font-weight: 700; letter-spacing: -0.01em; }
.cal-nav {
  font: inherit; font-size: 17px; line-height: 1; cursor: pointer;
  width: 26px; height: 26px; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); border: 1px solid transparent;
}
.cal-nav:hover:not(:disabled) { background: var(--card); color: var(--text); }
.cal-nav:disabled { opacity: 0.3; cursor: default; }
.cal-dows, .cal-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.cal-dows {
  margin-bottom: 4px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
  color: var(--muted); text-align: center;
}
.cal-day {
  font: inherit; font-size: 12px; cursor: pointer;
  aspect-ratio: 1; display: grid; place-items: center;
  border: 1px solid transparent; border-radius: var(--r-sm);
  background: transparent; color: var(--text);
}
.cal-day:hover:not(:disabled) { background: var(--card); }
.cal-day:disabled { color: var(--border); cursor: default; }
.cal-day.blank { visibility: hidden; }
/* Square middles, rounded ends: the run reads as one range rather than
   as a row of separately selected days. */
.cal-day.in-range { background: var(--primary-soft); border-radius: 0; }

/* ── Single-date picker ──────────────────────────────────────────────
 * The trigger and popover for a one-off event's date. The CALENDAR
 * inside is the same `.cal*` markup and CSS the range picker uses; only
 * the surrounding shell and the selection rules differ, because a range
 * has two ends and looks backwards while this has one and looks forward.
 */
.datepick { position: relative; }
.datepick-button {
  display: flex; align-items: center; gap: 8px; width: 100%;
  height: 40px; padding: 0 12px;
  background: var(--bg-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.datepick-button:hover { background: var(--card-2); }
.datepick-button:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Same treatment the inputs give their placeholder, so an unset date
   reads as unset rather than as a value. */
.datepick-button.empty { color: #6A6580; }
.datepick-caption { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.datepick-pop {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  min-width: 268px; padding: 12px;
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}
/* Opens rightwards from a narrow field; flipped when it would overflow. */
.datepick-pop.flip { left: auto; right: 0; }
.datepick-actions {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 10px;
}

@media (max-width: 560px) {
  /* The field is full width down here, so the popover matches it rather
     than hanging off the side of the card. */
  .datepick-pop { left: 0; right: 0; min-width: 0; }
}
.cal-day.edge {
  background: var(--primary); color: #fff; font-weight: 700; border-radius: var(--r-sm);
}
.cal-day.edge.start:not(.end) { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.cal-day.edge.end:not(.start) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.cal-day.today:not(.edge) { border-color: var(--border); }
.range-picked {
  font-size: 12px; color: var(--text); margin: 0; min-height: 16px;
}
.range-note { font-size: 11.5px; line-height: 1.45; color: var(--muted); margin: 0; min-height: 16px; }
.range-note.bad { color: var(--bad, #FF4D5A); }
.range-actions { display: flex; justify-content: flex-end; gap: 8px; }


/* ── Tabs ────────────────────────────────────────────────────────────
 *
 * Underline rather than pills: this is a section switch, not a filter, and
 * the page already uses pills for status. Full-width on a phone so both
 * are a comfortable target.
 */
.tabs {
  display: flex;
  gap: 4px;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--border-soft);
}
.tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 10px 14px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.14s, border-color 0.14s;
}
.tab:hover { color: var(--text-2); }
.tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--primary); }
.tab:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}
/* The jump target flashes so it is obvious which card you were sent to —
   landing at the top of an unfamiliar tab otherwise looks like nothing
   happened. */
@keyframes cardArrive {
  from { box-shadow: 0 0 0 2px var(--primary); }
  to   { box-shadow: 0 0 0 2px transparent; }
}
.card-arrived { animation: cardArrive 1.6s ease-out; }
@media (prefers-reduced-motion: reduce) { .card-arrived { animation: none; } }

@media (max-width: 560px) {
  .tab { flex: 1; text-align: center; padding: 10px 6px; }
}

/* ── Opening hours editor ───────────────────────────────────────────
 *
 * Two columns on a laptop, one on a phone — a bar owner is as likely to
 * be doing this on their phone behind the bar as at a desk.
 *
 * The inputs inherit the bare `input` rule above; only what differs in a
 * grid is set here.
 */
.hours-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 20px;
  margin: 14px 0 4px;
}
.hours-row {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr) 7rem;
  align-items: center;
  gap: 8px;
}
.hours-day { font-size: 13px; color: var(--text-2); }
/* Without min-width:0 a long value refuses to shrink and pushes the
   preview off the end of the row. */
.hours-row input { min-width: 0; padding: 8px 10px; }
.hours-row input:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-soft);
}
/* What the app will actually do with what was typed. Quiet by default,
   loud only when it could not be read. */
.hours-note {
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.hours-bad { color: var(--bad); font-weight: 600; white-space: normal; }

/* Scoped to this card rather than `.entry-actions` itself, which the door
   counts card also uses and where the button sits right under its own
   fields on purpose.
   35px matches the space below the button — the empty status line's 12px
   margin plus the card's 22px padding — so the button sits centered
   between the hint above it and the bottom of the card. Measured, not
   guessed; `gapAbove - gapBelow` is 0. */
#hoursCard .entry-actions { margin-top: 35px; }

@media (max-width: 720px) {
  .hours-grid { grid-template-columns: minmax(0, 1fr); }
  /* One column of rows, and the preview drops under the input rather than
     competing with it for width — at 390px three columns left the field
     too narrow to read "4 PM – 2:30 AM" without scrolling it. */
  .hours-row { grid-template-columns: 4.5rem minmax(0, 1fr); row-gap: 2px; }
  .hours-note { grid-column: 2; }
}


/* ── Deal and event editors ──────────────────────────────────────────
 *
 * A form above a list, the same shape the door-counts card already uses:
 * editing a row loads it back into the form rather than turning every row
 * into its own set of inputs. Fewer controls on screen, and only one thing
 * is ever being edited.
 */
.editor-grid {
  display: flex;
  flex-wrap: wrap;
  /* Row gap leaves room for the preview hanging under a field. */
  gap: 26px 14px;
  align-items: flex-end;
  margin: 14px 0 0;
}
.editor-grid .field { display: flex; flex-direction: column; gap: 5px; position: relative; }
.editor-grid .field > span:first-child { font-size: 12px; color: var(--text-2); }
/*
 * The parse preview hangs BELOW its field rather than sitting inside it.
 * In the flow it made that one field taller, and `align-items: flex-end`
 * then pushed its label out of line with the others — the When column sat
 * visibly higher than What is it and Kind. Out of the flow, every field is
 * the same height and the row gap leaves room underneath.
 *
 * The reset matters too: `.field > span` uppercases every direct span, so
 * the preview was rendering as "NO TIME SET" — indistinguishable from a
 * field label rather than a value.
 */
.editor-grid .hours-note {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}
.editor-grid .field-grow { flex: 1 1 220px; min-width: 0; }
.editor-grid .field-narrow { flex: 0 1 150px; min-width: 0; }
/*
 * A select and a text input do not render to the same height from the same
 * padding — the select came out 4px shorter, and `align-items: flex-end`
 * then dropped its label out of line with its neighbours. Pin both to one
 * height rather than trying to match padding.
 */
.editor-grid input, .editor-grid select { height: 40px; }
.editor-grid select {
  font: inherit; font-size: 14px; color: var(--text);
  background: var(--bg-raised); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 0 12px; width: 100%;
}
.editor-grid select:focus-visible {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft);
}

/* Day pickers: a tick per day, none ticked meaning every day. Checkboxes
   rather than a multi-select because the whole week has to be readable at
   a glance in a loud room. */
.daybox-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 0; }
.daybox {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 11px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-raised);
  font-size: 13px; color: var(--text-2); cursor: pointer;
  transition: border-color 0.14s, color 0.14s, background 0.14s;
}
.daybox input { width: auto; margin: 0; accent-color: var(--primary); }
.daybox:hover { color: var(--text); }
.daybox:has(input:checked) {
  border-color: var(--primary); background: var(--primary-soft); color: var(--text);
}
.daybox:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 2px; }

/* The button belongs to the form above it, not to the list below.
   It was flush against the hint text, which read as part of the
   paragraph. Same problem the hours card had, same fix. */
#dealsCard .entry-actions,
#eventsCard .entry-actions { margin-top: 20px; }

/* What already exists is a different thing from what you are adding, and
   these cards ran the two together with nothing between them. The rule
   is the boundary. Scoped by id because `.entry-list` is also the notes
   card, where the list IS the card and a divider would be a stray line. */
#dealList, #eventList {
  margin-top: 20px;
  padding-top: 6px;
  border-top: 1px solid var(--border-soft);
}
#dealList .empty, #eventList .empty { margin: 14px 0 2px; }

/* ── One row per saved deal or event ─────────────────────────────────
 * TWO LINES, not one. These used to be a single baseline-aligned strip
 * with the whole of a deal joined by "·" — "Happy hour · 4 PM – 7 PM ·
 * Mon, Tue, Wed" — so the name, the kind, the time and the days all had
 * equal weight and none of them could be found without reading the
 * sentence. The name is what you scan for and the rest is what you
 * check, so the name gets its own line and the rest sits under it.
 */
.item-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 5px 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--border-soft);
}
.item-row:last-child { border-bottom: none; }
.item-title {
  grid-column: 1;
  color: var(--text); font-weight: 600; font-size: 14px;
  overflow-wrap: anywhere;
}
/* Spans both lines so Edit/Remove sit against the row, not against the
   title — with a one-line row and a two-line row in the same list,
   top-aligning them made the buttons jump around. */
.item-actions {
  grid-column: 2; grid-row: 1 / span 2;
  display: flex; gap: 4px; align-self: center;
}
.item-sub {
  grid-column: 1;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px; font-size: 12.5px; color: var(--muted);
}
.item-badge {
  font-size: 11px; font-weight: 600; letter-spacing: 0.03em;
  color: var(--text-2); background: var(--bg-raised);
  border: 1px solid var(--border); border-radius: 999px;
  padding: 2px 9px; white-space: nowrap;
}
.item-when { color: var(--text-2); font-variant-numeric: tabular-nums; }
/* Visible, but quieter than the facts it separates. It was --border
   first, which is within a shade of the card behind it — a separator
   nobody can see is just a gap with extra markup. */
.item-dot { color: var(--muted); opacity: 0.5; }

/* The seven days, in the same shape as the checkboxes that set them.
   "Mon, Tue, Wed, Thu" is four words to read; this is one glance, and it
   matches the control directly above so the mapping is obvious. */
.item-days { display: inline-flex; gap: 3px; }
.item-day {
  width: 18px; height: 18px; border-radius: 5px;
  display: grid; place-items: center;
  font-size: 10.5px; font-weight: 600; line-height: 1;
  color: var(--muted); background: var(--bg-raised);
}
.item-day-on {
  color: var(--text); background: var(--primary-soft);
  box-shadow: inset 0 0 0 1px var(--primary);
}

/* A deal whose time could not be read still shows, flagged — hiding it
   would lose it, and the app cannot say whether it is running. */
.item-warn { color: var(--accent); }
/* Remove is destructive and looked identical to Edit. Only on hover, so
   the row is not a wall of red at rest. */
.btn-danger-link:hover { color: var(--bad); }

@media (max-width: 560px) {
  .editor-grid .field-narrow, .editor-grid .field-grow { flex: 1 1 100%; }
  /* Actions drop to their own line rather than squeezing the title. */
  .item-row { grid-template-columns: minmax(0, 1fr); }
  .item-actions { grid-column: 1; grid-row: auto; justify-content: flex-end; }
}
