/* Lunula shared building blocks — the design tokens and desk-board card visuals.
   DESKTOP-V2.md hard requirement: Waiting / In chairs / To collect / Paid must look
   identical on every surface. Extracted verbatim from mobile.css.

   BUT THIS FILE IS LOADED BY THE DESKTOP ONLY (public/index.html). The phone loads
   mobile.css and nothing else — grep salon-android, there is no reference. The rules
   are DUPLICATED into mobile.css, which is what makes the two surfaces match; it is
   not a shared import. So editing here changes the desktop alone and silently forks
   the very surfaces the requirement above says must be identical. Change both. */

:root {
  --ink: #17121a; --ink2: #6e6472; --ink3: #a89fae;
  --hair: #eeeaf0; --paper: #ffffff; --wash: #faf8fb;
  --brand: #9d4564; --brand-deep: #7c3450; --gold: #8c6d3a;
  --good: #2e7d54; --warn: #a06a1f; --bad: #b3261e; --info: #4a5d92;
  --r-sm: 12px; --r-md: 16px; --r-lg: 22px; --r-full: 999px;
  --shadow-pop: 0 10px 24px rgba(23, 18, 26, .18);
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --t-fast: 120ms; --t-med: 220ms;
}

.caps { font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); }
.num { font-variant-numeric: tabular-nums; }

/* ── desk board cards (phone-identical) ───────────────────────────────── */
.sec { margin: 6px 0 2px; display: flex; justify-content: space-between; align-items: baseline; }
.wstrip { display: flex; gap: 10px; overflow-x: auto; padding: 6px 2px; scrollbar-width: none; }
.wstrip::-webkit-scrollbar { display: none; }
.wcard { min-width: 146px; max-width: 200px; border: 1.5px solid var(--brand); border-radius: var(--r-md); padding: 12px 14px; text-align: left; background: var(--paper); flex: none; }
.wcard b { display: block; font-size: 14.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wcard span { font-size: 12px; color: var(--ink2); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wcard .wt { color: var(--brand); font-weight: 700; font-size: 12px; }
.chair { display: flex; align-items: center; gap: 12px; border: 1px solid var(--hair); border-radius: var(--r-md); padding: 11px 14px; margin: 8px 0 0; width: 100%; background: var(--paper); text-align: left; }
.chair.pay { border-color: var(--gold); }
.chair .who { flex: 1; min-width: 0; }
.chair .who b { display: block; font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chair .who span { font-size: 12.5px; color: var(--ink2); display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chair .t { font-weight: 300; flex: none; }
.ringlet { width: 44px; height: 44px; border-radius: 50%; flex: none; background: conic-gradient(var(--brand) 0 calc(var(--pct, 0) * 1%), var(--hair) calc(var(--pct, 0) * 1%) 100%); display: flex; align-items: center; justify-content: center; }
.ringlet.gold { background: var(--gold); }
.ringlet i { width: 36px; height: 36px; border-radius: 50%; background: var(--wash); font-style: normal; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 600; color: var(--ink2); font-variant-numeric: tabular-nums; }
.ringlet.gold i { background: var(--paper); color: var(--gold); }
.owes-big { font-size: 24px; font-weight: 300; color: var(--bad); flex: none; }
.paid-chip { font-size: 11px; font-weight: 700; letter-spacing: .06em; color: var(--gold); border: 1px solid var(--gold); border-radius: var(--r-full); padding: 4px 11px; flex: none; text-transform: uppercase; }
.crow { display: flex; align-items: center; gap: 14px; padding: 14px 0; border-bottom: 1px solid var(--hair); width: 100%; border-left: 0; border-right: 0; border-top: 0; background: none; text-align: left; }
.crow:last-child { border-bottom: 0; }
.crow .who { flex: 1; min-width: 0; }
.crow .who b { display: block; font-size: 15px; font-weight: 600; }
.crow .who span { font-size: 12.5px; color: var(--ink2); }
.crow .t { font-weight: 300; font-size: 15px; flex: none; }
.cmoon { width: 10px; height: 10px; border-radius: 50%; background: var(--hair); position: relative; overflow: hidden; flex: none; }
.cmoon::after { content: ""; position: absolute; left: 3px; top: -3px; width: 9px; height: 9px; border-radius: 50%; background: var(--paper); }
.cmoon.live { background: var(--brand); }
.cmoon.done { background: var(--ink3); }
.cmoon.done::after { display: none; }

/* ── desktop shell in Lunula (loads after styles.css and overrides it) ─── */
.shell { font-family: "Be Vietnam Pro", system-ui, sans-serif; }
.shell aside { background: var(--paper); border-right: 1px solid var(--hair); color: var(--ink); }
.shell aside .brand span { color: var(--ink); font-weight: 700; letter-spacing: -.01em; }
.shell aside .brand-mark { background: var(--brand); color: #fff; border-radius: 50%; }
.shell aside .user-card { background: var(--wash); border: 1px solid var(--hair); color: var(--ink2); border-radius: 12px; }
.shell aside .user-card strong { color: var(--ink); }
.shell aside nav .nav-section { font-size: 10px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); margin: 14px 0 4px; }
.shell aside nav button { background: none; border: 0; color: var(--ink2); font-weight: 500; border-radius: 10px; text-align: left; }
.shell aside nav button:hover { background: var(--wash); color: var(--ink); }
.shell aside nav button.active { background: var(--wash); color: var(--ink); font-weight: 600; box-shadow: inset 3px 0 0 var(--brand); }
.shell main { background: var(--wash); }
.shell h1 { font-family: "Be Vietnam Pro", system-ui, sans-serif; font-weight: 700; letter-spacing: -.01em; color: var(--ink); }

/* desk board layout */
.board-lanes { display: flex; gap: 18px; align-items: flex-start; margin-top: 16px; }
.board-lanes .lane { flex: 1; min-width: 0; }
.board-search { padding: 11px 16px; border: 1px solid var(--hair); border-radius: 12px; font-size: 14px; background: var(--paper); min-width: 260px; font-family: inherit; }
.board-search:focus { outline: none; border-color: var(--ink); }
.board-act { border: 0; border-radius: var(--r-full); background: var(--brand); color: #fff; font-weight: 600; padding: 8px 16px; font-size: 12.5px; white-space: nowrap; cursor: pointer; flex: none; font-family: inherit; }
.board-act:hover { background: var(--brand-deep); }

/* Today board — week day selector (clean Lunula pills, not default buttons) */
.day-nav-head { display: flex; align-items: center; justify-content: space-between; max-width: 300px; margin-bottom: 8px; }
.day-nav-label { font-size: 13px; font-weight: 600; color: var(--ink2); }
.day-arrow { width: 30px; height: 30px; border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-sm); cursor: pointer; font-size: 17px; line-height: 1; color: var(--ink2); font-family: inherit; }
.day-arrow:hover { border-color: var(--ink3); color: var(--ink); }
.days { display: flex; gap: 8px; flex-wrap: wrap; }
.day { display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 58px; padding: 7px 12px 8px; border: 1px solid var(--hair); border-radius: var(--r-sm); background: var(--paper); cursor: pointer; font-family: inherit; position: relative; transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out); }
.day small { font-size: 10px; font-weight: 700; letter-spacing: .1em; color: var(--ink3); }
.day b { font-size: 18px; font-weight: 300; font-variant-numeric: tabular-nums; color: var(--ink); line-height: 1.15; }
.day:hover { border-color: var(--ink3); }
.day.on { background: var(--brand); border-color: var(--brand); }
.day.on small { color: rgba(255, 255, 255, .78); }
.day.on b { color: #fff; }
.day i { position: absolute; top: 7px; right: 8px; width: 6px; height: 6px; border-radius: 50%; background: var(--brand); }
.day.on i { background: #fff; }
.chair .board-act { margin-left: 8px; }
#boardMoney { margin-top: 14px; }

/* ── service picker + wizards (phone-identical; desktop widths adapt) ──── */
@keyframes barUp { from { opacity: 0; transform: translateX(-50%) translateY(14px); } to { opacity: 1; transform: translateX(-50%); } }
.wiz-head { display: flex; align-items: center; gap: 12px; padding: 2px 0 4px; }
.wiz-back { width: 38px; height: 38px; border: 1px solid var(--hair); border-radius: 50%; background: var(--paper); color: var(--ink); font-size: 18px; flex: none; cursor: pointer; }
.wiz-title { flex: 1; min-width: 0; }
.wiz-title strong { display: block; font-size: 19px; font-weight: 600; letter-spacing: -.01em; }
.wiz-title small { color: var(--ink2); font-size: 12px; }
.wiz-dots { display: flex; gap: 5px; }
.wiz-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--hair); }
.wiz-dots i.on { background: var(--brand); width: 16px; border-radius: var(--r-full); }
.svc-cat { margin: 10px 2px 8px; font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); }
.svc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.shell .svc-grid { grid-template-columns: repeat(4, 1fr); }
.svc-card { position: relative; border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-md); padding: 0 0 10px; text-align: left; overflow: hidden; display: grid; gap: 2px; transition: border-color var(--t-fast); cursor: pointer; font-family: inherit; }
.svc-card.on { border-color: var(--ink); }
.svc-card img, .svc-card .svc-ph { width: 100%; height: 86px; object-fit: cover; margin-bottom: 6px; background: var(--wash); }
.svc-card .svc-ph { display: flex; align-items: center; justify-content: center; color: var(--ink3); font-weight: 300; font-size: 20px; }
.svc-card strong { font-size: 13.5px; font-weight: 600; line-height: 1.25; padding: 0 10px; }
.svc-card small { color: var(--ink2); padding: 0 10px; }
.svc-card .tick { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; border-radius: 50%; background: var(--ink); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 12px; opacity: 0; transform: scale(.6); transition: all var(--t-med) var(--ease-out); }
.svc-card.on .tick { opacity: 1; transform: none; }
.team-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.shell .team-grid { grid-template-columns: repeat(5, 1fr); }
.team-card { border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-md); padding: 18px 10px 14px; display: grid; justify-items: center; gap: 9px; transition: border-color var(--t-fast); cursor: pointer; font-family: inherit; }
.team-card.on { border-color: var(--ink); }
.team-card img, .team-card .team-ph { width: 70px; height: 70px; border-radius: 50%; object-fit: cover; background: var(--wash); }
.team-card .team-ph { display: flex; align-items: center; justify-content: center; color: var(--ink3); font-weight: 300; font-size: 20px; }
.team-card strong { font-size: 14px; font-weight: 600; text-align: center; }
/* Wrap to multiple rows instead of a hidden horizontal scroll, so chips never get cut off
   at the right edge when the browser is zoomed (owner request 2026-06-16). */
.cat-chips { display: flex; flex-wrap: wrap; gap: 7px; padding: 10px 2px 4px; }
.cat-chips button { border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-full); padding: 8px 14px; font-size: 12.5px; font-weight: 600; white-space: nowrap; color: var(--ink2); flex: none; cursor: pointer; font-family: inherit; }
.cat-chips button.has { border-color: var(--ink); color: var(--ink); }
.cat-head { display: flex; align-items: center; justify-content: space-between; width: 100%; border: 0; border-bottom: 1px solid var(--hair); background: none; padding: 15px 2px; font-size: 14.5px; font-weight: 600; color: var(--ink); text-align: left; cursor: pointer; font-family: inherit; }
.cat-head small { color: var(--ink3); font-size: 16px; font-weight: 400; }
.cat-head .cnt { color: var(--brand); font-weight: 700; margin-left: 6px; }
.float-bar { position: fixed; left: 50%; transform: translateX(-50%); width: calc(100% - 44px); max-width: 386px; bottom: 24px; background: var(--ink); color: #fff; border: 0; border-radius: var(--r-full); padding: 16px 24px; display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 14.5px; font-weight: 600; box-shadow: var(--shadow-pop); z-index: 9; animation: barUp var(--t-med) var(--ease-out) backwards; cursor: pointer; font-family: inherit; }
.float-bar small { font-weight: 400; opacity: .8; font-size: 18px; line-height: 1; }
.m-queue { display: grid; gap: 0; border-top: 1px solid var(--hair); max-width: 560px; }
.m-q-card { display: flex; align-items: center; gap: 13px; background: none; border: 0; border-bottom: 1px solid var(--hair); padding: 13px 2px; text-align: left; cursor: pointer; font-family: inherit; font-size: inherit; color: inherit; width: 100%; }
.m-q-photo { width: 52px; height: 52px; border-radius: 50%; object-fit: cover; background: var(--wash); color: var(--ink2); font-weight: 300; display: flex; align-items: center; justify-content: center; flex: none; }
.m-q-rank { margin-left: auto; font-size: 10px; font-weight: 700; letter-spacing: .1em; color: var(--good); border: 1px solid var(--good); border-radius: var(--r-full); padding: 5px 12px; flex: none; }
.m-tip { border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-sm); padding: 10px 13px; font-weight: 500; font-size: 13.5px; color: var(--ink); cursor: pointer; font-family: inherit; }
.m-tip.on { background: var(--ink); border-color: var(--ink); color: #fff; }
.m-slots { display: flex; gap: 7px; flex-wrap: wrap; max-width: 720px; }
.wiz-summary { border-top: 1px solid var(--hair); margin-bottom: 6px; max-width: 560px; }
.wiz-summary .row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 12px 0; border-bottom: 1px solid var(--hair); }
.picker-input { width: 100%; max-width: 560px; padding: 13px 16px; border: 1px solid var(--hair); border-radius: 12px; font-size: 15px; background: var(--paper); font-family: inherit; display: block; margin-bottom: 8px; }
.picker-input:focus { outline: none; border-color: var(--ink); }

/* the desktop + button (bottom-right) and its quick menu */
.desk-fab { position: fixed; right: 36px; bottom: 32px; width: 56px; height: 56px; border: 0; border-radius: 50%; background: var(--ink); color: #fff; font-size: 24px; box-shadow: var(--shadow-pop); cursor: pointer; z-index: 30; font-family: inherit; }
.desk-fab-menu { position: fixed; right: 36px; bottom: 100px; background: var(--paper); border: 1px solid var(--hair); border-radius: var(--r-md); box-shadow: var(--shadow-pop); z-index: 30; display: none; min-width: 240px; overflow: hidden; }
.desk-fab-menu.open { display: block; }
.desk-fab-menu button { display: block; width: 100%; border: 0; background: none; text-align: left; padding: 14px 18px; font-family: inherit; font-size: 14.5px; font-weight: 600; color: var(--ink); cursor: pointer; border-bottom: 1px solid var(--hair); }
.desk-fab-menu button:last-child { border-bottom: 0; }
.desk-fab-menu button small { display: block; font-weight: 400; color: var(--ink2); font-size: 12.5px; }
.desk-fab-menu button:hover { background: var(--wash); }

/* ── owner money home (dashboard = phone owner-home grown up) ──────────── */
.dash-cols { display: flex; gap: 32px; align-items: flex-start; margin-top: 10px; }
.dash-money { flex: 1; min-width: 0; }
.dash-feed { flex: 1.2; min-width: 0; border-left: 1px solid var(--hair); padding-left: 32px; }
.big-money { display: inline-block; font-size: 64px; font-weight: 200; letter-spacing: -.03em; font-variant-numeric: tabular-nums; color: var(--ink); border-bottom: 3px solid var(--gold); padding: 0 8px 6px 2px; margin: 6px 0 22px; cursor: pointer; }
.dash-stats { display: flex; flex-wrap: wrap; }
.dash-stats button { flex: 1 1 40%; min-width: 0; border: 0; background: none; text-align: left; border-top: 1px solid var(--hair); padding: 14px 10px 14px 2px; cursor: pointer; font-family: inherit; }
.dash-stats button + button { border-left: 1px solid var(--hair); padding-left: 14px; }
.dash-stats b { display: block; font-size: 26px; font-weight: 300; color: var(--ink); font-variant-numeric: tabular-nums; margin-top: 3px; }
.dash-stats b.red { color: var(--bad); }
.feed-amt { color: var(--gold); font-weight: 600; }
.pulse-line { font-size: 14px; font-weight: 600; margin: -8px 0 18px; }
.pulse-line.up { color: var(--good); }
.pulse-line.down { color: var(--ink2); }

/* ── calendar grid, Lunula skin (structure unchanged — owner stage 3) ──── */
/* overflow:auto (not hidden) so the height-capped grid actually SCROLLS to all hours instead of
   hard-clipping them — `hidden` made the day look "cut off" with no scrollbar (owner 2026-06-15).
   auto still clips the rounded corners. */
.shell .employee-calendar { background: var(--paper); border: 1px solid var(--hair); border-radius: var(--r-md); overflow: auto; }
.shell .calendar-cell { border-color: var(--hair) !important; background: var(--paper); }
.shell .calendar-head { background: var(--wash) !important; color: var(--ink) !important; font-weight: 600; font-size: 13px; }
.shell .calendar-head small { color: var(--ink3); }
.shell .calendar-time { color: var(--ink3) !important; font-weight: 300; font-variant-numeric: tabular-nums; background: var(--paper) !important; }
/* Lean card: store-colour left stripe, service hero, customer, time, status dot.
   Store = the stripe (owner 2026-06-13); status = the small dot. */
/* #181 — the same two channels the phone carries (#178). WHO is the card: a tint of the technician's
   colour with the full-strength hue on the edge, so a column reads as one person. WHAT is the stripe
   just inside it. The edge used to be the STORE, but the columns are already grouped under a store
   header band, so it was repeating what the column said; the store variable is still set on the
   element for anything else that wants it. STATUS is untouched — it has owned `.ce-dot` all along.
   Tint, never fill: the technician palette is a deliberately darker family, chosen to work as a 7px
   dot, and filling with it turns the grid heavy. */
.shell .calendar-event { background: color-mix(in srgb, var(--tech, var(--store, var(--brand))) 11%, var(--paper)) !important; border: 1px solid color-mix(in srgb, var(--tech, var(--store, var(--hair))) 34%, var(--paper)) !important; border-left: 5px solid var(--tech, var(--store, var(--hair))) !important; border-radius: 10px; color: var(--ink) !important; padding: 5px 8px 5px 13px !important; overflow: hidden; position: relative; }
/* The kind of work, as a stripe just inside the technician's edge. */
.shell .calendar-event::before { content: ""; position: absolute; left: 3px; top: 4px; bottom: 4px; width: 3px; border-radius: 2px; background: var(--svc, var(--hair)); pointer-events: none; }
/* #179's count, not a symbol. Outlined for a second or third visit, filled once they are a regular,
   so the loud version stays rare — most bookings in a nail salon are repeats. Placed top-right and
   given room by padding the service name, rather than laid over it or added as a fourth line a
   15-minute booking has no height for — the same arrangement measured working on the phone. */
.shell .calendar-event .ce-nth { position: absolute; top: 3px; right: 4px; font-size: 9.5px; font-weight: 700; line-height: 1; padding: 2px 5px; border-radius: 999px; font-variant-numeric: tabular-nums; white-space: nowrap; border: 1px solid color-mix(in srgb, var(--tech, var(--brand)) 50%, transparent); color: color-mix(in srgb, var(--tech, var(--brand)) 78%, var(--ink)); background: var(--paper); }
.shell .calendar-event.regular .ce-nth { background: var(--tech, var(--brand)); border-color: var(--tech, var(--brand)); color: #fff; }
.shell .calendar-event.has-nth .ce-svc { padding-right: 34px; }
.shell .calendar-event .ce-svc { display: block; font-size: 13px; font-weight: 700; line-height: 1.2; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* #104: the customer's name matches the service's size — weight and colour carry the hierarchy, not
   size. (This file is the authority for the card; the duration-grid rules in styles.css lose to it.) */
.shell .calendar-event .ce-who { display: block; font-size: 13px; color: var(--ink2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.shell .calendar-event .ce-time { display: block; font-size: 11px; color: var(--ink3); font-variant-numeric: tabular-nums; }
.shell .calendar-event .ce-dot { position: absolute; top: 7px; right: 7px; width: 8px; height: 8px; border-radius: 50%; background: var(--ink3); }
.shell .calendar-event.status-requested .ce-dot { background: var(--warn); }
.shell .calendar-event.status-confirmed .ce-dot { background: var(--info); }
.shell .calendar-event.status-checked_in .ce-dot { background: var(--brand); }
.shell .calendar-event.status-in_service .ce-dot { background: var(--good); }
.shell .calendar-event.status-completed .ce-dot { background: var(--gold); }
.shell .calendar-head .store-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; vertical-align: middle; }

/* ── light Lunula sweep over remaining desktop chrome ──────────────────── */
.shell .panel { background: var(--paper); border: 1px solid var(--hair); border-radius: var(--r-md); box-shadow: none; }
.shell button.primary { background: var(--ink); border: 0; border-radius: 12px; font-weight: 600; font-family: inherit; }
.shell button.primary:hover { background: #2b2230; }
.shell button.secondary { background: var(--paper); border: 1px solid var(--hair); color: var(--ink); border-radius: 12px; font-family: inherit; }
.shell input, .shell select, .shell textarea { border-radius: 10px; border-color: var(--hair); font-family: inherit; }
.shell .topbar h1 { font-weight: 700; letter-spacing: -.01em; }
.shell .subtitle { color: var(--ink2); }

/* ── checkout overlay (board Collect → staff → client tip → thank-you) ─── */
.lux-overlay { position: fixed; inset: 0; background: rgba(23, 18, 26, .45); z-index: 60; display: flex; align-items: center; justify-content: center; animation: fadeIn var(--t-fast) ease both; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.lux-sheet { background: var(--paper); width: 480px; max-width: calc(100vw - 48px); max-height: 88vh; overflow: auto; border-radius: var(--r-lg); padding: 26px 28px; box-shadow: var(--shadow-pop); animation: fadeUp var(--t-med) var(--ease-out) both; font-family: "Be Vietnam Pro", system-ui, sans-serif; }
.lux-sheet h2 { margin: 0 0 12px; font-size: 20px; font-weight: 600; letter-spacing: -.01em; color: var(--ink); }
.lux-sheet .row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 12px 0; border-bottom: 1px solid var(--hair); }
.lux-sheet .row:last-child { border-bottom: 0; }
.lux-sub { margin: 16px 0 8px; font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); }
.lux-primary { width: 100%; border: 0; border-radius: var(--r-md); background: var(--ink); color: #fff; font-weight: 600; font-size: 15px; padding: 15px 18px; margin-top: 16px; cursor: pointer; font-family: inherit; }
.lux-primary:disabled { opacity: .4; }
/* #168 — the "Ask for a review" step sits UNDER Done on the Curtain Call screen. Quieter on purpose:
   finishing the sale is the primary action, asking for the review is the optional extra beat. */
.lux-ghost { width: 100%; border: 1px solid var(--hair); border-radius: var(--r-md); background: var(--paper); color: var(--ink); font-weight: 600; font-size: 14px; padding: 13px 18px; margin-top: 9px; cursor: pointer; font-family: inherit; }
.lux-ghost:hover { border-color: var(--brand); }
.lux-center { text-align: center; }
.tip-big { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tip-big button { border: 1px solid var(--hair); background: var(--paper); border-radius: var(--r-md); padding: 18px 10px; display: grid; gap: 2px; justify-items: center; cursor: pointer; font-family: inherit; }
.tip-big button b { font-size: 22px; font-weight: 600; color: var(--ink); }
.tip-big button small { color: var(--ink2); font-variant-numeric: tabular-nums; }
.tip-big button.on { background: var(--ink); border-color: var(--ink); }
.tip-big button.on b { color: #fff; }
.tip-big button.on small { color: rgba(255,255,255,.7); }
.thanks-moon { display: block; width: 56px; height: 56px; border-radius: 50%; background: var(--gold); margin: 6px auto 0; animation: fadeUp var(--t-med) var(--ease-out) both; }

/* ── the Curtain Call register finale (#270) ───────────────────────────── */
.rc-band { background: linear-gradient(160deg, var(--brand) 0%, var(--brand-deep) 100%); border-radius: 18px; padding: 22px 20px 24px; color: #fff; text-align: left; position: relative; overflow: hidden; animation: fadeUp var(--t-med) var(--ease-out) both; }
.rc-wm { position: absolute; right: -26px; top: -26px; width: 140px; height: 140px; color: rgba(255, 255, 255, .08); }
.rc-store { font-size: 10.5px; letter-spacing: .15em; text-transform: uppercase; font-weight: 600; color: rgba(255, 255, 255, .75); }
.rc-thanks { font-size: 22px; font-weight: 700; margin-top: 8px; letter-spacing: -.01em; }
.rc-cap { font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; font-weight: 600; color: rgba(255, 255, 255, .6); margin-top: 12px; }
.rc-total { font-size: 40px; font-weight: 800; letter-spacing: -.02em; color: var(--gold); margin-top: 2px; line-height: 1.05; }
.rc-sub { font-size: 12px; color: rgba(255, 255, 255, .8); margin-top: 8px; }
.rc-qr { margin: 14px auto 4px; display: flex; flex-direction: column; align-items: center; gap: 7px; }
.rc-qr svg { border: 1px solid var(--hair); border-radius: 14px; padding: 8px; background: #fff; width: 132px; height: 132px; }
.rc-qr span { font-size: 12px; color: var(--ink2); font-weight: 500; }

/* ── ticket drawer (Today absorbs Pipeline — owner interview #5) ───────── */
@keyframes drawerIn { from { transform: translateX(40px); opacity: 0; } to { transform: none; opacity: 1; } }
.drawer-backdrop { position: fixed; inset: 0; background: rgba(23, 18, 26, .25); z-index: 50; }
.drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 440px; max-width: 92vw; background: var(--paper); border-left: 1px solid var(--hair); box-shadow: var(--shadow-pop); z-index: 51; overflow: auto; padding: 24px 26px; animation: drawerIn var(--t-med) var(--ease-out) both; font-family: "Be Vietnam Pro", system-ui, sans-serif; }
.drawer h2 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.01em; }
.drawer .row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 11px 0; border-bottom: 1px solid var(--hair); }
.drawer .row:last-child { border-bottom: 0; }
.drawer-close { position: absolute; top: 16px; right: 18px; width: 34px; height: 34px; border: 1px solid var(--hair); border-radius: 50%; background: var(--paper); color: var(--ink2); cursor: pointer; font-size: 15px; }
.drawer .gallery { display: flex; gap: 8px; overflow-x: auto; padding: 4px 0; }
.drawer .gallery img { width: 96px; height: 96px; border-radius: var(--r-md); object-fit: cover; flex: none; cursor: pointer; }
.drawer-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.req-card { border: 1.5px solid var(--warn); border-radius: var(--r-md); padding: 12px 14px; margin: 8px 0 0; display: flex; align-items: center; gap: 12px; background: var(--paper); }
.req-card .who { flex: 1; min-width: 0; }
.req-card .who b { display: block; font-size: 14.5px; font-weight: 600; }
.req-card .who span { font-size: 12.5px; color: var(--ink2); display: block; }
.req-card .who .req-store { display: inline-flex; align-items: center; gap: 5px; margin-top: 3px; font-size: 11.5px; font-weight: 600; color: var(--ink); }
.req-card .who .req-store .store-dot-sm { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.ghost-act { border: 1px solid var(--hair); background: var(--paper); color: var(--ink); border-radius: var(--r-full); padding: 8px 16px; font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit; flex: none; }
.ghost-act:hover { border-color: var(--ink); }
.ghost-act.danger { color: var(--bad); border-color: var(--bad); }

/* Always-visible EN | VI language toggle in the sidebar (#164). */
.lang-toggle { display: inline-flex; align-self: center; border: 1px solid var(--hair); border-radius: var(--r-full); overflow: hidden; background: var(--paper); margin: 6px auto 2px; }
.lang-toggle button { border: 0; background: transparent; padding: 5px 14px; font-size: 11.5px; font-weight: 700; letter-spacing: .04em; color: var(--ink3); font-family: inherit; line-height: 1; cursor: pointer; }
.lang-toggle button:hover { color: var(--ink); }
.lang-toggle button.on { background: var(--brand); color: #fff; }

/* ── phone-app escape hatch + narrow-screen behavior ───────────────────── */
.mobile-switch { display: none; align-items: center; justify-content: center; gap: 8px; background: var(--ink); color: #fff; text-decoration: none; font-weight: 600; font-size: 13.5px; padding: 12px 16px; border-radius: var(--r-md); margin-bottom: 12px; }
.side-phone-link { display: block; text-align: center; color: var(--ink2); text-decoration: none; font-size: 12.5px; font-weight: 600; padding: 10px 0 2px; }
.side-phone-link:hover { color: var(--ink); }
@media (max-width: 760px) {
  .mobile-switch { display: flex; }
  /* the shell stacks: sidebar becomes a slide-over menu, main takes the screen */
  .shell { display: block !important; }
  .shell aside { position: fixed; top: 0; left: 0; bottom: 0; width: min(320px, 86vw); z-index: 46; overflow: auto; box-shadow: var(--shadow-pop); }
  body.nav-collapsed .shell aside { display: none; }
  .shell .nav-expand { display: block !important; position: fixed; top: 12px; right: 14px; z-index: 45; width: 42px; height: 42px; border-radius: 50%; border: 1px solid var(--hair); background: var(--paper); color: var(--ink); font-size: 18px; }
  .shell main { padding: 14px 16px !important; max-width: 100vw; }
  .board-lanes { flex-direction: column; }
  .dash-cols { flex-direction: column; }
  .dash-feed { border-left: 0; padding-left: 0; border-top: 1px solid var(--hair); padding-top: 16px; }
  .money-strip { flex-wrap: wrap; gap: 12px 20px; }
  .big-money { font-size: 44px; }
  .shell .svc-grid { grid-template-columns: 1fr 1fr; }
  .shell .team-grid { grid-template-columns: 1fr 1fr; }
  .drawer { width: 100vw; max-width: 100vw; border-left: 0; }
  .days { max-width: 100%; }
  .desk-fab { right: 16px; bottom: 20px; }
  .desk-fab-menu { right: 16px; bottom: 86px; }
  .board-search { min-width: 0; width: 100%; }
  .lux-sheet { padding: 20px; }
}

/* ── split sign-in (owner interview 2026-06-12) ───────────────────────── */
.lux-login { display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh; min-height: 100dvh; font-family: "Be Vietnam Pro", system-ui, sans-serif; }
.lux-login-brand { background: linear-gradient(155deg, var(--brand-deep) 0%, var(--brand) 60%, #b8587c 100%); color: #fff; display: flex; flex-direction: column; justify-content: center; padding: 8vw 6vw; }
.lux-login-mark { width: 72px; height: 72px; border-radius: 22px; display: block; box-shadow: 0 8px 22px rgba(0,0,0,.18); }
.lux-login-brand h1 { font-size: 46px; font-weight: 700; letter-spacing: -.03em; margin: 26px 0 10px; }
.lux-login-brand p { font-size: 17px; line-height: 1.55; max-width: 30ch; color: rgba(255,255,255,.88); margin: 0; }
.lux-login-form { display: flex; align-items: center; justify-content: center; padding: 8vw 6vw; background: var(--paper); }
.lux-login-form form { width: 100%; max-width: 340px; }
.lux-login-form h2 { font-size: 26px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 18px; }
.lux-field { display: block; font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink3); margin-bottom: 14px; }
.lux-field input { display: block; width: 100%; margin-top: 6px; padding: 14px 16px; border: 1px solid var(--hair); border-radius: var(--r-md); font-size: 16px; font-weight: 400; letter-spacing: normal; text-transform: none; color: var(--ink); background: var(--paper); font-family: inherit; }
.lux-field input:focus { outline: none; border-color: var(--ink); }
.lux-signin { width: 100%; border: 0; border-radius: var(--r-md); background: var(--ink); color: #fff; font-weight: 600; font-size: 15.5px; padding: 15px; cursor: pointer; font-family: inherit; margin-top: 4px; }
.lux-signin:hover { background: #2b2230; }
.lux-demo { margin-top: 20px; font-size: 13px; color: var(--ink3); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lux-chip { border: 1px solid var(--hair); background: var(--paper); color: var(--ink); font-weight: 600; font-size: 13px; padding: 8px 16px; border-radius: var(--r-full); cursor: pointer; font-family: inherit; }
.lux-chip:hover { border-color: var(--ink); }
.lux-login .message { margin-bottom: 14px; }
/* #171 demo seats — replaces the two rows of bare .lux-chip buttons. Each seat is a full-width
   button so the whole card is the target, not a small chip inside it. */
.demo-panel { margin-top: 22px; }
.demo-band { display: flex; gap: 10px; align-items: flex-start; border: 1px solid var(--hair); border-left: 3px solid var(--brand); border-radius: 10px; background: var(--wash); padding: 11px 13px; margin-bottom: 14px; }
.demo-band svg { flex: none; margin-top: 1px; color: var(--brand); }
.demo-band p { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--ink2); }
.demo-band strong { display: block; color: var(--ink); font-weight: 700; font-size: 12.5px; }
.demo-seat { display: flex; gap: 12px; align-items: flex-start; width: 100%; text-align: left; border: 1px solid var(--hair); border-radius: 13px; padding: 12px 14px; margin-bottom: 8px; background: var(--paper); cursor: pointer; font-family: inherit; color: var(--ink); }
.demo-seat:hover { border-color: var(--brand); }
.demo-seat .ds-av { flex: none; width: 34px; height: 34px; border-radius: 50%; background: var(--wash); border: 1px solid var(--hair); display: flex; align-items: center; justify-content: center; font-size: 12.5px; font-weight: 700; color: var(--ink2); }
.demo-seat .ds-body { flex: 1; min-width: 0; }
.demo-seat .ds-nm { display: block; font-size: 14.5px; font-weight: 600; }
.demo-seat .ds-nm em { font-style: normal; font-weight: 400; color: var(--ink2); }
.demo-seat .ds-does { display: block; font-size: 12.5px; line-height: 1.4; color: var(--ink2); margin: 1px 0 5px; }
.demo-seat .ds-try { display: inline-block; font-size: 12px; line-height: 1.35; background: var(--wash); border-radius: 8px; padding: 5px 8px; }
.demo-seat .ds-try u { text-decoration: none; font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink3); margin-right: 5px; }
.demo-seat .ds-go { flex: none; align-self: center; border: 1px solid var(--hair); border-radius: var(--r-full); font-size: 12px; font-weight: 600; padding: 7px 13px; }
.demo-more { display: block; width: 100%; text-align: left; border: 0; background: none; padding: 6px 0 0; font-family: inherit; font-size: 12.5px; font-weight: 600; color: var(--brand); cursor: pointer; }
.demo-cap { font-size: 12px; line-height: 1.45; color: var(--ink2); margin: 12px 0 8px; }
/* Same class name as the phone's (.demo-mini), for the same thing — the three round-the-clock seats
   in the drawer. Do not let these two surfaces drift onto different names again. */
.demo-mini { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; width: 100%; text-align: left; border: 1px solid var(--hair); border-radius: 11px; padding: 10px 13px; margin-bottom: 7px; background: var(--paper); cursor: pointer; font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--ink); }
.demo-mini:hover { border-color: var(--brand); }
.demo-mini small { font-size: 12px; font-weight: 400; color: var(--ink2); }
@media (max-width: 820px) {
  .lux-login { grid-template-columns: 1fr; }
  .lux-login-brand { padding: 48px 32px 36px; }
  .lux-login-brand h1 { font-size: 38px; margin-top: 18px; }
  .lux-login-brand p { font-size: 15px; }
  .lux-login-form { padding: 36px 32px 48px; }
  .lux-login-form form { max-width: 420px; }
}

/* ── money strip (desktop top / owner views) ──────────────────────────── */
.money-strip { display: flex; gap: 26px; border: 1px solid var(--hair); border-radius: var(--r-sm); padding: 12px 16px; background: var(--paper); }
.money-strip span b { display: block; font-size: 22px; font-weight: 300; font-variant-numeric: tabular-nums; }
.money-strip .red b { color: var(--bad); }
.money-strip .gold b { color: var(--gold); }

/* ── Today, #194 ───────────────────────────────────────────────────────────
   The hero composition itself is body.ds .money-strip further down — it was already
   exactly the shape this needed (52px/300 with a gold underline, supporting figures
   inline beneath), so it is reused rather than duplicated under a new name. */

/* The greeting rides inside the hero span, above its caps label, the way the phone's
   t-hero does. Quiet: it says who and when, and then gets out of the way of the number. */
.board-greet {
  display: block;
  font-style: normal;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink2);
  margin-bottom: 7px;
}

/* Search sits alone above the hero now that the page title it used to sit beside is gone. */
.board-searchwrap { display: flex; justify-content: flex-end; margin-bottom: 12px; }
.board-searchwrap .board-search { max-width: 320px; }

/* "N more ›" — a text link, not a button, because it reveals rather than acts. Full width so
   it reads as the end of the lane rather than as another ticket in it. */
.lane-more {
  display: block;
  width: 100%;
  margin: 6px 0 2px;
  padding: 7px 4px;
  border: 0;
  border-top: 1px solid var(--hair);
  background: none;
  font: inherit;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--brand);
  text-align: left;
  cursor: pointer;
}
.lane-more:hover { color: var(--brand-deep); }

/* Payment drill breakdown (#193) — the four figures that left the ledger table live here.
   Two columns, right-aligned numerals, a hairline before the total. */
.pay-breakdown {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px 24px;
  margin: 14px 0 0;
  max-width: 340px;
  font-size: 13px;
}
.pay-breakdown dt { color: var(--ink2); }
.pay-breakdown dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }
.pay-breakdown .tot { border-top: 1px solid var(--hair); padding-top: 8px; margin-top: 2px; font-weight: 700; color: var(--ink); }

/* The customer row s second line: tags and the provenance link share it (#193). */
/* Measured: the tag row (63px) plus the 8px gap plus the trace button (94px) came to 165px in a
   161px column — four pixels over, so it wrapped to a second line and took the whole row to 102px.
   nowrap keeps it on one line and lets the TAGS give up the space, since a clipped tag is still
   recognisable and the customer opens on click anyway. Nothing is removed. */
.cust-meta { display: inline-flex; align-items: center; gap: 8px; flex-wrap: nowrap; min-width: 0; }
.cust-meta .tag-row { display: inline-flex; gap: 4px; min-width: 0; overflow: hidden; }
.cust-meta .tag-chip { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cust-meta .trace-button { flex: 0 0 auto; }

/* ── the secondary menu (#195) ─────────────────────────────────────────────
   Owner picked option A, "Convention", from public/mockups-desktop-panel.html:
   the current screen takes a NEUTRAL fill and the brand colour appears only as a
   thin rule inside its left edge. That is what Linear/Notion/Vercel do, and it
   stops the panel spending burgundy twice — the rail's active pill two inches to
   the left is already using it.

   Numbers are from published sidebar guidance, not taste. Measured before: 32px
   rows, 10px padding, 10px labels at 1.4px tracking — against a recommendation of
   40-48px rows, 16px padding, and 11-12px labels at 1.5-2px. Panel width was
   already right at 228px (guidance: 220-260px), so it is untouched.

   NOT scoped to body.ds: the panel is chrome and must not change appearance
   depending on whether the SCREEN inside it has been converted. */
/* .panel-col-scoped so it outranks `body.ds h2` (0,1,1), which was winning at 18px — meaning the
   panel TITLE changed size depending on whether the screen inside the shell had been converted.
   Same class of bug as the rail background: a content rule reaching into navigation chrome. */
.panel-col .panel-title {
  margin: 0 12px 2px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.01em;
  border-bottom: 0;
  padding-bottom: 0;
}
.panel-col .panel-title small {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--ink3);
  margin-bottom: 2px;
}

/* Groups are separated by SPACE and a small uppercase label — never a box. */
.panel-group { margin: 22px 0 0; }
.panel-group:first-of-type { margin-top: 18px; }
.panel-group h3 {
  margin: 0 12px 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--ink3);
}

.panel-col button[data-view] {
  min-height: 42px;
  padding: 0 16px;
  margin: 0 6px;
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink2);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.panel-col button[data-view]:hover { background: var(--wash); color: var(--ink); }
/* The current screen: neutral fill, ink-dark, and ONE burgundy rule on the inside edge. */
.panel-col button[data-view].active {
  background: var(--wash);
  color: var(--ink);
  font-weight: 700;
  box-shadow: inset 2px 0 0 var(--brand);
}
.panel-col button[data-view]:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* ── report catalogue (#196) ───────────────────────────────────────────────
   A list of things you can run, in the same hairline language as the settings
   accordion — not a grid of dashboard cards, which is the shape we are moving
   away from. Wide rows because each carries a sentence explaining itself. */
.report-picker { border-top: 1px solid var(--hair); max-width: 720px; }
.report-card {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 2px 18px;
  width: 100%;
  text-align: left;
  border: 0;
  border-bottom: 1px solid var(--hair);
  background: none;
  font: inherit;
  padding: 16px 4px;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease-out);
}
.report-card:hover { background: var(--wash); }
.report-card:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.report-card .rc-name { font-size: 15px; font-weight: 600; color: var(--ink); }
.report-card .rc-blurb { grid-column: 1; font-size: 13px; color: var(--ink2); }
.report-card .rc-go {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: center;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand);
  white-space: nowrap;
}

/* ── settings accordion (#193) ─────────────────────────────────────────────
   accordionize() in app.js wraps each rendered panel in <details class="acc">, moving
   the panel's own .panel-head into the <summary>. So the row's words and markup are
   the ones that were already there — only the composition changes. Hairlines, not
   boxes: the closed state should read as a table of contents, not a stack of cards. */
details.acc {
  border-bottom: 1px solid var(--hair);
}
details.acc > summary {
  list-style: none;                 /* the default marker, replaced by our own chevron */
  cursor: pointer;
  position: relative;
  padding: 2px 40px 2px 0;
}
details.acc > summary::-webkit-details-marker { display: none; }
details.acc > summary:hover .panel-head h2,
details.acc > summary:hover .panel-head h3 { color: var(--brand); }
details.acc > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: var(--r-sm); }

/* The head already carries its own padding as a panel header; keep it, drop the card edges
   so the closed rows sit as one continuous list. */
details.acc > summary .panel-head { border: 0; margin: 0; background: none; }

/* Chevron. Rotates on open; the global reduced-motion rule already collapses the transition. */
details.acc > summary::after {
  content: "";
  position: absolute;
  right: 14px; top: 50%;
  width: 8px; height: 8px;
  margin-top: -6px;
  border-right: 1.5px solid var(--ink3);
  border-bottom: 1.5px solid var(--ink3);
  transform: rotate(45deg);
  transition: transform var(--t-fast) var(--ease-out);
}
details.acc[open] > summary::after { transform: rotate(-135deg); margin-top: -2px; }
details.acc[open] > summary .panel-head h2,
details.acc[open] > summary .panel-head h3 { color: var(--brand); }

/* The opened body: it is already a .panel, so strip the duplicate frame it would otherwise
   draw inside its own row. */
details.acc > section.panel {
  border: 0;
  border-radius: 0;
  margin: 0;
  padding-top: 0;
  background: none;
}

/* ── subscription / billing panel ─────────────────────────────────────── */
.billing-panel .subtitle .gold { color: var(--gold); font-weight: 600; }
.bill-badge { display: inline-block; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 3px 10px; border-radius: var(--r-full); vertical-align: middle; margin-left: 8px; border: 1px solid var(--hair); color: var(--ink2); }
.bill-badge.ok { color: var(--gold); border-color: var(--gold); }
.bill-badge.warn { color: var(--bad); border-color: var(--bad); }

/* ── offline banner (task #99) ─────────────────────────────────────────── */
.offline-banner { position: fixed; top: 0; left: 0; right: 0; z-index: 200; text-align: center; padding: 8px 18px; font-size: 13px; font-weight: 600; color: #fff; }
.offline-banner.hidden { display: none; }
.offline-banner.off { background: #43354d; }
.offline-banner.sync { background: var(--brand); }
.offline-banner.conflict { background: #b4541f; cursor: pointer; }
.offline-banner.conflict:hover { background: #9d4719; }
body.offline-on { padding-top: 34px; }

/* ── #189: the design-system scope ──────────────────────────────────────────────────────────────
   Option C, applied ONE SCREEN AT A TIME. `body.ds` is set by a view that has been converted and
   cleared on every other navigation, so a half-migrated app is never half-and-half WITHIN a screen —
   you are always looking at one system or the other, never a seam down the middle.

   Everything here is scoped to body.ds on purpose. Nothing below may be lifted to a bare element
   selector "to finish faster": that would convert all twenty-odd screens the moment it landed, which
   is exactly what reviewing one screen first is meant to prevent.

   The tokens are NOT redefined here — :root above already carries the canonical set. This block is
   only the two things the old styles.css layer still overrides: the typeface, and the surfaces. */
body.ds {
  /* One typeface. styles.css sets Inter on body; this is the whole of the type half of option C.
     The face is self-hosted (/android/fonts/fonts.css) — no network fetch, no silent fallback. */
  font-family: "Be Vietnam Pro", system-ui, sans-serif;
  /* White paper. The old layer paints two gradients here; the system has one surface and reserves
     colour for the things that mean something. */
  background: var(--paper);
}
/* The rail keeps a wash so the shell reads as a separate plane from the page it holds. */
body.ds .rail { background: var(--wash); border-right: 1px solid var(--hair); }
/* Unconditional: the shell is chrome, so it must not change colour depending on whether the SCREEN
   inside it has been converted. Measured drifting between #faf8fb and #faf2f6 while navigating. */
.rail { background: var(--wash); }
.panel-col { border-right: 1px solid var(--hair); }

/* Structure by hairline, not by shadow. A resting card is a bordered rectangle; only things that
   genuinely float (sheets, popovers, the action plate) get --shadow-pop, and none of those are here. */
body.ds .card,
body.ds .panel,
body.ds .kpi-tile { background: var(--paper); border: 1px solid var(--hair); box-shadow: none; border-radius: var(--r-md); }

/* Type scale, from the phone. The eyebrow is the load-bearing one — it is what makes a number read
   as a labelled fact rather than as decoration. */
body.ds .caps { font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); }
body.ds h1 { font-size: 22px; font-weight: 700; letter-spacing: -.02em; }
body.ds h2 { font-size: 18px; font-weight: 700; letter-spacing: -.02em; }
body.ds .sub, body.ds .muted { color: var(--ink2); }

/* The hero money figure. Gold on the underline, never on the digits — gold marks money, it does not
   colour text that has to be read at a glance (#145: gold-on-anything fails contrast fast). */
body.ds .big-money { font-size: 56px; font-weight: 300; letter-spacing: -.03em; color: var(--ink); border-bottom: 2px solid var(--gold); }
body.ds .dash-stats button b { font-size: 21px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
body.ds .dash-feed { border-left: 1px solid var(--hair); }

/* Money batch. These screens are tables and ledgers rather than hero cards, so the system reaches
   them through the shared furniture: the panel, the table, the controls. Structure by hairline; the
   only colour is on the things that carry meaning — money, and a status that needs to be noticed. */
body.ds .panel { background: var(--paper); border: 1px solid var(--hair); box-shadow: none; border-radius: var(--r-md); }
body.ds .panel-head { border-bottom: 1px solid var(--hair); }
/* .panel-group / .panel-title belong to the navigation panel (renderShell), not to a content
   section. They were swept in with the rule above purely because of the shared name prefix; a nav
   group must never draw a card frame. Their styling lives with the rail, below. */
body.ds .panel-group { background: none; border: 0; border-radius: 0; }

body.ds table { border-collapse: collapse; width: 100%; }
body.ds thead th { font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink3); background: none; border-bottom: 1px solid var(--hair); padding: 10px 12px; text-align: left; }
body.ds tbody td { border-bottom: 1px solid var(--hair); padding: 11px 12px; font-size: 13.5px; }
body.ds tbody tr:last-child td { border-bottom: 0; }
/* Zebra striping is decoration standing in for structure. One hairline per row does the job, and a
   ledger of forty rows reads calmer without alternating bands fighting the numbers. */
body.ds tbody tr:nth-child(even) { background: none; }
body.ds tbody tr:hover { background: var(--wash); }
/* Money columns line up or they are not worth printing. */
body.ds td.num, body.ds th.num, body.ds .num, body.ds .money { font-variant-numeric: tabular-nums; }

body.ds .btn, body.ds button.btn, body.ds .btn-sm { border-radius: var(--r-full); border: 1px solid var(--hair);
  background: var(--paper); color: var(--ink); box-shadow: none; font-weight: 600; }
/* One burgundy action per screen — the primary. Everything else is quiet, which is what makes the
   primary readable as "the thing to do" rather than as one more coloured control. */
body.ds .btn.primary, body.ds .btn-primary { background: var(--brand); border-color: var(--brand); color: #fff; }
body.ds input, body.ds select, body.ds textarea { border: 1px solid var(--hair); border-radius: var(--r-md);
  background: var(--paper); padding: 10px 12px; }
body.ds input:focus, body.ds select:focus, body.ds textarea:focus { outline: none; border-color: var(--ink); }

body.ds .pill, body.ds .chip { border-radius: var(--r-full); font-size: 11px; font-weight: 600; }
body.ds .kpi-tile { background: var(--paper); border: 1px solid var(--hair); box-shadow: none; border-radius: var(--r-md); }
body.ds .kpi-tile b, body.ds .kpi-tile .val { font-variant-numeric: tabular-nums; letter-spacing: -.02em; }

/* ── The misses, found by screenshot (#189) ──────────────────────────────────────────────────────
   The first pass set body.ds and a set of GENERIC component classes — .card, .panel, tables — and I
   called the screen converted. It wasn't. Today is built from its own furniture (.money-strip,
   .o-card, .desk-fab) and none of it inherited anything, so the screen kept reading pink while four
   proxy measurements said "system". Lesson recorded in the skill: measure the components a screen
   actually uses, not the ones you expected it to use. */

/* `main` carries its own background, so body.ds never reached the page surface. This is THE reason
   the desktop still looked washed next to the phone. */
body.ds main { background: var(--paper); }

/* The o-card family — the notice, the floor columns, the section heads. Hairline, paper, no tint. */
body.ds .o-card, body.ds .o-card-head, body.ds .money-strip {
  background: var(--paper); border: 1px solid var(--hair); box-shadow: none; border-radius: var(--r-md);
}
body.ds .o-card-head { border: 0; border-bottom: 1px solid var(--hair); border-radius: 0; }
body.ds .o-card + .o-card { margin-top: 12px; }

/* The floating action button is burgundy on the phone — it is the one action that matters, which is
   exactly what burgundy is rationed for. Near-black reads as a system control, not an invitation. */
body.ds .desk-fab { background: var(--brand); }
body.ds .desk-fab:hover { background: var(--brand-deep); }

/* ── Composition, not just colour (#189) ─────────────────────────────────────────────────────────
   The phone leads with ONE number the size of a headline and lets everything else be subordinate.
   The desktop led with six equal facts in a row — which is why it still read like a dashboard once
   the palette matched. A row of equals gives the eye no first thing to land on.

   This changes only the COMPOSITION. WHICH facts appear is owner-locked (#157, #158, #153) and is
   decided in app.js; do not add or remove one here. The first is the day's takings because that is
   the question the screen exists to answer. */
body.ds .money-strip {
  display: block;
  padding: 22px 24px 8px;
  border-radius: var(--r-md);
}
/* The hero. Light weight at this size on purpose — 52px in bold is a shout; in 300 it is simply
   large, and the room around it reads as confidence rather than emphasis. */
body.ds .money-strip > span:first-child {
  display: block;
  padding-bottom: 18px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--hair);
}
body.ds .money-strip > span:first-child b {
  display: inline-block;
  font-size: 52px;
  font-weight: 300;
  line-height: 1.05;
  letter-spacing: -.03em;
  margin-top: 6px;
  padding-bottom: 6px;
  /* Gold underlines money; it never colours the digits (the rule, and #145's contrast lesson). */
  border-bottom: 2px solid var(--gold);
}
/* The supporting facts: one wrapping row beneath, sized to support the hero rather than compete. */
body.ds .money-strip > span:not(:first-child) {
  display: inline-block;
  padding: 14px 34px 14px 0;
  vertical-align: top;
}
body.ds .money-strip > span:not(:first-child) b {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -.02em;
}

/* ── My earnings (#46, owner pick A) ─────────────────────────────────────────
   Money left, the evidence for it right. Own `me-` namespace on purpose: .card, .panel and .tile
   already mean specific things elsewhere in this file, and grouping a new screen onto them by
   shared name is exactly what drew card frames around the nav groups in #193.

   Sizes are the phone's scale — 10.5 / 11 / 12 / 12.5 / 13 / 15, plus one display numeral. The
   mockup carried four in-between sizes (17/20/23/27); they are snapped here, which is the deviation
   the mockup itself flagged for fixing once it became real code. */
body.ds .me-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
body.ds .me-pick { display: inline-flex; gap: 4px; background: var(--wash); border-radius: var(--r-full); padding: 3px; }
body.ds .me-pick button {
  border: 0;
  background: none;
  cursor: pointer;
  font: 700 12.5px/1 "Be Vietnam Pro", system-ui, sans-serif;
  color: var(--ink2);
  padding: 8px 16px;
  border-radius: var(--r-full);
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
/* Burgundy is rationed: the chosen period is the one place it appears on this screen. */
body.ds .me-pick button.on { background: var(--brand); color: #fff; }
body.ds .me-pick-note { font-size: 11.5px; color: var(--ink2); margin: 0; }

body.ds .me-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: start; }
body.ds .me-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
/* Hairlines, not shadows — a resting card gets a border. */
body.ds .me-card { background: var(--paper); border: 1px solid var(--hair); border-radius: var(--r-md); padding: 20px; }
body.ds .me-card h2 { font: 700 15px/1.3 "Be Vietnam Pro", system-ui, sans-serif; color: var(--ink); margin: 0 0 14px; letter-spacing: -.01em; }

body.ds .me-hero .caps { display: block; margin-bottom: 6px; }
/* The one display numeral on the screen. Gold underline, because it is money. */
body.ds .me-total {
  display: inline-block;
  font: 700 40px/1.1 "Be Vietnam Pro", system-ui, sans-serif;
  letter-spacing: -.02em;
  color: var(--ink);
  border-bottom: 2px solid var(--gold);
  padding-bottom: 3px;
  margin-bottom: 18px;
}
/* Every numeral on this screen lines up: the money, the dates, the times. */
body.ds .me-total,
body.ds .me-split .v,
body.ds .me-tile .tv,
body.ds .me-tile .ts,
body.ds .me-rows .when,
body.ds .me-rows .amt,
body.ds .me-rows .tip,
body.ds .me-rate-head strong { font-variant-numeric: tabular-nums; }

body.ds .me-split { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
body.ds .me-split button {
  display: flex;
  flex-direction: column;
  text-align: left;
  background: var(--wash);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 10px 12px;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}
body.ds .me-split button:hover { background: var(--paper); border-color: var(--hair); }
body.ds .me-split button.on { background: var(--paper); border-color: var(--brand); }
body.ds .me-split .k { display: block; flex: 1 1 auto; font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); margin-bottom: 4px; }
body.ds .me-split .v { display: block; font-size: 15px; font-weight: 700; color: var(--ink); }
body.ds .me-hint { font-size: 11.5px; color: var(--ink2); margin: 12px 0 0; }

body.ds .me-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
body.ds .me-tile { border: 1px solid var(--hair); border-radius: var(--r-sm); padding: 14px; }
body.ds .me-tile:last-child { grid-column: 1 / -1; }
body.ds .me-tile .tk { font-size: 10.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--ink3); }
body.ds .me-tile .tv { font-size: 15px; font-weight: 700; color: var(--ink); margin: 6px 0 2px; }
/* Gold is money and nothing else — so the dollar records get it and the two rate tiles do not (#247). */
body.ds .me-tile.g .tv { color: var(--gold); }
body.ds .me-tile .ts { font-size: 11px; color: var(--ink2); }

body.ds .me-rows { width: 100%; border-collapse: collapse; }
body.ds .me-rows td { border-bottom: 1px solid var(--hair); padding: 11px 0; vertical-align: top; font-size: 13px; }
body.ds .me-rows tr:last-child td { border-bottom: 0; }
body.ds .me-rows .when { color: var(--ink2); white-space: nowrap; padding-right: 14px; }
body.ds .me-rows .when span { display: block; font-size: 11.5px; color: var(--ink3); }
body.ds .me-rows .who { display: block; font-weight: 700; color: var(--ink); }
body.ds .me-rows .svc { display: block; font-size: 12px; color: var(--ink2); }
body.ds .me-rows .r { text-align: right; white-space: nowrap; }
body.ds .me-rows .amt { display: block; font-weight: 700; color: var(--ink); }
body.ds .me-rows .tip { display: block; font-size: 11.5px; color: var(--ink2); }

body.ds .me-filter-note { font-size: 11.5px; color: var(--ink2); margin: -4px 0 12px; }
body.ds .me-filter-note button { border: 0; background: none; padding: 0; cursor: pointer; color: var(--brand); font: 700 11.5px "Be Vietnam Pro", system-ui, sans-serif; }
body.ds .me-empty { font-size: 12.5px; color: var(--ink2); margin: 0; }

body.ds .me-rate-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
body.ds .me-rate-head strong { font-size: 28px; font-weight: 700; letter-spacing: -.02em; color: var(--ink); }
body.ds .me-rate-head small { font-size: 11.5px; color: var(--ink2); }
/* Gold is money, so stars are not gold — they are ink, which is also how the phone draws them. */
body.ds .me-stars { display: inline-flex; gap: 2px; color: var(--ink); }
body.ds .me-stars.sm { color: var(--ink2); }
body.ds .me-rate-note { border-top: 1px solid var(--hair); padding-top: 10px; margin-top: 10px; }
body.ds .me-rate-note p { font-size: 12.5px; color: var(--ink2); margin: 4px 0 0; }

/* ---- #46 walk-in board (owner pick A) --------------------------------------------------------
   Three standing columns. No money on this screen, so no gold: the only colour is the burgundy on
   the one card that answers the question the screen exists to answer (who is next). Own `wb-`
   namespace for the same reason `me-` has one — `.q`, `.board` and `.col` are far too generic to
   introduce into a stylesheet this old. */
body.ds .wb-wrap { max-width: 1400px; }
body.ds .wb-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
body.ds .wb-title { font: 700 21px/1.25 "Be Vietnam Pro", system-ui, sans-serif; letter-spacing: -.02em; color: var(--ink); margin: 0; }
body.ds .wb-policy { font-size: 12.5px; color: var(--ink2); margin: 4px 0 0; max-width: 60ch; }
body.ds .wb-tools { display: flex; align-items: center; gap: 10px; }
body.ds .wb-rule {
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink);
  border-radius: var(--r-full); padding: 7px 14px; cursor: pointer;
  font: 700 12px "Be Vietnam Pro", system-ui, sans-serif;
  transition: border-color var(--t-fast) var(--ease-out);
}
body.ds .wb-rule:hover { border-color: var(--brand); }
body.ds .wb-live { font-size: 11px; font-weight: 700; letter-spacing: .06em; color: var(--ink3); font-variant-numeric: tabular-nums; white-space: nowrap; }
body.ds .wb-store { font: 700 13px "Be Vietnam Pro", system-ui, sans-serif; color: var(--ink2); margin: 22px 0 10px; }

body.ds .wb-board { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; align-items: start; }
body.ds .wb-col { background: var(--paper); border: 1px solid var(--hair); border-radius: var(--r-md); padding: 16px; min-width: 0; }
body.ds .wb-colhead { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--hair); margin-bottom: 12px; }
body.ds .wb-colhead + .wb-colhead, body.ds .wb-col > * + .wb-colhead { margin-top: 22px; }
body.ds .wb-n { font-size: 11px; color: var(--ink3); font-variant-numeric: tabular-nums; white-space: nowrap; }

body.ds .wb-p { display: flex; align-items: flex-start; gap: 10px; padding: 10px; border-radius: var(--r-sm); border: 1px solid transparent; }
body.ds .wb-p.next { border-color: var(--brand); background: var(--wash); }
body.ds .wb-p.off { opacity: .62; }
body.ds .wb-p[role="button"] { cursor: pointer; }
body.ds .wb-p[role="button"]:hover { border-color: var(--hair); background: var(--wash); }
/* The rotation cards became clickable 2026-08-05 (book with this technician), which put the hover
   above on the "Next" card for the first time — and it out-specifies `.wb-p.next`, so hovering the
   one card on this board that carries burgundy would have greyed it out. Burgundy marks who is up
   next; it does not come off because the pointer passed over it. */
body.ds .wb-p.next[role="button"]:hover { border-color: var(--brand); }
body.ds .wb-av {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: var(--r-full);
  background: var(--wash); color: var(--ink2); display: flex; align-items: center; justify-content: center;
  font: 700 11px "Be Vietnam Pro", system-ui, sans-serif; letter-spacing: .02em;
}
body.ds .wb-p.next .wb-av { background: var(--brand); color: var(--paper); }
body.ds .wb-b { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
body.ds .wb-nm { font-size: 13px; font-weight: 700; color: var(--ink); }
body.ds .wb-nm small { font-weight: 400; color: var(--ink3); }
body.ds .wb-sub { font-size: 11.5px; color: var(--ink2); font-variant-numeric: tabular-nums; }
body.ds .wb-rank { flex: 0 0 auto; font-size: 11px; font-weight: 700; color: var(--ink3); font-variant-numeric: tabular-nums; }
body.ds .wb-rank.next { color: var(--brand); letter-spacing: .1em; text-transform: uppercase; }
body.ds .wb-free { flex: 0 0 auto; text-align: right; font-size: 10.5px; color: var(--ink3); line-height: 1.35; }
body.ds .wb-free b { display: block; font-size: 11.5px; color: var(--ink2); font-variant-numeric: tabular-nums; }
body.ds .wb-pill { flex: 0 0 auto; font-size: 10.5px; font-weight: 700; color: var(--ink2); background: var(--wash); border-radius: var(--r-full); padding: 3px 9px; white-space: nowrap; }

/* Quiet by design: a column of people should read as people, not as a wall of buttons. */
body.ds .wb-rot + .wb-rot { margin-top: 4px; }
body.ds .wb-acts { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 10px 10px 52px; }
body.ds .wb-act {
  border: 1px solid var(--hair); background: var(--paper); color: var(--ink2);
  border-radius: var(--r-full); padding: 4px 10px; cursor: pointer;
  font: 700 11px "Be Vietnam Pro", system-ui, sans-serif;
  transition: border-color var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
body.ds .wb-act:hover { border-color: var(--brand); color: var(--ink); }
body.ds .wb-act.primary { border-color: var(--brand); color: var(--brand); }
body.ds .wb-act:disabled { opacity: .5; cursor: default; }
body.ds .wb-empty { font-size: 12px; color: var(--ink3); margin: 0; padding: 4px 0 2px; }

/* One column below the desktop breakpoint. Option A's whole argument is the width — without it the
   honest layout is the phone's stack, in the phone's order: money, then the rows that explain it.
   The board makes the same trade: three abreast is the point, and below this width the honest
   fallback is the phone's single column rather than three squeezed ones. */
@media (max-width: 1000px) {
  body.ds .me-two { grid-template-columns: minmax(0, 1fr); }
}

/* The board needs more room than the two-column screens, and it is the rail plus the contextual
   panel — about 370px before the content starts — that decides when. Measured on the owner's own
   machine: at a 1057px viewport three columns resolve to 218px each, which is narrower than the
   card inside them wants (32px avatar + name + two sub-lines + a rank tag). A column that has to
   wrap a person's name is worse than a column below the fold, so the board steps down instead. */
@media (max-width: 1280px) {
  body.ds .wb-board { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  body.ds .wb-board { grid-template-columns: minmax(0, 1fr); }
}
