/* WVCS Staff — visual language taken from the check-in page, which Derek named
   as the reference. Everything below is either lifted from it or built to sit
   beside it: same greys, same blue, same card, same 8/10px radii, same
   16px-minimum inputs (below that iOS Safari zooms on focus).
   Tokens live here so later UX passes are one file, not a hunt. */

:root {
  --bg:        #f5f5f5;
  --surface:   #ffffff;
  --ink:       #222222;
  --ink-soft:  #777777;
  --ink-faint: #999999;
  --line:      #eeeeee;
  --line-soft: #f0f0f0;
  --blue:      #1a73e8;
  --blue-pale: #e8f0fe;
  --good:      #137333;
  --good-pale: #e6f4ea;
  --bad:       #c5221f;
  --bad-pale:  #fce4e4;
  --warn-pale: #fff8e1;
  --warn-line: #ffe082;
  --warn-ink:  #b26a00;

  --pad:       18px;
  --radius:    10px;
  --radius-sm: 8px;
  --bar-h:     56px;

  --shadow:    0 1px 3px rgba(0,0,0,.1);
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box }
/* NOT height:100%. That caps how far a sticky child can stick — to one screen
   — so on a long client record the app bar unstuck and scrolled away while the
   client name kept sticking, leaving a gap with content showing through. */
html, body { min-height: 100% }
body {
  margin: 0; font-family: var(--font); background: var(--bg); color: var(--ink);
  -webkit-text-size-adjust: 100%;
}

/* ── App bar ─────────────────────────────────────────────────────────── */
/* Deliberately NOT sticky. On a client record the branding scrolls away and
   the client's name takes the top — which is the thing you actually need
   anchored while reading 31 fields on a phone. */
.appbar {
  display: flex; align-items: center; gap: 4px;
  height: calc(var(--bar-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) 8px 0;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
/* Tapping the mark refetches — the cheapest possible "reload" on a device
   where pull-to-refresh does nothing useful inside a standalone app. */
.brand {
  display: flex; align-items: center; gap: 8px; min-width: 0;
  background: none; border: 0; font: inherit; color: inherit;
  padding: 6px 8px; margin-left: -4px; border-radius: var(--radius-sm); cursor: pointer;
}
.brand:active { background: var(--bg) }
.brand.busy { opacity: .5 }
.brand-mark { width: 26px; height: 26px; display: block }
.brand-name { font-weight: 700; font-size: 17px; letter-spacing: .01em }
.appbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; padding-right: 4px }

.icon-btn {
  appearance: none; border: 0; background: transparent; color: var(--ink);
  width: 44px; height: 44px;              /* 44px is the tap-target floor */
  display: grid; place-items: center; border-radius: 50%; cursor: pointer;
}
.icon-btn:active { background: var(--line) }

.badge {
  display: inline-block; padding: 4px 12px; background: var(--blue-pale);
  border-radius: 16px; font-size: 13px; color: var(--blue); white-space: nowrap;
  max-width: 45vw; overflow: hidden; text-overflow: ellipsis;
}

/* ── Tabs ────────────────────────────────────────────────────────────────
   Replaces the drawer. Always visible, no hidden state, nothing to toggle.
   `[hidden]` is asserted below with !important precisely because the drawer's
   `display: flex` silently beat it and left the thing sitting open. */
[hidden] { display: none !important }

.tabs {
  display: flex; gap: 4px; padding: 0 var(--pad);
  background: var(--surface); border-bottom: 1px solid var(--line);
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.tabs:empty { display: none }
.tabs a {
  padding: 12px 4px; margin-right: 16px; white-space: nowrap; text-decoration: none;
  color: var(--ink-soft); font-size: 15px; border-bottom: 2px solid transparent;
}
.tabs a.current { color: var(--blue); font-weight: 600; border-bottom-color: var(--blue) }

.footline {
  padding: 4px var(--pad) calc(20px + env(safe-area-inset-bottom));
  font-size: 11px; color: #bbb; line-height: 1.6; word-break: break-word;
}

/* ── Screens ─────────────────────────────────────────────────────────── */
main { padding: var(--pad); padding-bottom: calc(var(--pad) + env(safe-area-inset-bottom)) }
h2 { margin: 0 0 14px; font-size: 20px }

.card {
  background: var(--surface); border-radius: var(--radius); padding: 12px 14px;
  margin-bottom: 16px; box-shadow: var(--shadow);
}

/* Label left, value right — the check-in row, reused for client fields. */
.row {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 10px 0; border-bottom: 1px solid var(--line-soft);
}
.row:last-child { border-bottom: 0 }
.row .k { color: var(--ink-soft); flex: 1 }
.row .v { text-align: right; flex: 2; word-break: break-word }
/* `blank`, not `empty` — `.empty` below is the full-screen empty-state block
   and brings 40px of padding with it. A field value wearing that class was
   inheriting it, which made every unfilled field 120px tall. */
.row .v.blank { color: #ccc; font-style: italic }
.row .v.multiline { white-space: pre-wrap; text-align: left }

.section-header {
  font-weight: 700; font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-soft); margin: 20px 0 8px; padding-bottom: 4px; border-bottom: 1px solid var(--line);
  /* flex so a section can carry its "Edit all" control on the right */
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}

/* Client name follows the branding up and then holds the very top.
   Negative SIDE margins + matching padding make it span the full width, so
   nothing scrolls past in the gutters beside it.

   The back control lives IN here now. As its own row above, it was clipped by
   this element's negative top margin — and removing that margin only fixed the
   very top of the page, because any scroll at all slid the link under the
   header regardless. Inside, it cannot be covered, and it stays reachable from
   anywhere in a long record. */
.sticky-head {
  position: sticky; top: 0; z-index: 20;
  background: var(--bg);
  margin: 0 calc(var(--pad) * -1) 4px;
  padding: 10px var(--pad);
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 10px;
}
.back-btn {
  appearance: none; border: 0; background: none; cursor: pointer;
  color: var(--blue); font: inherit; font-size: 22px; line-height: 1;
  padding: 0; margin-left: -10px; width: 44px; min-height: 32px;   /* 44px wide; the row's padding makes up the height */
  display: grid; place-items: center; border-radius: 50%; flex: none;
}
.back-btn:active { background: var(--line) }
/* Truncate rather than wrap: the name shares this row with the status and the
   id, and a long name must not push either off the screen. */
.sticky-head .name {
  font-size: 18px; font-weight: 700; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sticky-head .id   { color: var(--ink-faint); font-size: 13px; white-space: nowrap }

/* Record status, beside the name — the app's equivalent of the Client Editor's
   B2 cell (§5). Same streetlight vocabulary as the sheet, so a colour means the
   same thing on both surfaces. */
.sticky-head .status { font-size: 12px; white-space: nowrap; font-weight: 600 }
.sticky-head .status.ok    { color: var(--good) }
.sticky-head .status.dirty { color: var(--warn-ink) }
.sticky-head .status.err   { color: var(--bad) }

/* ── Inline field editing ────────────────────────────────────────────────
   Tap a row, it becomes an input in place, commit or cancel (§3). The read
   view stays a scannable list; only the field being touched grows. */
.row.editable { cursor: pointer; -webkit-tap-highlight-color: transparent }
.row.editable:active { background: var(--line-soft) }
/* Nothing to tap and nothing that looks tappable: these belong to Reconcile
   and the importer, and the app must never appear to offer them. */
.row.locked .k, .row.locked .v { color: var(--ink-faint) }
/* An edit that is staged but not yet saved. The marker sits on the label side
   so a column of them scans down the page. */
.row.changed .k { font-weight: 600; color: var(--warn-ink) }
.row.changed .k::before { content: '● '; color: var(--warn-ink) }

/* Someone else changed this while you had the record (2026-10-27). Blue, not
   amber: amber is YOUR change on its way; blue is news from elsewhere. */
.row.remote { background: var(--blue-pale) }
.row.remote .k { font-weight: 600; color: var(--blue) }

/* A price-setting field the server has not confirmed yet — shown, not offered. */
.row.checking .v { color: var(--ink-faint) }
.checking-tag { display: block; font-size: 11px; font-style: italic; color: var(--ink-faint) }

/* Door code / key info: behind a tap, never stored on the phone. */
.row.access { cursor: pointer; -webkit-tap-highlight-color: transparent }
.row.access:active { background: var(--line-soft) }
.row.access .v { color: var(--blue); font-style: normal }

.row.editing {
  display: block; background: var(--blue-pale);
  margin: 0 -14px; padding: 12px 14px; border-radius: var(--radius-sm);
}
.row.editing label.k { display: block; margin-bottom: 6px; font-weight: 600; color: var(--ink) }
.row.editing input, .row.editing textarea, .row.editing select {
  width: 100%; padding: 12px; font-size: 16px; font-family: inherit;   /* 16px: no iOS zoom */
  border: 1px solid #ccc; border-radius: var(--radius-sm);
  background: var(--surface); color: var(--ink);
}
.row.editing textarea { resize: vertical; min-height: 88px }
.row.editing .hint { font-size: 12px; color: var(--ink-soft); margin-top: 6px }
.edit-actions { display: flex; gap: 8px; margin-top: 10px }
.edit-actions .btn { width: auto; flex: 1; padding: 10px 16px; font-size: 15px; margin-top: 0 }

/* Sticky so Save is reachable from anywhere in a 31-field record without
   scrolling to the bottom to find it. */
.save-bar {
  position: sticky; bottom: 0; z-index: 15;
  background: var(--bg); border-top: 1px solid var(--line);
  margin: 8px calc(var(--pad) * -1) calc(var(--pad) * -1);
  padding: 12px var(--pad) calc(12px + env(safe-area-inset-bottom));
}

/* ── Controls ────────────────────────────────────────────────────────── */
.btn {
  appearance: none; display: inline-block; width: 100%;
  padding: 14px 20px; font-size: 17px; font-weight: 600; font-family: inherit;
  color: #fff; background: var(--blue); border: 0; border-radius: var(--radius-sm); cursor: pointer;
}
.btn:disabled { background: #9bbef0 }
.btn.quiet { background: transparent; color: var(--blue); border: 1px solid var(--line) }
.btn.danger { background: transparent; color: var(--bad); border: 1px solid #f5c6cb }
.btn + .btn { margin-top: 8px }

.field { margin-bottom: 12px }
.field label { display: block; margin: 0 0 6px; font-weight: 600; font-size: 14px }
input[type=text], input[type=email], input[type=search] {
  width: 100%; padding: 12px; font-size: 16px; font-family: inherit;   /* 16px: no iOS zoom */
  border: 1px solid #ccc; border-radius: var(--radius-sm); background: var(--surface); color: var(--ink);
}

/* ── Scope control (§4) ──────────────────────────────────────────────────
   The toggle and the filter are one control: these pick WHAT is being
   searched, the box below narrows it. Scrolls sideways rather than wrapping,
   so four scopes never become two rows on a narrow phone. */
.scopes {
  display: flex; gap: 6px; margin-bottom: 10px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.scope {
  appearance: none; font: inherit; font-size: 14px; white-space: nowrap; cursor: pointer;
  padding: 8px 14px; border-radius: 16px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft);
}
.scope.current { background: var(--blue-pale); border-color: var(--blue); color: var(--blue); font-weight: 600 }

/* ── Search results ──────────────────────────────────────────────────── */
/* `.results li` is (0,1,1) — a bare `.result-group` at (0,1,0) loses to it and
   the header renders as a card. Qualified so it wins. */
.results li.result-group {
  padding: 14px 2px 2px; background: none; border: 0; border-radius: 0; margin: 0;
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-faint); cursor: default;
}
.results li.result-group:first-child { padding-top: 4px }
.results li .r-main { font-size: 16px }
.results li .r-pets { color: var(--ink-soft); font-size: 14px }
.results li .r-sub  { color: var(--ink-faint); font-size: 13px; margin-top: 2px }

/* ── Booking record ──────────────────────────────────────────────────────
   The per-field indicators (§5) — same wording the Confirmation tab shows in
   C10/C13/C14/C19/C31, attached to the field they judge instead of a distant
   cell. Green and amber mean here exactly what they mean on the sheet. */
.check {
  font-size: 13px; line-height: 1.4; padding: 6px 10px; margin: 0 0 8px;
  border-radius: var(--radius-sm); border: 1px solid transparent;
}
.check.ok   { color: var(--good); background: var(--good-pale); border-color: #b7e1c2 }
.check.warn { color: var(--warn-ink); background: var(--warn-pale); border-color: var(--warn-line) }
.check-line { margin-top: 6px }
.checks-ok {
  color: var(--good); background: var(--good-pale); border: 1px solid #b7e1c2;
  border-radius: var(--radius-sm); padding: 10px 12px; margin-bottom: 16px; font-size: 14px;
}
/* A voided line, and the reversal itself, both stay listed — the ledger is
   append-only, so the mistake and its correction are both part of the record. */
.pay-row.voided .k, .pay-row.voided .v { color: var(--ink-faint); text-decoration: line-through }
.pay-row .k .r-sub { display: block; color: var(--ink-faint); font-size: 13px; text-decoration: none }

.kind-line { display: flex; align-items: center; gap: 10px; margin: 2px 0 14px }
.kind-pets { color: var(--ink-soft); font-size: 14px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.badge-kind {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 3px 8px; border-radius: 10px; white-space: nowrap;
}
.badge-kind.pending   { color: var(--warn-ink); background: var(--warn-pale); border: 1px solid var(--warn-line) }
.badge-kind.confirmed { color: var(--good); background: var(--good-pale); border: 1px solid #b7e1c2 }

.results { list-style: none; padding: 0; margin: 8px 0 0 }
.results li {
  padding: 14px 12px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-sm); margin-bottom: 6px; cursor: pointer;   /* ~46px tap target */
}
.results li.active { background: var(--blue-pale); border-color: var(--blue) }
.results li.needs-id { color: var(--ink-faint); border-style: dashed }
.tag {
  float: right; font-size: 11px; color: var(--warn-ink); background: var(--warn-pale);
  border: 1px solid var(--warn-line); border-radius: 10px; padding: 1px 8px;
}
.more-note { text-align: center; color: var(--ink-faint); font-size: 12px; padding: 8px }

/* ── Feedback ────────────────────────────────────────────────────────── */
.note    { border-radius: var(--radius-sm); padding: 12px; margin-bottom: 16px; font-size: 14px;
           line-height: 1.5; word-break: break-word; user-select: text; -webkit-user-select: text }
.note.err  { background: var(--bad-pale); border: 1px solid #f5c6cb; color: var(--bad) }
.note.ok   { background: var(--good-pale); border: 1px solid #b7e1c2; color: var(--good) }
.note.warn { background: var(--warn-pale); border: 1px solid var(--warn-line); color: var(--warn-ink) }
.note-hint { margin-top: 8px; font-size: 11px; opacity: .65 }

/* Notices — someone changed what you changed. They stay until dismissed. */
.notices { margin-bottom: 16px }
.notice-card { display: flex; gap: 10px; align-items: flex-start; background: var(--warn-pale);
               border: 1px solid var(--warn-line); border-radius: var(--radius-sm); padding: 10px 12px;
               margin-bottom: 8px; font-size: 14px; line-height: 1.45; color: var(--ink) }
.notice-text { flex: 1; word-break: break-word }
.notice-text[data-act] { cursor: pointer }
.notice-x { appearance: none; border: 0; background: transparent; color: var(--warn-ink);
            font: inherit; font-size: 13px; font-weight: 600; padding: 2px 0; cursor: pointer }

.empty { text-align: center; padding: 40px 20px; color: var(--ink-faint) }
.empty .big { font-size: 32px }
.spinner {
  width: 20px; height: 20px; margin: 40px auto; border: 3px solid var(--line);
  border-top-color: var(--blue); border-radius: 50%; animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }

.mono { font-family: ui-monospace, Menlo, monospace; font-size: 12px; word-break: break-all;
        background: var(--surface); border: 1px solid var(--line); border-radius: 6px;
        padding: 8px; margin-top: 8px; user-select: all; -webkit-user-select: all }
.diag { font-size: 11px; color: #bbb; margin-top: 28px; line-height: 1.5; word-break: break-word }

@media (prefers-reduced-motion: reduce) {
  .drawer { animation: none } .spinner { animation-duration: 2s }
}

/* ── The price (E2) ────────────────────────────────────────────────────────
   Pricing is a PROPERTY of a booking, not the result of pressing Calculate, so
   this is a statement rather than a control — except when the total has been
   overridden, which is the one genuine decision on the screen. Then both
   numbers are shown together, because a total that silently disagrees with its
   own arithmetic is exactly what confirm records as a MANUAL OVERRIDE. */
.card.price { padding: 14px 16px; margin-bottom: 16px }
.card.price.overridden { border-color: var(--warn-line); background: var(--warn-pale) }
.price-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 2px 0;
}
.price-k { font-size: 13px; color: var(--ink-soft) }
.price-v { font-variant-numeric: tabular-nums }
.price-v.big { font-size: 22px; font-weight: 600; color: var(--ink) }
.price-v.muted { font-size: 15px; color: var(--ink-soft) }
/* Deliberately NOT struck through: a strikethrough is the retail idiom for a
   price that was reduced, and the computed total is not a cancelled offer — it
   is the arithmetic the charged figure deviates from. */
.price-note { font-size: 12px; color: var(--ink-soft); margin-top: 6px; line-height: 1.4 }
.price-note.warn { color: var(--warn-ink) }
.card.price .btn.quiet { margin-top: 10px; width: 100% }

/* ── Booking actions (E3) ──────────────────────────────────────────────────
   What you can DO to a booking, gathered in one place above the 60 fields —
   because an action two thirds of the way down a record is an action nobody
   finds. A pending draft gets Confirm; a confirmed contract gets Amend and
   Cancel, which is what collapses two spreadsheet tabs into one screen. */
.actions { margin: 0 0 20px; display: flex; flex-direction: column; gap: 8px }
.actions .btn { width: 100% }
/* No .primary rule: the base .btn is ALREADY filled blue and full width, so a
   second one only made Amend look as inviting as Confirm. A pending draft has
   one obvious action (confirm it); a live contract has none, so both of its
   actions are outlines. */
.btn:disabled { opacity: .55 }
.action-note { font-size: 12px; color: var(--ink-soft); line-height: 1.45; margin: 2px 0 0 }

/* The unsaved marker, in the sticky header where it cannot be scrolled past.
   It replaces the booking id rather than crowding it: on a phone the client's
   name is the one thing in that row that has to stay readable, and three items
   squeezed it to "Alice Ande…" once already. */
.sticky-head .unsaved {
  margin-left: auto; flex: none; font-size: 12px; font-weight: 600;
  color: var(--warn-ink); background: var(--warn-pale);
  border: 1px solid var(--warn-line); border-radius: 999px;
  padding: 3px 9px; white-space: nowrap;
}

/* ── Editing a whole section at once ───────────────────────────────────────
   If one date is changing, usually several are. The section header carries the
   way in; the card then holds every editable field with ONE Done for the lot,
   which is one round trip instead of four. */
.section-edit {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 12px; font-weight: 600; letter-spacing: 0;
  color: var(--blue); text-transform: none; padding: 2px 0;
}
.card.editing-group { border-color: var(--blue-pale); box-shadow: 0 0 0 2px var(--blue-pale) }
.card.editing-group .row.editing + .row.editing { border-top: 1px solid var(--line-soft) }

/* A client name you can step into, from a booking. Looks like the name it
   replaces — this is navigation, not a control, and dressing it as a button
   would make the header read as a toolbar. */
.sticky-head .name.link {
  appearance: none; border: 0; background: transparent; padding: 0;
  font: inherit; color: var(--blue); cursor: pointer; text-align: left;
}
/* Says the detour is a detour, so ← is obviously a return and not an exit. */
.crumb {
  font-size: 12px; color: var(--ink-soft); background: var(--blue-pale);
  border-radius: var(--radius-sm); padding: 8px 10px; margin: 0 0 14px; line-height: 1.45;
}
