/* ═══════════════════════════════════════════════════════════════════════════
   SYFT V2 — BUSINESS DASHBOARD
   ═══════════════════════════════════════════════════════════════════════════
   Desktop-first, works down to a phone. Restaurant owners set deals up on a
   laptop in the office and check numbers on their phone in the dining room.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  --amber:       #F59E0B;
  --amber-deep:  #D97706;
  --cream:       #FFFBF0;
  --warm-white:  #FEF9EE;
  --text:        #1C0A00;
  --muted:       #92400E;
  --soft:        #B45309;
  --surface:     #FFFFFF;
  --border:      #FDE68A;
  --border-soft: #FEF3C7;
  --line:        #F0E4C8;
  --success:     #16A34A;
  --danger:      #DC2626;
  --pill:        #FEF3C7;
  --font-num:    'Space Grotesk', system-ui, sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--warm-white);
  font-family: 'DM Sans', system-ui, sans-serif;
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font-family: inherit; color: inherit; font-size: inherit; }
button { cursor: pointer; border: none; background: none; }

/* ── Layout ───────────────────────────────────────────────────────────────── */
.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: 236px;
  flex-shrink: 0;
  background: var(--surface);
  border-right: 1px solid var(--line);
  padding: 26px 16px;
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.logo {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 25px;
  font-weight: 800;
  color: var(--amber-deep);
  letter-spacing: -0.03em;
  padding: 0 10px 4px;
}
.logo-sub { font-size: 11px; color: var(--soft); padding: 0 10px 22px; }

.side-link {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 11px 12px;
  border-radius: 10px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--muted);
  text-align: left;
  margin-bottom: 2px;
}
.side-link:hover  { background: var(--cream); }
.side-link.active { background: var(--pill); color: var(--amber-deep); }
.side-link svg { width: 18px; height: 18px; flex-shrink: 0; }
.side-link .badge {
  margin-left: auto;
  background: var(--danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
}

.side-foot { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--line); }

.main { flex: 1; min-width: 0; padding: 30px 34px 60px; }

.page { display: none; }
.page.active { display: block; }

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
.page-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 27px;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.page-sub { font-size: 14px; color: var(--muted); margin-top: 3px; }

/* ── Cards ────────────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 15px;
  padding: 20px 22px;
  margin-bottom: 16px;
}
.card-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 14px;
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  gap: 14px;
  margin-bottom: 20px;
}
.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 17px 19px;
}
.stat-label { font-size: 12px; color: var(--muted); font-weight: 600; }
.stat-value {
  font-family: var(--font-num);
  font-size: 29px;
  font-weight: 700;
  margin-top: 5px;
  line-height: 1;
}
.stat-hint { font-size: 11.5px; color: var(--soft); margin-top: 5px; }

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.btn {
  background: var(--amber);
  color: #fff;
  border-radius: 11px;
  padding: 12px 20px;
  font-size: 14.5px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
}
.btn:active { transform: scale(0.985); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn.full { width: 100%; }
.btn.secondary { background: var(--cream); color: var(--muted); border: 1.5px solid var(--border); }
.btn.danger    { background: #FEE2E2; color: var(--danger); }
.btn.small     { padding: 8px 14px; font-size: 13px; border-radius: 9px; }

/* ── Forms ────────────────────────────────────────────────────────────────── */
.field-label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  margin: 16px 0 7px;
}
/* Only on the fields that genuinely are. A form where half the labels shout
   "Required" has taught the reader to stop reading them. */
.field-label .req {
  margin-left: 6px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--amber-deep);
}
.field {
  width: 100%;
  background: var(--cream);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 14.5px;
  outline: none;
}
.field:focus { border-color: var(--amber); }
.field::placeholder { color: #C9A227; }
select.field { cursor: pointer; }
textarea.field { resize: vertical; min-height: 74px; }

.field-row { display: flex; gap: 11px; flex-wrap: wrap; }
.field-row > * { flex: 1; min-width: 128px; }

.field-hint { font-size: 12px; color: var(--soft); margin-top: 6px; line-height: 1.5; }

.chip {
  background: var(--cream);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  padding: 8px 15px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--soft);
}
.chip.selected { background: var(--amber); border-color: var(--amber); color: #fff; }
.chip-row { display: flex; flex-wrap: wrap; gap: 8px; }

.form-error {
  color: var(--danger);
  font-size: 13.5px;
  font-weight: 600;
  min-height: 19px;
  margin-top: 10px;
}

/* ── Deal templates ───────────────────────────────────────────────────────── */
/* Four buttons that fill in the whole schedule. Most restaurants want one of
   the first three, and the ones who do not still get a working starting point
   instead of an empty form. */
.template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(152px, 1fr));
  gap: 10px;
  margin-bottom: 6px;
}
.template {
  background: var(--cream);
  border: 2px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  text-align: left;
}
.template.selected { border-color: var(--amber); background: var(--pill); }
.template-name { font-size: 14.5px; font-weight: 700; }
.template-hint { font-size: 12px; color: var(--soft); margin-top: 3px; }

/* ── Day picker ───────────────────────────────────────────────────────────── */
.day-row { display: flex; gap: 7px; flex-wrap: wrap; }
.day-btn {
  width: 50px;
  padding: 11px 0;
  background: var(--cream);
  border: 1.5px solid var(--border);
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--soft);
  text-align: center;
}
.day-btn.selected { background: var(--amber); border-color: var(--amber); color: #fff; }

.day-shortcuts { display: flex; gap: 7px; margin-top: 9px; flex-wrap: wrap; }
.day-shortcut {
  font-size: 12px;
  font-weight: 600;
  color: var(--amber-deep);
  background: var(--pill);
  border-radius: 999px;
  padding: 5px 11px;
}

/* ── Schedule preview ─────────────────────────────────────────────────────── */
/* Restates the schedule in plain language plus the resulting cost. The cost
   line is the whole point of the daily-fee model — it should never be a
   surprise on an invoice. */
.schedule-preview {
  background: var(--pill);
  border-radius: 11px;
  padding: 14px 16px;
  margin-top: 16px;
}
.schedule-preview .headline {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--amber-deep);
  line-height: 1.45;
}
.schedule-preview .cost { font-size: 12.5px; color: var(--soft); margin-top: 6px; line-height: 1.5; }

/* ── Deal rows ────────────────────────────────────────────────────────────── */
.deal-row {
  display: flex;
  gap: 15px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 17px 19px;
  margin-bottom: 11px;
  flex-wrap: wrap;
}
.deal-row.paused { opacity: 0.6; }

.live-dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #D6D3D1;
  flex-shrink: 0;
}
.live-dot.live { background: var(--success); box-shadow: 0 0 0 4px rgba(22, 163, 74, 0.16); }

.deal-info { flex: 1; min-width: 190px; }
.deal-name { font-size: 15.5px; font-weight: 700; }
.deal-schedule { font-size: 12.5px; color: var(--soft); margin-top: 3px; }

.deal-stats { display: flex; gap: 22px; }
.deal-stat { text-align: center; }
.deal-stat .n { font-family: var(--font-num); font-size: 18px; font-weight: 700; }
.deal-stat .l { font-size: 10.5px; color: var(--soft); text-transform: uppercase; letter-spacing: 0.03em; }

.deal-actions { display: flex; gap: 7px; }

.tag {
  display: inline-block;
  background: var(--pill);
  color: var(--amber-deep);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
}
.tag.live   { background: #DCFCE7; color: #15803D; }
.tag.paused { background: #F5F5F4; color: #78716C; }

/* ── Feedback ─────────────────────────────────────────────────────────────── */
.feedback-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 4px solid var(--success);
  border-radius: 13px;
  padding: 17px 19px;
  margin-bottom: 12px;
}
.feedback-card.low { border-left-color: var(--danger); }
.feedback-card.mid { border-left-color: var(--amber); }

.fb-head {
  display: flex;
  align-items: center;
  gap: 11px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.fb-code {
  font-family: var(--font-num);
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.fb-meta { font-size: 12.5px; color: var(--soft); }
.fb-score {
  margin-left: auto;
  font-family: var(--font-num);
  font-size: 20px;
  font-weight: 700;
}
.fb-score.low { color: var(--danger); }

.fb-breakdown { display: flex; gap: 18px; flex-wrap: wrap; margin: 10px 0; }
.fb-metric { font-size: 12.5px; color: var(--soft); }
.fb-metric b { color: var(--text); font-family: var(--font-num); }

.fb-comment {
  background: var(--cream);
  border-radius: 9px;
  padding: 12px 14px;
  font-size: 14px;
  line-height: 1.55;
  margin: 10px 0;
}

/* ── Staff table ──────────────────────────────────────────────────────────── */
.staff-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.staff-row:last-child { border-bottom: none; }
.staff-avatar {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--pill);
  color: var(--amber-deep);
  display: grid;
  place-items: center;
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
.staff-name { font-size: 14.5px; font-weight: 700; }
.staff-email { font-size: 12.5px; color: var(--soft); }
.staff-inactive { opacity: 0.5; }

/* ── Modal ────────────────────────────────────────────────────────────────── */
.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 900;
  background: rgba(28, 10, 0, 0.45);
  display: none;
  align-items: flex-start;
  justify-content: center;
  padding: 32px 18px;
  overflow-y: auto;
}
.modal-backdrop.open { display: flex; }

.modal {
  background: var(--surface);
  border-radius: 18px;
  padding: 26px 28px 30px;
  width: 100%;
  max-width: 580px;
  margin: auto 0;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}
.modal-title {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -0.01em;
}
.modal-close { font-size: 24px; color: var(--soft); line-height: 1; padding: 4px 8px; }

/* ── Banners ──────────────────────────────────────────────────────────────── */
.banner {
  background: var(--pill);
  border-radius: 12px;
  padding: 15px 18px;
  margin-bottom: 18px;
  font-size: 13.5px;
  color: var(--amber-deep);
  line-height: 1.55;
}
.banner b { font-weight: 700; }
.banner.warn { background: #FEE2E2; color: #B91C1C; }

.empty { text-align: center; padding: 52px 22px; color: var(--muted); }
.empty-title { font-size: 16px; font-weight: 700; margin-bottom: 5px; }
.empty-text  { font-size: 13.5px; line-height: 1.6; opacity: 0.85; max-width: 400px; margin: 0 auto; }

.toast {
  position: fixed;
  bottom: 26px;
  left: 50%;
  transform: translate(-50%, 18px);
  z-index: 950;
  background: var(--text);
  color: var(--cream);
  border-radius: 999px;
  padding: 13px 22px;
  font-size: 14px;
  font-weight: 600;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

.spinner {
  width: 26px; height: 26px;
  border: 3px solid var(--border);
  border-top-color: var(--amber);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  margin: 44px auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.hidden { display: none !important; }

/* ── Auth screen ──────────────────────────────────────────────────────────── */
/* Centred with `margin: auto` on the card rather than `align-items: center`.
   A centred flex item that is taller than its container overflows equally in
   both directions, and the overflow above the top is unreachable — you cannot
   scroll back past the start of a page. `margin: auto` centres when there is
   room and behaves like a normal block when there is not, which is the whole
   difference between a long form you can fill in and one whose first field is
   somewhere above the ceiling. */
.auth-wrap {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  padding: 24px;
  overflow-y: auto;
}
.auth-wrap > .auth-card { margin: auto; }
.auth-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 34px 32px;
  width: 100%;
  max-width: 430px;
}

/* ── Hour chart ─────────────────────────────────────────────────────────────
   A real chart, not a row of coloured slabs: a labelled y-axis, gridlines to
   read values against, and an hour under every bar.

   Grid gives the y-axis a fixed column and lets the plot take the rest, so the
   bars and the hour labels underneath share exactly the same track widths and
   stay aligned however many hours are shown. */
.chart {
  display: grid;
  grid-template-columns: 40px 1fr;
  grid-template-rows: auto auto;
  margin-top: 10px;
}

/* Y axis. Labels sit ON their gridline rather than between lines, so the
   number reads as the height of that line. */
.chart-y {
  position: relative;
  height: 170px;
  grid-column: 1;
}
.chart-y span {
  position: absolute;
  right: 9px;
  transform: translateY(-50%);
  font-family: var(--font-num);
  font-size: 11px;
  color: var(--soft);
  font-variant-numeric: tabular-nums;
}

.chart-plot {
  position: relative;
  height: 170px;
  grid-column: 2;
  border-left: 1px solid var(--line);
  border-bottom: 1.5px solid var(--border);
}
.chart-grid {
  position: absolute;
  left: 0; right: 0;
  border-top: 1px dashed var(--line);
}

.chart-bars {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  gap: 4px;
  padding: 0 4px;
}
.chart-bar {
  flex: 1;
  position: relative;
  border-radius: 4px 4px 0 0;
  background: var(--amber);
  min-width: 0;
  transition: background 0.15s;
}
/* An hour with no redemptions draws nothing. A 2px stub reads as a real value
   and makes the baseline look ragged. */
.chart-bar.empty { background: transparent; }
.chart-bar.peak  { background: var(--amber-deep); }
.chart-bar:hover { background: var(--text); }

/* The value above the tallest bar, so the chart answers "how many" without a
   hover on the number that matters most. */
.chart-bar .peak-value {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 4px;
  font-family: var(--font-num);
  font-size: 11px;
  font-weight: 700;
  color: var(--amber-deep);
  white-space: nowrap;
}

.chart-x {
  grid-column: 2;
  display: flex;
  gap: 4px;
  padding: 6px 4px 0;
}
.chart-x span {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: 10px;
  color: var(--soft);
  white-space: nowrap;
}
/* Noon and 6pm anchor the day — darker so the eye can find its place without
   reading every label. */
.chart-x span.anchor { color: var(--muted); font-weight: 700; }

.chart-empty {
  height: 170px;
  display: grid;
  place-items: center;
  font-size: 13.5px;
  color: var(--soft);
}

@media (max-width: 700px) {
  .chart-x span { font-size: 9px; }
  .chart-x span.minor { visibility: hidden; }
}

/* ── Responsive ───────────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .shell { flex-direction: column; }
  .sidebar {
    width: 100%;
    height: auto;
    position: static;
    flex-direction: row;
    overflow-x: auto;
    padding: 12px;
    gap: 6px;
    align-items: center;
    border-right: none;
    border-bottom: 1px solid var(--line);
  }
  .logo, .logo-sub, .side-foot { display: none; }
  .side-link { width: auto; white-space: nowrap; margin-bottom: 0; padding: 9px 13px; }
  .main { padding: 20px 16px 50px; }
  .deal-stats { width: 100%; justify-content: flex-start; }
}

/* ── Deal photo ──────────────────────────────────────────────────────────────
   Stock is what a deal is born with, so this is never an empty upload box. It
   shows something from the moment the form opens, and "Use your own" reads as
   an offer instead of another field to fill in. */
.photo-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin-top: 8px;
}
.photo-preview {
  flex-shrink: 0;
  width: 104px;
  height: 78px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--pill);
  overflow: hidden;
  display: grid;
  place-items: center;
  font-size: 30px;
}
.photo-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-actions { flex: 1; min-width: 0; }
.photo-state {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 8px;
}
.photo-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.btn.ghost {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
}

/* ── Photo picker ─────────────────────────────────────────────────────────── */
.photo-picker-section-label {
  font-size: 12px;
  font-weight: 700;
  color: var(--soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 14px 0 8px;
}
.photo-picker-section-label:first-child { margin-top: 0; }
.photo-picker-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.photo-picker-item {
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  cursor: pointer;
  background: var(--pill);
  padding: 0;
  text-align: left;
}
.photo-picker-item:active { transform: scale(0.985); }
.photo-picker-item img { width: 100%; height: 64px; object-fit: cover; display: block; }
.photo-picker-item span {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  padding: 5px 6px 6px;
  line-height: 1.2;
}
.photo-picker-item.selected {
  border-color: var(--amber-deep);
  box-shadow: 0 0 0 2px var(--amber-deep);
}


/* ═══════════════════════════════════════════════════════════════════════════
   POSTING A DEAL
   ═══════════════════════════════════════════════════════════════════════════
   Pick a template, check the card, post. The full form is still there, one tap
   behind "More options", and it is what an owner editing a deal sees straight
   away — there is no template for the thing you already made.
   ═══════════════════════════════════════════════════════════════════════════ */
.step-lead {
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 12px;
  line-height: 1.45;
}

/* ── At the limit ────────────────────────────────────────────────────────────
   Amber, not red. Running every deal your plan allows is not a fault, it is a
   full shelf — and a restaurant that has filled its allowance is the one doing
   the right thing. Red would tell them they had broken something. */
.limit-note {
  margin: -4px 0 14px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--amber);
  border-radius: 8px;
  background: var(--pill);
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}
.limit-actions { display: flex; justify-content: flex-end; margin-top: 18px; }

/* ── Templates ───────────────────────────────────────────────────────────────
   Two columns of whole deals. The offer is the second line and the largest
   thing after the name, because "50% off draft beer" is what an owner is
   choosing between — the hours are a detail they will adjust anyway. */
.template-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}
.template {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px 11px;
  text-align: left;
  background: var(--surface);
  cursor: pointer;
}
.template:active { transform: scale(0.985); }
.template-icon { font-size: 22px; line-height: 1; margin-bottom: 7px; }
.template-name { font-size: 13.5px; font-weight: 700; line-height: 1.2; }
.template-offer {
  font-size: 12px;
  font-weight: 600;
  color: var(--amber-deep);
  margin-top: 3px;
  line-height: 1.25;
}
.template-hint { font-size: 11px; color: var(--soft); margin-top: 4px; }

/* The escape hatch, and the last thing in the grid. Quieter than the real
   templates because choosing it is choosing more work. */
.template.custom {
  grid-column: 1 / -1;
  background: var(--pill);
  border-style: dashed;
  text-align: center;
}
.template.custom .template-icon { margin-bottom: 4px; }

/* ── The confirmation ────────────────────────────────────────────────────────
   The customer's card, drawn the same way the app draws it. An owner asking
   "is this right" is asking what people will see, and the honest answer is to
   show them rather than describe it. */
/* Held to roughly the width it has on a phone. Stretched across a desktop
   modal the same markup reads as a banner, and the owner is being shown this
   precisely so they know what it looks like in the feed. */
.preview-card {
  border: 1px solid var(--border);
  border-radius: 13px;
  overflow: hidden;
  background: var(--surface);
  margin: 0 auto 14px;
  max-width: 330px;
}
.preview-photo {
  position: relative;
  height: 132px;
  background: var(--pill);
  display: grid;
  place-items: center;
  font-size: 34px;
  overflow: hidden;
}
/* Absolute, not `height: 100%` — a percentage height on a centred grid item
   resolves against a row being sized by that item, and the browser falls back
   to the image's own aspect ratio. Same bug bit the customer feed. */
.preview-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.preview-offer {
  position: absolute;
  right: 7px;
  bottom: 7px;
  background: rgba(0, 0, 0, 0.66);
  color: #fff;
  border-radius: 7px;
  padding: 2px 9px;
  font-size: 14px;
  font-weight: 700;
}
.preview-text { padding: 9px 11px 11px; }
.preview-what { font-size: 13.5px; font-weight: 700; }
.preview-what .cond { color: #B91C1C; }
.preview-biz { font-size: 11.5px; font-weight: 600; color: var(--muted); margin-top: 2px; }
.preview-facts { font-size: 11.5px; color: var(--soft); margin-top: 3px; }

/* ── Basics / Advanced ───────────────────────────────────────────────────────
   Description, fine print, redemption limit, stacking. Real settings that a
   few restaurants need and most never touch — a second tab rather than a
   wall of fields everyone sees on every deal, on the same reasoning as the
   collapsed "More options" this replaced: not simple to remove them, simple
   to put them one tap away.

   Reuses .chip / .chip-row for the tab switcher itself — same look as the
   schedule-type toggle above, so a restaurant sees one pattern for "pick one
   of these" throughout the form, not two. */
.checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
}
.checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin-top: 2px;
  flex-shrink: 0;
  accent-color: var(--amber-deep);
  cursor: pointer;
}

/* Honest, and deliberately quiet. The cost belongs on the screen where someone
   commits, but making it loud right above the button buys hesitation at the
   exact moment we want none. */
.fee-note {
  font-size: 11.5px;
  color: var(--muted);
  text-align: center;
  margin-top: 14px;
  line-height: 1.5;
}


/* ═══════════════════════════════════════════════════════════════════════════
   PHONE
   ═══════════════════════════════════════════════════════════════════════════
   One page, two jobs. At a desk an owner is setting up their week and reading
   what worked, so the desktop layout stays a dashboard. On a phone they are
   standing in a half-empty room at 2pm, and the only thing that matters is
   putting something on the board before the moment passes.

   So the phone view is not a narrower dashboard — it leads with the action.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {

  /* Always reachable, never scrolled past. The button in the page head is fine
     on a desk and useless three screens down on a phone. */
  .phone-post.hidden { display: none; }
  .phone-post {
    position: fixed;
    left: 14px;
    right: 14px;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    z-index: 400;
    box-shadow: 0 6px 20px rgba(120, 53, 15, 0.30);
  }
  .main { padding-bottom: 92px; }
  .page-head .btn { display: none; }   /* the sticky one replaces it */

  /* Full screen, because a card floating in a dim backdrop wastes a third of a
     phone on decoration — and this is the screen where every pixel is either a
     template or a reason not to post. */
  .modal-backdrop { padding: 0; background: var(--bg); }
  .modal {
    border-radius: 0;
    max-width: none;
    min-height: 100vh;
    min-height: 100dvh;
    padding: calc(18px + env(safe-area-inset-top, 0px)) 18px
             calc(24px + env(safe-area-inset-bottom, 0px));
  }

  /* Thumb-sized. These are the only controls on the screen that matter. */
  .template { padding: 14px 12px; }
  .template-icon { font-size: 26px; }
  .quick-row { padding: 15px 14px; }
}

@media (min-width: 641px) {
  .phone-post { display: none; }
  /* More room, so the escape hatch does not need a whole row to itself. */
  .template-grid { grid-template-columns: repeat(3, 1fr); }
  .template.custom { grid-column: auto; text-align: left; }
  .photo-picker-grid { grid-template-columns: repeat(4, 1fr); }
}


/* ═══════════════════════════════════════════════════════════════════════════
   THE SCANNER
   ═══════════════════════════════════════════════════════════════════════════
   Lifted from the standalone scan page it used to live on. Dark and high
   contrast on purpose: a server holds this in a dim dining room with one hand
   while carrying plates in the other. Nothing to read, nothing to navigate,
   and nothing about the customer — point, scan, look at the colour.

   Scoped to .scan-host and .employee-shell so a dark palette built for one
   screen cannot leak into a dashboard built for a desk.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   SYFT FOR RESTAURANTS
   ═══════════════════════════════════════════════════════════════════════════
   Dark, high contrast, one job at a time. A server holds this in a dim dining
   room with one hand while carrying plates in the other. There is nothing to
   read, no navigation, and no information about the customer — point, scan,
   look at the colour. Green means apply the discount. Red means do not.

   The owner half borrows the same skin. An owner opening this on a phone is
   also standing up, also in a hurry, and also wants one number and one button.
   ═══════════════════════════════════════════════════════════════════════════ */

.scan-host, .employee-shell {
  --bg:      #12100E;
  --panel:   #1E1B17;
  --line:    #35302A;
  --text:    #F5F0E6;
  --muted:   #A39683;
  --amber:   #F59E0B;
  --green:   #16A34A;
  --red:     #DC2626;
  --safe-t:  env(safe-area-inset-top, 0px);
  --safe-b:  env(safe-area-inset-bottom, 0px);

  /* Redefining --text above is not enough on its own, and the reason is the
     whole bug. `body` sets `color: var(--text)` once, against the LIGHT
     palette, and computes it to near-black there. Custom properties cascade;
     an already-computed `color` does not. So every element in here that never
     declared a colour of its own quietly inherited #1C0A00 — near-black on a
     near-black panel.

     Two were doing exactly that, and neither looked broken enough to report:
     the deal title on the open-tables bar at 1.12:1, and the staff member's
     own name at the top of the scanner at 1.01:1. Both are populated, both
     were invisible, and their immediate siblings were fine because those
     happened to declare `color: var(--muted)`.

     Declaring it here fixes the class rather than the two instances, and any
     element added to this screen later inherits something readable by
     default rather than by luck. 4.5:1 is the floor; this is 15.1:1. */
  color: var(--text);
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* ── This used to lock the whole document ─────────────────────────────────────
   `html, body { height: 100%; overflow: hidden; }` arrived with the scanner
   when the staff page was folded into this one. The palette above it was
   carefully scoped to .scan-host and .employee-shell; this was not, so every
   page here — dashboard, settings, registration — silently lost the ability to
   scroll.

   It hid for weeks because nothing was taller than a screen. Then the
   registration form grew a search box, a confirmation map and a fallback
   address block, and an owner signing up simply could not reach the button.

   The scanner does need a locked viewport: it is one screen, held in one hand,
   and a camera that scrolls away mid-scan is useless. So the lock lives on the
   thing that needs it. */
body:has(.employee-shell:not(.hidden)) { height: 100%; overflow: hidden; }

/* Safari before 15.4 has no :has(). There the page merely scrolls when it did
   not before, which on the scanner screen is invisible — nothing overflows —
   and everywhere else is the fix. Failing open is right here: an owner who
   cannot reach a button is a lost registration; a scanner that can be nudged
   a few pixels is nothing. */

body {
  background: var(--bg);
  color: var(--text);
  font-family: 'DM Sans', system-ui, sans-serif;
  -webkit-user-select: none;
  user-select: none;
}

button, input { font-family: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }


/* ── Sign in ──────────────────────────────────────────────────────────────── */
#view-login {
  justify-content: center;
  padding: calc(var(--safe-t) + 32px) 26px calc(var(--safe-b) + 32px);
}

.brand {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 34px;
  font-weight: 800;
  color: var(--amber);
  letter-spacing: -0.03em;
  text-align: center;
}
.brand-sub {
  text-align: center;
  color: var(--muted);
  font-size: 14px;
  margin: 4px 0 32px;
}

.field {
  width: 100%;
  background: var(--panel);
  border: 1.5px solid var(--line);
  border-radius: 12px;
  padding: 15px 16px;
  font-size: 16px;
  color: var(--text);
  outline: none;
  margin-bottom: 11px;
  -webkit-user-select: text;
  user-select: text;
}
.field:focus { border-color: var(--amber); }
.field::placeholder { color: #6B6152; }

.btn {
  width: 100%;
  background: var(--amber);
  color: #1C0A00;
  border-radius: 12px;
  padding: 16px;
  font-size: 16px;
  font-weight: 700;
}
.btn:active { transform: scale(0.985); }
.btn:disabled { opacity: 0.5; }
.btn.ghost {
  background: transparent;
  border: 1.5px solid var(--line);
  color: var(--muted);
  font-size: 14px;
  padding: 13px;
}

.error {
  color: #FCA5A5;
  font-size: 14px;
  font-weight: 600;
  min-height: 20px;
  margin-bottom: 6px;
}

/* ── Scanner ──────────────────────────────────────────────────────────────── */
.top-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(var(--safe-t) + 14px) 18px 14px;
  border-bottom: 1px solid var(--line);
}
.who { font-size: 15px; font-weight: 700; }
.where { font-size: 12px; color: var(--muted); margin-top: 1px; }
.signout { font-size: 13px; font-weight: 600; color: var(--muted); padding: 6px; }

.scan-area {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: #000;
}
#camera {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
#frame-canvas { display: none; }

/* Reticle — gives the server something to aim with. */
.reticle {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 62vw;
  max-width: 300px;
  aspect-ratio: 1;
  border-radius: 20px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
.reticle::before,
.reticle::after {
  content: '';
  position: absolute;
  width: 34px; height: 34px;
  border: 4px solid var(--amber);
}
.reticle::before { top: -2px;    left: -2px;  border-right: none; border-bottom: none; border-radius: 16px 0 0 0; }
.reticle::after  { bottom: -2px; right: -2px; border-left: none;  border-top: none;    border-radius: 0 0 16px 0; }

.scan-hint {
  position: absolute;
  bottom: 22px;
  left: 0; right: 0;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
  pointer-events: none;
}

.camera-fallback {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  justify-content: center;
  padding: 26px;
  background: var(--bg);
}
.camera-fallback.active { display: flex; }

/* ── Result ───────────────────────────────────────────────────────────────── */
/* Full screen, one colour, one word. Readable from arm's length in a hurry. */
#view-result { justify-content: center; align-items: center; padding: 26px; text-align: center; }
#view-result.valid   { background: var(--green); }
#view-result.invalid { background: var(--red); }

.result-mark {
  width: 108px; height: 108px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  display: grid;
  place-items: center;
  margin-bottom: 22px;
}
.result-mark svg { width: 60px; height: 60px; }

.result-word {
  font-family: 'Plus Jakarta Sans', sans-serif;
  font-size: 42px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: #fff;
}
.result-detail {
  font-size: 17px;
  color: rgba(255, 255, 255, 0.92);
  margin-top: 10px;
  line-height: 1.45;
  max-width: 300px;
}
/* ── What the server has to do ───────────────────────────────────────────────
   Syft never sees a check total. The person holding this phone is the
   enforcement mechanism, so a screen that says only "Valid" is asking them to
   enforce terms nobody told them. This is the deal's own terms — nothing about
   the customer. */
.result-check {
  background: rgba(0, 0, 0, 0.28);
  border-radius: 12px;
  padding: 13px 18px;
  margin-top: 20px;
  font-size: 16px;
  font-weight: 700;
  color: #fff;
  line-height: 1.4;
  max-width: 320px;
}
.result-fine {
  margin-top: 12px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.78);
  line-height: 1.45;
  max-width: 320px;
}
.result-next {
  margin-top: 34px;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
}

/* ── The confirmation ────────────────────────────────────────────────────────
   Sized for a thumb on a phone held one-handed behind a pass, because that is
   where it will be used. Both buttons are the same size: "Not applied" is a
   real answer, not a way out, and making it small teaches people to press the
   green one to make the screen go away. */
.result-settle { margin-top: 26px; width: 100%; max-width: 320px; }
.settle-ask {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.82);
  margin-bottom: 10px;
}
.settle-row { display: flex; gap: 10px; }
.settle-row button {
  flex: 1;
  min-height: 52px;
  border-radius: 12px;
  font-size: 16px;
  font-weight: 700;
  border: 1.5px solid rgba(255, 255, 255, 0.55);
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}
.settle-yes { background: rgba(255, 255, 255, 0.95); color: var(--text); border-color: transparent; }
.settle-row button:disabled { opacity: 0.75; }

.manual-wrap { padding: 18px; border-top: 1px solid var(--line); }

/* ── Open tables ─────────────────────────────────────────────────────────────
   A minimum-spend deal cannot be settled at the scan — the total does not exist
   yet. The server scans at greeting and comes back here when the check prints.

   Deliberately a bar and not a screen. A server should never have to go looking
   for it; it appears at the bottom of the scanner the moment a table is open
   and disappears when there are none, which is most of the time. */
.open-bar {
  display: none;
  border-top: 1px solid var(--line);
  background: var(--panel);
  padding: 13px 18px calc(var(--safe-b) + 13px);
}
.open-bar.active { display: block; }
.open-bar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  font-weight: 700;
  color: var(--amber);
  margin-bottom: 10px;
}
.open-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
}
.open-info { flex: 1; min-width: 0; }
.open-deal {
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.open-check { font-size: 13px; color: var(--amber); font-weight: 700; margin-top: 2px; }
.open-when  { font-size: 12px; color: var(--muted); margin-top: 2px; }
.open-btn {
  flex-shrink: 0;
  border-radius: 10px;
  padding: 10px 13px;
  font-size: 13px;
  font-weight: 700;
}
.open-btn.yes { background: var(--green); color: #fff; }
.open-btn.no  { background: transparent; border: 1.5px solid var(--line); color: var(--muted); }

/* Full-screen confirm. Declining takes a deal off a customer who is standing
   right there, so it does not happen on a single tap next to "Approve". */
.confirm-wrap {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 40;
  background: rgba(0, 0, 0, 0.82);
  padding: 26px;
  flex-direction: column;
  justify-content: center;
  text-align: center;
}
.confirm-wrap.active { display: flex; }
.confirm-title { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 22px; font-weight: 800; }
.confirm-body  { font-size: 14px; color: var(--muted); margin: 10px 0 24px; line-height: 1.55; }


/* Somebody handed this phone for a shift gets the scanner and no way out of
   it except signing out. */
.employee-shell {
  position: fixed;
  inset: 0;
  z-index: 700;
  background: var(--bg);
  overflow-y: auto;
}
.employee-shell.hidden { display: none; }

/* Inside the dashboard the scanner is one page among several, so it sits in a
   rounded panel rather than swallowing the screen. */
.scan-host {
  position: relative;
  border-radius: 14px;
  overflow: hidden;
  background: var(--bg);
}

.scan-view { display: none; }
.scan-view.active { display: flex; flex-direction: column; }

/* The camera needs a height it can actually have.

   These styles were written for a page that filled the phone, where
   `flex: 1` had a 100vh parent to take its share of. Dropped into a panel on a
   dashboard there is no such parent, the chain breaks, and the viewfinder
   collapses to nothing — a black rectangle with no camera and no error, which
   is the worst of both. */
.scan-host .scan-area     { flex: none; height: 420px; }
.employee-shell .scan-view.active { min-height: 100vh; min-height: 100dvh; }
.employee-shell .scan-area { flex: 1; min-height: 300px; }

/* The result screen is a full-bleed colour on a phone; in a panel it needs a
   floor so a green tick is not squeezed into a strip. */
.scan-host #view-result { min-height: 420px; justify-content: center; }

/* ── Who walked in ───────────────────────────────────────────────────────────
   One bar, two colours, no axis. This is not a chart to study — it is a single
   proportion an owner should be able to read without stopping, so anything
   that would make it look like analysis has been left off.

   New is the accent colour and returning is the quiet one. The eye should land
   on the part that answers the question. */
.mix-line {
  font-size: 16px;
  line-height: 1.5;
  margin-bottom: 14px;
}
.mix-line b { font-family: var(--font-num); font-size: 19px; }

.mix-bar {
  height: 14px;
  border-radius: 999px;
  background: var(--pill);
  overflow: hidden;
}
.mix-new {
  height: 100%;
  background: var(--amber);
  border-radius: 999px;
  /* A restaurant with one new customer out of forty should still see a sliver
     rather than an empty bar that reads as a rendering fault. */
  min-width: 3px;
}

.mix-key {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 10px;
  font-size: 13px;
  color: var(--muted);
  font-weight: 600;
}
.mix-key .dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  margin-right: 6px;
}
.mix-key .dot.new { background: var(--amber); }
.mix-key .dot.old { background: var(--pill); border: 1px solid var(--border); }

/* ── Finding the restaurant ──────────────────────────────────────────────────
   A list of real places instead of two number boxes. The old form asked an
   owner to type a longitude, which is not a thing anybody should be asked to
   do — it put a real store 7,000 miles away and nothing objected, because the
   number was valid, just wrong. */
.place-list {
  border: 1.5px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  margin-top: 8px;
  background: var(--surface);
}
.place-row {
  display: block;
  width: 100%;
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
}
.place-row:last-child { border-bottom: none; }
.place-row:hover      { background: var(--cream); }
.place-name { font-size: 14.5px; font-weight: 700; }
.place-addr { font-size: 12.5px; color: var(--soft); margin-top: 2px; }
.place-none { padding: 12px 14px; font-size: 13px; color: var(--soft); }

/* The confirmation. A dot in the wrong place is obvious at a glance; a wrong
   decimal is not, which is the entire reason this picture exists. */
.place-picked {
  margin-top: 12px;
  border: 1.5px solid var(--amber);
  border-radius: 12px;
  padding: 14px;
  background: var(--cream);
}
.picked-name { font-size: 15.5px; font-weight: 700; }
.picked-addr { font-size: 13px; color: var(--soft); margin-top: 2px; }

.picked-map {
  position: relative;
  height: 150px;
  margin-top: 11px;
  border-radius: 9px;
  overflow: hidden;
  background: var(--pill);
}
.picked-map .tile {
  position: absolute;
  width: 256px;
  height: 256px;
  /* The tiles are decoration around one dot; dragging one out of the box is
     not a thing anybody means to do. */
  pointer-events: none;
  user-select: none;
}
.tile-pin {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border-radius: 50%;
  background: var(--danger);
  border: 3px solid #fff;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
}

.picked-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 11px;
  flex-wrap: wrap;
}
.picked-ask { font-size: 13.5px; font-weight: 600; color: var(--muted); }
