/* ─────────────────────────────────────────────────────────────────────────────
   Booking Logger — the LIVE SITE's design system, verified against site.css on
   2026-09-01 (not brand-tokens.md, which is a hand-kept snapshot and drifts).
   Bone ground, warm brown ink, rose accent, Libre Bodoni for display moments,
   square tracked buttons. Nothing green anywhere — nothing on the site is.

   The variable NAMES below are the ones the JS and markup already use; only the
   VALUES moved to the new brand. --good/--bad/--amber are semantic slots, now
   expressed in the brand's warm range rather than emerald/rose-red/amber.
   ───────────────────────────────────────────────────────────────────────────── */
:root {
  --bg: #F7ECE5;            /* bone — the ground everywhere */
  --surface: #FCF7F2;       /* field/card fill, one step lighter than bone */
  --surface-2: #F3E8DF;
  --border: #EADACF;        /* hairline */
  --border-strong: #D9C6B8;
  --ink: #3B2A24;           /* warm dark brown — never black */
  --ink-soft: #5C463D;
  --muted: #786258;         /* stone */
  --faint: #A18A7E;
  --rose: #B0716B;
  --rose-deep: #A2574F;
  --rose-pale: #EFD8D2;
  --good: #3B2A24;          /* success speaks in ink on a rose-pale seal */
  --good-soft: #EFD8D2;
  --good-ink: #3B2A24;
  --bad: #A2574F;
  --bad-soft: #F3DCD6;
  --bad-ink: #8A4038;
  --amber: #B0716B;         /* the "still owes an answer" dot is rose now */
  --amber-soft: #EFD8D2;
  --amber-ink: #A2574F;
  --slate: #786258;
  --slate-soft: #F0E4DA;
  --stone-deep: #6A5349;    /* the reschedule mode, distinct from both ink and rose */
  --brand: #B0716B;
  --brand-soft: #EFD8D2;
  --action: #3B2A24;        /* primary buttons are ink, like the site's */
  --action-press: #2C1F1A;
  /* ROUNDED, as of the second design pass (Ujjwal, 2026-09-01: "I liked the
     rounded fields and buttons from earlier"). The live website is square-edged
     and stays square-edged; this is a phone tool used one-handed, and the softer
     shapes are what he chose after seeing both drawn side by side. */
  --radius-tile: 999px;     /* buttons and chips are pills */
  --radius-lg: 12px;        /* fields */
  --radius-card: 16px;      /* the client card */
  --radius-sheet: 22px;     /* the drawer top corners */
  --line-soft: #E2CFC3;     /* the outline on a quiet button - warmer than the hairline */
  --ease-sheet: cubic-bezier(0.32, 0.72, 0, 1);
  --serif: 'Libre Bodoni', 'Bodoni Moda', Didot, Georgia, serif;
  --shadow-sm: none;
  --shadow-lg: 0 10px 30px -10px rgba(59, 42, 36, .30);
  --shadow-2xl: 0 24px 60px -20px rgba(44, 31, 26, .38);
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* Light-only by design, same as the live site (`color-scheme: light only`). */
:root { color-scheme: light; }

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: inherit;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  padding:
    env(safe-area-inset-top)
    20px
    calc(env(safe-area-inset-bottom) + 104px);
}
.wrap { max-width: 430px; margin: 0 auto; }

/* ── Type scale ──────────────────────────────────────────────────────────────
   Display moments are the site's italic didone; labels are small, uppercase and
   tracked, exactly like the site's buttons and kickers. */
.t-display { font-family: var(--serif); font-style: italic; font-weight: 500; font-size: 1.75rem; line-height: 1.14; letter-spacing: 0; }
.t-title { font-family: var(--serif); font-style: italic; font-weight: 500; font-size: 1.45rem; line-height: 1.15; letter-spacing: 0; }
.t-heading { font-size: .875rem; line-height: 1.3; font-weight: 600; }
.t-label {
  font-size: .625rem; line-height: 1.2; font-weight: 500; letter-spacing: .2em;
  text-transform: uppercase; color: var(--muted);
}
.t-caption { font-size: .75rem; line-height: 1.4; font-weight: 400; color: var(--muted); }
.hidden { display: none !important; }

/* ── Header ─────────────────────────────────────────────────────────────────── */
header.app {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 10px 4px; margin: 0 -4px 4px;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: saturate(1.2) blur(10px);
  -webkit-backdrop-filter: saturate(1.2) blur(10px);
  border-bottom: 1px solid var(--border);
}
header.app .brand { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
/* Lia's own logo, inline SVG, taking its colour from the text around it. It is
   deliberately quiet in the header: the screen title carries the page, and two
   display-weight identities stacked on each other was the single biggest source
   of "dense and busy". It is NOT set in a substitute typeface anywhere. */
header.app .logo { display: block; width: 104px; height: auto; color: var(--ink); opacity: .82; }
header.app .actions { display: flex; align-items: center; gap: 2px; }

/* The sign-in lockup — the ONE place the name is allowed to be the loudest
   thing on screen, because there is nothing else on the screen. */
.lockup { display: flex; flex-direction: column; align-items: center; margin: 72px 0 44px; }
.lockup .logo { width: 210px; height: auto; color: var(--ink); }
.lockup-rule { width: 34px; border-top: 1px solid var(--border-strong); margin: 26px 0 14px; }
.login-note { text-align: center; margin: 18px 12px 0; }

.icon-btn {
  position: relative;
  display: grid; place-items: center;
  height: 44px; width: 44px; flex: none;
  border: none; background: transparent; color: var(--muted);
  border-radius: 0; cursor: pointer;
  transition: background .15s, color .15s;
}
.icon-btn:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink); }
.icon-btn:active { transform: scale(.94); }
.icon-btn svg { width: 20px; height: 20px; }
.icon-btn.on { color: var(--rose-deep); }
.icon-btn .dot {
  position: absolute; top: 9px; right: 10px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--rose);
  border: 2px solid var(--bg);
}

/* ── Cards ──────────────────────────────────────────────────────────────────── */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 0; box-shadow: none;
}
.pad { padding: 20px 18px; }

/* ── Buttons — square, uppercase, tracked, like the site's ─────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; font-family: inherit;
  font-size: .6875rem; font-weight: 500; letter-spacing: .18em; text-transform: uppercase;
  min-height: 50px; padding: 13px 18px; border-radius: var(--radius-tile);
  border: 1px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: background .18s, color .18s, transform .12s var(--ease-sheet), border-color .18s;
}
.btn svg { width: 16px; height: 16px; flex: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: default; transform: none; }

.btn-primary { background: var(--action); color: var(--bg); }
.btn-primary:hover { background: var(--action-press); }

/* Quiet, not loud: a hairline outline in the warm line colour rather than a
   full-strength ink border. Two ink-weight buttons side by side read as two
   equally urgent things, and only one of them ever is. */
.btn-outline {
  background: transparent; color: var(--ink-soft);
  border-color: var(--line-soft);
}
.btn-outline:hover { background: var(--surface); }

.btn-ghost {
  background: transparent; color: var(--muted); border-radius: var(--radius-tile);
  font-weight: 500; padding: 12px 16px;
}
.btn-ghost:hover { color: var(--ink); }

.btn-bad { background: transparent; color: var(--bad-ink); border-color: var(--line-soft); }
.btn-slate { background: transparent; color: var(--slate); border-color: var(--line-soft); }

/* ── Segmented control (Log modes) ──────────────────────────────────────────── */
/* Three modes, three clearly different colours when chosen — logging a
   cancellation and logging a booking must never look alike at a glance. The
   unchosen two sit back in stone so the chosen one carries. */
.seg {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px;
  padding: 4px; margin-bottom: 26px;
  background: var(--surface-2); border: none;
  border-radius: var(--radius-tile);
}
.seg button {
  font-family: inherit; font-size: .625rem; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  min-height: 42px; padding: 9px 4px; border: none; border-radius: var(--radius-tile); cursor: pointer;
  background: transparent; color: var(--muted); white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 5px;
  transition: background .2s var(--ease-sheet), color .2s;
}
.seg button svg { width: 13px; height: 13px; }
.seg button.on { background: var(--bg); color: var(--ink); box-shadow: 0 1px 3px rgba(59, 42, 36, .10); }
.seg button.on.mode-cancel { color: var(--rose-deep); }
.seg button.on.mode-resched { color: var(--stone-deep); }

/* ── Fields ─────────────────────────────────────────────────────────────────── */
/* One rhythm for the whole form: 20px between fields, 8px between a label and
   its input. Fields are laid on the bone directly — the form used to sit inside
   a bordered card, which put a box inside a box and made every screen read as
   heavier than it is. */
.field { margin-top: 20px; display: flex; flex-direction: column; }
.field:first-child { margin-top: 0; }
.field > label {
  display: block; font-size: .625rem; font-weight: 500; color: var(--muted);
  letter-spacing: .2em; text-transform: uppercase;
  margin-bottom: 8px;
}
.field .hint { font-weight: 400; color: var(--faint); text-transform: none; letter-spacing: .02em; }
.field .hint.hint-required { color: var(--bad-ink); font-weight: 500; }

/* Side-by-side fields. `align-items: stretch` plus a growing label is what keeps
   the two inputs on one line even when one label wraps to two — previously a
   wrapped label pushed its input down and the pair sat visibly out of step. */
.row { display: flex; gap: 12px; align-items: stretch; }
.row > .field { flex: 1; margin-top: 20px; }
.row > .field > label { flex: 1 0 auto; }

input, select, textarea {
  width: 100%; font-size: 16px; font-family: inherit; color: var(--ink);
  min-height: 52px;
  padding: 13px 16px; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); transition: border-color .15s, box-shadow .15s;
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--ink);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose) 24%, transparent);
}
textarea { resize: vertical; min-height: 56px; line-height: 1.45; }
select {
  appearance: none; -webkit-appearance: none; cursor: pointer; padding-right: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23786258' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}
select:invalid { color: var(--faint); }

/* ── Inline message ─────────────────────────────────────────────────────────── */
.msg { text-align: center; font-size: .8125rem; margin-top: 14px; min-height: 16px; line-height: 1.4; }
.msg.ok { color: var(--ink); font-weight: 600; }
.msg.err { color: var(--bad-ink); }

/* ── Screen headings ────────────────────────────────────────────────────────── */
.screen-head { margin: 10px 0 22px; }
.screen-head h2 { margin: 0; }
.screen-head p { margin: 8px 0 0; max-width: 34ch; }

/* ── The client list ──────────────────────────────────────────────────────────
   Two groups, and they are labelled: the few people who owe an answer, then
   everyone else. Unlabelled, the pink cards and the quiet rows ran into each
   other and the screen read as one dense wall. */
.list-section { margin: 26px 0 10px; display: flex; align-items: center; gap: 8px; }
.list-section + .client-list { margin-bottom: 26px; }
.list-section .dot-mark {
  width: 6px; height: 6px; border-radius: 50%; background: var(--rose); flex: none;
}
.list-section .t-label.urgent { color: var(--rose-deep); }

.client-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
/* A soft card on the bone, NOT a block of pink. The rose dot beside the name is
   the only colour on it - two full pink slabs stacked was most of what read as
   "dense and busy", and the fill was doing no work the dot cannot do. */
.client-list li {
  position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 18px;
  transition: box-shadow .2s, outline-color .2s;
}
.client-list li.highlight {
  outline: 1.5px solid var(--rose);
  outline-offset: 2px;
}
.client-list .p-name {
  font-family: var(--serif); font-weight: 500; font-size: 1.0625rem;
  display: flex; align-items: center; gap: 9px;
}
.client-list .p-name .p-name-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.client-list .p-name .p-name-text.is-nameless { color: var(--muted); font-style: italic; }
/* The day sits at the end of the name line, in the body face — the card is
   asking about a day, and at the right margin several cards can be scanned at
   once without reading each one. */
.client-list .p-name .p-when { font-family: inherit; font-weight: 400; flex: none; }
.client-list .p-meta { margin-top: 6px; color: var(--ink-soft); }
/* One primary answer, then two quiet ones. The other two outcomes (cancelled,
   rescheduled) live on her page behind "More" — the approved design, and what
   turned a five-button slab into a card. */
/* One row: the answer she gives most, then the other answer, then everything
   else behind More. Widths are proportional rather than equal so the primary
   reads as the primary without needing a heavier colour. */
.client-list .p-actions { display: grid; gap: 8px; margin-top: 16px; }
.client-list .p-actions.p-actions-row { grid-auto-flow: column; grid-template-columns: 1fr auto auto; }
.client-list .p-actions .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.client-list .p-actions .row3 { grid-template-columns: 1fr 1fr 1fr; }
.client-list .p-actions .btn {
  min-height: 46px; padding: 11px 16px; font-size: .5625rem; letter-spacing: .14em;
}
.client-list .p-actions .btn-more { padding: 11px 18px; color: var(--muted); }
.client-list .p-actions.p-actions-single { grid-template-columns: 1fr; margin-top: 8px; }
.client-list .p-actions .btn-quiet {
  background: transparent; color: var(--ink-soft); border-color: var(--line-soft);
}
.client-list .p-actions .btn-quiet:hover { background: var(--surface-2); }
.pending-empty {
  text-align: center; padding: 40px 20px; color: var(--muted);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-card); background: transparent;
}
.pending-empty .em-check {
  width: 44px; height: 44px; margin: 0 auto 10px; color: var(--ink);
  display: grid; place-items: center;
  background: var(--rose-pale); border-radius: 50%;
}
.pending-empty .em-check svg { width: 22px; height: 22px; }

/* Quiet rows for everyone who owes nothing: flat on the bone, hairline-topped. */
/* A quiet row is NOT a card. It has to explicitly undo the card's border, fill
   and radius, or it inherits all three and every name in the list turns into
   another box — which is the density the card was slimmed down to avoid. */
.client-list li.client-row {
  background: transparent;
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: 15px 0;
  display: flex; align-items: center; gap: 12px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.client-list li.client-row .row-main { flex: 1; min-width: 0; }
.client-list li.client-row .row-chev { flex: none; display: grid; place-items: center; color: var(--faint); }
.client-list li.client-row .row-chev svg { width: 16px; height: 16px; display: block; }
.client-list li.client-row:last-child { border-bottom: 1px solid var(--border); }
.client-list li.client-row:active { background: var(--surface-2); }
.client-list li.client-row:focus-visible {
  outline: 1.5px solid var(--rose);
  outline-offset: 4px;
  border-radius: var(--radius-lg);
}
.client-list li.client-row .p-name { font-family: inherit; font-weight: 400; font-size: 1rem; }
.client-sub { margin-top: 3px; color: var(--muted); }

/* The "still needs an answer" marker. */
.due-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--rose);
  margin-right: 8px;
  vertical-align: middle;
  flex: none;
}
.client-list li.due .p-name { display: flex; align-items: center; }

/* Where the photographs stand, at a glance. On a row they sit to the RIGHT of
   the name rather than under it — stacked, they doubled every row's height and
   were most of why the list felt dense. */
.chips { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex: none; }
.chip {
  display: inline-flex; align-items: center; gap: 4px;
  height: 22px; padding: 0 9px; border-radius: var(--radius-tile);
  font-size: .5625rem; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink); background: var(--rose-pale);
}
.chip.chip-open { background: transparent; border: 1px solid var(--rose); color: var(--rose-deep); }
.chip.chip-quiet { background: transparent; border: 1px solid var(--border); color: var(--muted); }

/* ── "A few questions" ────────────────────────────────────────────────────────
   Quieter than a due card on purpose. Nothing here is urgent — an unanswered
   question costs nothing today, and a section that shouts would train her to
   ignore the one that does matter. */
.client-list .gap-card {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 16px 16px 14px;
}
.gap-card .gap-q { font-size: .9375rem; line-height: 1.35; }
.gap-card .gap-meta { margin-top: 5px; }
.gap-card .gap-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.gap-card .gap-actions .btn {
  width: auto; flex: 1 1 auto; min-height: 42px; padding: 10px 14px;
  font-size: .5625rem; letter-spacing: .14em;
}
/* Always last and always right — it is the way out, not an answer, and on the
   left it read as a heading for the row below it. */
.gap-card .gap-actions .gap-skip { flex: 0 0 auto; margin-left: auto; padding: 10px 12px; color: var(--faint); }
#gapsCount { margin-left: auto; }

/* ── The client page ────────────────────────────────────────────────────────── */
.client-page .cp-back { border-radius: var(--radius-tile);
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 0 8px 0 0; margin-left: -4px;
  border: none; background: transparent; color: var(--muted); cursor: pointer;
  font-family: inherit; font-size: .625rem; letter-spacing: .2em; text-transform: uppercase;
}
.client-page .cp-back svg { width: 18px; height: 18px; }
.client-page .cp-head { margin: 4px 0 22px; }
.client-page .cp-head h2 { margin: 0; }
.client-page .cp-head p { margin: 8px 0 0; }
.cp-section { margin-top: 26px; }
.cp-section-title {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-bottom: 12px;
}
.cp-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-card); padding: 18px; }
.cp-line { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.cp-line + .cp-line { margin-top: 10px; }
.cp-line .cp-service { font-family: var(--serif); font-size: 1.125rem; font-weight: 500; }
.cp-rule { border-top: 1px solid var(--border); margin: 14px 0; }
.cp-price { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.cp-price .amount { font-size: 1.0625rem; margin-top: 3px; }
.cp-price .amount .unconfirmed { font-size: .75rem; color: var(--muted); }
.cp-price .btn { width: auto; min-height: 44px; padding: 0 18px; }
.cp-history { list-style: none; margin: 0; padding: 0; }
.cp-history li { border-top: 1px solid var(--border); }
.cp-history li:last-child { border-bottom: 1px solid var(--border); }
/* Every earlier visit opens. They used to be plain text, which meant only the
   most recent booking could ever be corrected. */
.cp-history .cp-history-row {
  width: 100%; display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 14px 0; background: transparent; border: none; cursor: pointer;
  font-family: inherit; font-size: .9375rem; color: var(--ink); text-align: left;
}
.cp-history .cp-history-row:active { background: var(--surface-2); }

.cp-open-booking {
  width: 100%; min-height: 44px; border: none; background: transparent; cursor: pointer;
  font-family: inherit; font-size: .5625rem; letter-spacing: .18em; text-transform: uppercase;
  color: var(--muted); border-radius: var(--radius-tile);
}
.cp-open-booking:active { background: var(--surface-2); }

/* What she has actually paid, across every visit that happened. */
.cp-worth { margin: 12px 0 0; display: flex; align-items: baseline; gap: 6px; }
.cp-worth .amount { font-size: 1.25rem; font-family: var(--serif); }

/* Two slots, kept to a modest 4:5 so the pair reads as a status line rather
   than taking half the screen. An empty slot is HAIRLINE and quiet; only the
   shot she actually owes is drawn in rose — otherwise "nothing here yet" and
   "you still owe this" looked identical, and both looked like an error. */
.cp-photos { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cp-photo-slot {
  aspect-ratio: 4 / 5; border: 1px solid var(--border); border-radius: var(--radius-lg);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  color: var(--muted); background: var(--surface); cursor: pointer;
  font-family: inherit; font-size: .5625rem; letter-spacing: .16em; text-transform: uppercase;
}
.cp-photo-slot svg { width: 22px; height: 22px; }
.cp-photo-slot.owed { border: 1px dashed var(--rose); color: var(--rose-deep); background: transparent; }
.cp-photo-slot.held { border-color: transparent; background: var(--rose-pale); color: var(--ink); }
.cp-bottom { margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--border); }

/* ── Footer ─────────────────────────────────────────────────────────────────── */
footer { text-align: center; color: var(--faint); font-size: .625rem; margin-top: 24px; letter-spacing: .22em; text-transform: uppercase; }

/* ── Bottom tab bar — flat on the bone, like the site's chrome ──────────────── */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: 30;
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid var(--border);
  padding: 6px 0 calc(env(safe-area-inset-bottom) + 8px);
}
.tabbar .rail {
  display: flex; max-width: 430px; margin: 0 auto;
}
.tabbar button {
  position: relative; flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 52px;
  border: none; background: transparent; cursor: pointer; border-radius: 0;
  color: var(--muted); font-family: inherit;
  font-size: .5625rem; font-weight: 500; letter-spacing: .2em; text-transform: uppercase;
  transition: color .2s;
}
.tabbar button svg { width: 21px; height: 21px; flex: none; }
.tabbar button[aria-selected="true"] { color: var(--ink); }
.tabbar button .tab-dot {
  width: 4px; height: 4px; border-radius: 50%; background: transparent;
}
.tabbar button[aria-selected="true"] .tab-dot { background: var(--rose); }
.tabbar .tab-badge {
  position: absolute; top: 4px; right: calc(50% - 22px); min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 50%; background: var(--rose-deep); color: var(--bg);
  font-size: .625rem; font-weight: 700; line-height: 16px; text-align: center;
}

/* ── Bottom sheet ───────────────────────────────────────────────────────────── */
/* ── The drawer ───────────────────────────────────────────────────────────────
   Ujjwal, 2026-09-01: "drawers nice but make sure the animations are nice and
   smooth as well of it opening and closing."

   THE THREE THINGS THAT MAKE IT FEEL SMOOTH, none of which is the duration:

   1. OPEN AND CLOSE ARE DIFFERENT CURVES AND DIFFERENT LENGTHS. A drawer that
      leaves on the same easing it arrived on feels sluggish going away, because
      nobody is reading it any more. In: 420ms on a decelerating spring, so it
      settles rather than stops dead. Out: 260ms accelerating, so it is gone
      before it is missed.
   2. THE SCRIM IS TIED TO THE SHEET, not to its own timing. It fades in over the
      same 420ms and out over the same 260ms; a scrim that finishes first leaves
      the sheet sliding over an already-flat background, which is the single
      most common way this reads as cheap.
   3. IT ANIMATES ONLY `transform` AND `opacity`, and declares them up front with
      `will-change`, so the whole thing runs on the compositor. Animating height,
      top or padding here would drop frames on Lia's phone.

   The drag-to-dismiss handler in ui-sheet.js turns the transition OFF while her
   finger is down (so the sheet tracks the finger exactly) and back ON when she
   lets go — that is what makes a flick feel connected rather than replayed. */
.sheet-scrim {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: flex-end; justify-content: center;
  background: rgba(59, 42, 36, .38);
  backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity 260ms cubic-bezier(.4, 0, 1, 1);
  padding-bottom: env(safe-area-inset-bottom);
  will-change: opacity;
}
.sheet-scrim.in { opacity: 1; transition-duration: 420ms; transition-timing-function: cubic-bezier(.16, 1, .3, 1); }
.sheet {
  position: relative; width: 100%; max-width: 380px;
  background: var(--bg); color: var(--ink);
  border: none;
  border-radius: var(--radius-sheet) var(--radius-sheet) 0 0;
  padding: 10px 22px calc(28px + env(safe-area-inset-bottom));
  box-shadow: var(--shadow-2xl);
  transform: translateY(100%);
  transition: transform 260ms cubic-bezier(.4, 0, 1, 1);
  will-change: transform;
  touch-action: none;
}
@media (min-width: 480px) {
  .sheet-scrim { align-items: center; }
  .sheet { border-radius: var(--radius-sheet); margin-bottom: 0; }
}
.sheet.in {
  transform: translateY(0);
  transition: transform 420ms cubic-bezier(.16, 1, .3, 1);
}
.sheet .grab { width: 38px; height: 4px; border-radius: 999px; background: var(--border-strong); margin: 0 auto 18px; }
.sheet .sheet-title { text-align: center; margin: 0; }
.sheet .sheet-msg { text-align: center; margin: 8px auto 0; max-width: 42ch; }
.sheet .sheet-field { margin-top: 18px; }
.sheet .sheet-field input,
.sheet .sheet-field select { font-size: 16px; text-align: center; }
.sheet .sheet-field select { text-align-last: center; }
.sheet .sheet-actions { display: grid; gap: 8px; margin-top: 20px; }

/* Feedback glyph — ink stroke on a rose-pale seal. */
.sheet .glyph { width: 84px; height: 84px; margin: 6px auto 2px; display: block; }
.sheet .glyph .seal { transform-origin: center; }
.sheet .glyph .draw {
  fill: none; stroke-width: 7; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 120; stroke-dashoffset: 120;
  transition: stroke-dashoffset .5s var(--ease-sheet) .12s;
}
.sheet.drawn .glyph .draw { stroke-dashoffset: 0; }
.sheet .glyph.ok .seal { fill: var(--rose-pale); }
.sheet .glyph.ok .draw { stroke: var(--ink); }
.sheet .glyph.err .seal { fill: var(--bad-soft); }
.sheet .glyph.err .draw { stroke: var(--bad-ink); }

@media (prefers-reduced-motion: reduce) {
  .sheet, .sheet-scrim, .sheet .glyph .draw { transition: none !important; }
  .sheet { transform: translateY(0); }
}

/* Action-sheet rows stack full width. */
.sheet-actions-list { display: grid; gap: 8px; }
.sheet-actions-list .sheet-action {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  text-align: left;
}
.sheet-action-hint { color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: .02em; }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Guided camera ────────────────────────────────────────────────────────────
   Dark, but the BRAND's dark: the deep warm brown from the design boards, bone
   controls, rose accents. Scoped to `.sheet.cam` so nothing else shifts. */
.sheet.cam {
  inset: 0;
  max-height: 100dvh;
  height: 100dvh;
  max-width: none;
  border: none;
  border-radius: 0;
  padding: 0;
  background: #221610;
  color: #F7ECE5;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.sheet.cam .grab { display: none; }

.cam-wrap { display: flex; flex-direction: column; gap: 10px; padding-bottom: max(12px, env(safe-area-inset-bottom)); }

/* Matches CAPTURE_ASPECT in camera-guides.js — change them together. */
.cam-stage {
  position: relative;
  aspect-ratio: 9 / 16;
  max-height: 72dvh;
  margin: 0 auto;
  width: calc(100% - 28px);
  background: #17100B;
  overflow: hidden;
  border-radius: 18px;
}
.cam-stage video,
.cam-ghost {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;   /* both, identically — this is what makes the ghost line up */
}
.cam-ghost { opacity: var(--ghost-alpha, .35); pointer-events: none; }

.cam-guides { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* Faint enough to ignore, visible over both dark hair and a bright window. */
.cam-guides .g-grid   { stroke: rgba(247, 236, 229, .18); stroke-width: 1; }
.cam-guides .g-centre { stroke: rgba(247, 236, 229, .45); stroke-width: 1.5; }
.cam-guides .g-frame  { stroke: rgba(247, 236, 229, .6); stroke-width: 1.5; stroke-dasharray: 6 5; }
.cam-guides .g-arch   { stroke: rgba(176, 113, 107, .55); stroke-width: 1.5; stroke-dasharray: 3 6; fill: none; }
/* The body outline: where the client goes, not where her hair ends. */
.cam-guides .g-bust   { stroke: rgba(247, 236, 229, .38); stroke-width: 1.5; fill: none; }

/* The two lines that mean something get told apart, in words. A line she has to
   remember the meaning of is a line she ignores. */
.cam-labels { position: absolute; inset: 0; pointer-events: none; }
.cam-label {
  position: absolute; right: 12px;
  transform: translateY(-50%);
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(26, 17, 12, .6);
  color: rgba(247, 236, 229, .88);
  font-size: .5rem; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
  white-space: nowrap;
}

/* Each overlay is its own quiet switch (see the toggles below the stage). */
.cam-stage.no-lines .g-grid, .cam-stage.no-lines .g-centre, .cam-stage.no-lines .g-frame,
.cam-stage.no-lines .cam-labels { display: none; }
.cam-stage.no-arch .g-arch { display: none; }
.cam-stage.no-body .g-bust { display: none; }
.cam-stage.no-ghost .cam-ghost { display: none; }

.cam-caption {
  position: absolute;
  top: 12px; left: 50%;
  transform: translateX(-50%);
  max-width: 88%;
  padding: 6px 13px;
  border-radius: 999px;
  font-size: .8125rem;
  font-weight: 500;
  text-align: center;
  backdrop-filter: blur(6px);
}
/* Named, so "that is the wrong woman" is a glance rather than a deduction. */
.cam-caption.has-ghost { background: rgba(34, 22, 16, .72); color: #F7ECE5; border: 1px solid var(--rose); }
/* An explicit absence — "no ghost" and "the ghost failed" must never look alike. */
.cam-caption.no-ghost  { background: rgba(34, 22, 16, .6); color: rgba(247, 236, 229, .7); border: 1px solid rgba(247, 236, 229, .25); }

.cam-error {
  position: absolute;
  left: 16px; right: 16px; bottom: 16px;
  padding: 13px 16px;
  border-radius: 14px;
  background: rgba(34, 22, 16, .92);
  border: 1px solid var(--rose-deep);
  color: #F7ECE5;
  font-size: .875rem;
}

/* What's drawn on the preview, each its own quiet switch. */
.cam-toggles { display: flex; justify-content: center; gap: 6px; padding: 0 12px; flex-wrap: wrap; }
.cam-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 0 13px;
  border: 1px solid rgba(247, 236, 229, .18); border-radius: 999px;
  background: transparent; color: rgba(247, 236, 229, .45);
  font-family: inherit; font-size: .5625rem; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
  cursor: pointer;
  transition: color .18s, border-color .18s, background .18s;
}
.cam-toggle[aria-pressed="true"] {
  color: #F7ECE5;
  border-color: rgba(247, 236, 229, .32);
  background: rgba(247, 236, 229, .14);
}
.cam-toggle[aria-pressed="true"]::before {
  content: ""; width: 4px; height: 4px; border-radius: 50%; background: var(--rose);
}

.cam-slider { display: flex; align-items: center; gap: 10px; padding: 0 18px; color: rgba(247, 236, 229, .75); }
.cam-slider input { flex: 1; accent-color: #B0716B; height: 28px; }
.cam-side { letter-spacing: .2em; }

.cam-controls { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; padding: 0 12px; }
.cam-controls .cam-side { color: rgba(247, 236, 229, .85); }
.cam-shutter {
  width: 70px; height: 70px;
  border-radius: 50%;
  background: #F7ECE5;
  border: none;
  box-shadow: 0 0 0 3px rgba(247, 236, 229, .22);
  cursor: pointer;
  transition: transform .12s var(--ease-sheet);
}
.cam-shutter:active { transform: scale(.94); }
.cam-hint { text-align: center; color: rgba(247, 236, 229, .6); margin: 0 18px; }
