/* ============================================================
   MD Spa Mentor CRM — Phase 0 clickable prototype
   Light / premium direction (Mangomint-class), NOT the dark teal.
   Fake data only. No backend.
   ============================================================ */

:root{
  --bg:#F4F5F7;
  --surface:#FFFFFF;
  --surface-2:#FAFBFC;
  /* What the pointer is over.
     Twice wrong before this. First it was --surface-2 (#FAFBFC) — 2% off
     white, invisible. Then #EDEFF3, which measured an honest 18/255 step
     against the BACKGROUND and he still could not see it. The measurement
     was asking the wrong question.
     The right question is whether hover differs from everything ELSE on the
     sidebar, and it did not: #EDEFF3 sits ΔL* 5.60 from white while the
     SELECTED row sits ΔL* 5.15 — 0.45 apart, hover marginally the darker of
     the two. Two pills of identical weight, telling you nothing.
     #E1E5EC is ΔL* 9.17 (1.2635:1), and neutral blue-grey against the
     selected teal — so the two can never be confused. */
  --hover:#DCE2EA;
  /* The selected row still has to answer the pointer, or half the menu is a
     dead target. One step darker, and still teal so it never reads as hover. */
  --acc-soft-hover:#D6EBE7;
  --line:#E4E7EB;
  --line-soft:#EFF1F4;
  --ink:#12161A;
  --ink-2:#3F464D;
  --muted:#767E87;
  --faint:#9AA1A9;

  --acc:#0E7C74;
  --acc-dark:#0A5F59;
  --acc-soft:#E6F3F1;
  --acc-line:#BFDEDA;

  --gold:#B08542;
  --gold-soft:#FBF3E6;

  --danger:#C2453D;
  --danger-soft:#FDECEA;
  /* A delete control at rest. Unmistakably red, quiet enough that a
     list of ten of them still reads as a list. */
  --danger-rest:#D08C86;

  --r:10px;
  --r-lg:14px;
  --sh:0 1px 2px rgba(16,24,40,.04), 0 1px 3px rgba(16,24,40,.06);
  --sh-md:0 4px 12px rgba(16,24,40,.07), 0 1px 3px rgba(16,24,40,.05);
  --sh-lg:0 16px 40px rgba(16,24,40,.14), 0 4px 12px rgba(16,24,40,.08);

  --font:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,Roboto,"Helvetica Neue",Arial,sans-serif;

  /* Motion. Built-in CSS easings are too weak; these are the strong variants.
     Color wants ease. Movement wants a strong ease-out. Never ease-in on UI. */
  --ease-out:cubic-bezier(0.23,1,0.32,1);
  --ease-drawer:cubic-bezier(0.32,0.72,0,1);   /* iOS drawer curve (Ionic) */
  --t-color:180ms ease;
  --t-move:200ms var(--ease-out);
  --t-press:160ms var(--ease-out);
}

*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{
  font-family:var(--font);
  background:var(--bg);
  color:var(--ink);
  font-size:14px;
  line-height:1.45;
  -webkit-font-smoothing:antialiased;
}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
:focus-visible{outline:2px solid var(--acc);outline-offset:2px;border-radius:6px}
/* Every tick is the spa's own color. Two places set this and forty inherited
   the browser's blue, a blue tick inside a teal chip (2026-09-14). */
input[type=checkbox],input[type=radio]{accent-color:var(--acc)}
/* Line icons inside buttons sit on the text's center line, never the baseline. */
.btn > svg,.x > svg,.datenav button > svg,.iconbtn > svg{flex:none;display:block}
.av-room{display:inline-grid;place-items:center}

/* Hover is gated on ANY-HOVER, not on the primary pointer.
   Why it matters, and it cost three rounds to find: `(pointer:fine)` asks
   about the PRIMARY input. His laptop has a touchscreen, so Windows can call
   the primary pointer COARSE even with a mouse plugged in — and then this
   whole block switched off. Every hover in the app, dead at once. He reported
   it three times as "the menu hover does not show" while I kept adjusting
   colors that were never being applied.
   `any-hover:hover` asks the question actually meant: can ANY input here
   hover? Touchscreen laptop with a trackpad, yes. A bare iPad in a treatment
   room, no — so the sticky-tap problem this gate was written for is still
   handled. */
/* The menu says what you are ABOUT to pick, not only what you picked.
   A FILL ALONE WAS NOT ENOUGH — reported three times. It now gets a bar of
   its own, the same kind of cue the selected row has, because a bar is
   binary: it is there or it is not, and no screen brightness, warm filter
   or viewing angle can wash it out the way four points of lightness can. */
.nav a:hover{
  background:var(--hover);color:var(--ink);
  box-shadow:inset 3px 0 0 var(--ink-2);
}
/* The selected row answers the pointer too, in its own color. Restating
   --acc-soft here made it a no-op: hovering the row you were already on
   changed nothing at all, and with two menu items that is half of every
   casual test. */
.nav a.on:hover{background:var(--acc-soft-hover);color:var(--acc-dark)}
.side-foot .who:hover{background:var(--hover)}
.btn:hover{background:var(--surface-2);border-color:#D6DAE0}
.btn.pri:hover{background:var(--acc-dark);border-color:var(--acc-dark)}
.btn.gold:hover{filter:brightness(.94)}
.btn.ghost:hover{background:var(--surface-2)}
.datenav button:hover{background:var(--surface-2);color:var(--ink)}
.x:hover{background:var(--surface-2);color:var(--ink)}
.tabs button:hover{color:var(--ink-2)}
.chip:hover{border-color:var(--acc-line);background:var(--acc-soft)}
.line .del:hover{background:var(--danger-soft);color:var(--danger)}
tbody tr:hover{background:var(--surface-2)}
.appt:hover{box-shadow:var(--sh-md);z-index:5}

/* ---------- shell ---------- */
.app{display:grid;grid-template-columns:232px 1fr;height:100vh;overflow:hidden}

.side{
  background:var(--surface);
  border-right:1px solid var(--line);
  display:flex;flex-direction:column;
  padding:18px 14px;
}
.brand{display:flex;align-items:center;gap:10px;padding:4px 8px 20px}
.brand-mark{
  width:30px;height:30px;border-radius:8px;flex:none;
  background:linear-gradient(140deg,var(--acc),var(--acc-dark));
  display:grid;place-items:center;color:#fff;font-weight:650;font-size:13px;letter-spacing:-.02em;
}
.brand-name{font-weight:650;letter-spacing:-.01em;font-size:14.5px;line-height:1.25}
.brand-sub{font-size:11px;color:var(--faint);margin-top:-2px}

.nav{display:flex;flex-direction:column;gap:2px}
.nav-label{font-size:10.5px;font-weight:650;letter-spacing:.7px;text-transform:uppercase;color:var(--faint);padding:16px 10px 6px}
.nav a{
  display:flex;align-items:center;gap:10px;
  padding:8px 10px;border-radius:8px;
  color:var(--ink-2);font-weight:500;font-size:13.5px;
  transition:background var(--t-color),color var(--t-color),transform var(--t-press);
}
.nav a:active{transform:scale(.97)}
.subnav a:active{transform:scale(.98)}
.nav a.on{
  background:var(--acc-soft);color:var(--acc-dark);font-weight:600;
  /* A bar down the left edge. Binary — there or not — so it survives a dim
     screen, a warm filter and an off-axis glance, none of which a 4-point
     lightness difference does. */
  box-shadow:inset 3px 0 0 var(--acc);
}
/* A line icon, not a character. `opacity` is what keeps it quieter than the
   label without a second color to keep in step.

   THE CENTRING HERE IS BELT-AND-BRACES, and it is annotated rather than left
   looking load-bearing: `.nav a` is already `display:flex; align-items:center`
   and the icon is exactly 17x17, so removing `display:grid` or the `svg`
   rule moves nothing -- measured, to a tenth of a pixel, when a sabotage on
   this rule was caught by nothing. It earns its place the day an icon is not
   square, which is the day it would otherwise be found by eye. */
.nav .ic{width:17px;height:17px;flex:none;display:grid;place-items:center;opacity:.85}
.nav .ic svg{display:block}
.nav .badge{margin-left:auto;font-size:10.5px;font-weight:600;background:var(--gold-soft);color:var(--gold);padding:1px 6px;border-radius:20px}

/* ---- Settings, as a drop-down --------------------------------------
   His instruction, 2026-08-30: "let's make the settings a drop down list so
   whenever you click on it the list will drop and you'll see all options.
   Every option will have it's own page." */
.nav a.parent .caret{
  margin-left:auto;font-size:10px;color:var(--faint);
  transition:transform 160ms var(--ease-out);
}
.nav a.parent.open .caret{transform:rotate(180deg)}
/* Held open while a page under it is showing, so you can always see where
   you are without the list collapsing under you. */
.nav a.parent.here{color:var(--ink);font-weight:600}

.subnav{
  display:grid;grid-template-rows:0fr;
  transition:grid-template-rows 180ms var(--ease-out);
}
.subnav.open{grid-template-rows:1fr}
.subnav > div{overflow:hidden}
/* A collapsed list is INERT, not merely flat. Without this the links were
   still in the tab order and still clickable at zero height — a keyboard
   user would tab into a menu that is not on screen. */
.subnav{visibility:hidden}
.subnav.open{visibility:visible}
.subnav a{
  font-size:13px;padding:7px 10px 7px 37px;color:var(--muted);
  position:relative;
}
/* A hairline down the left, so the group reads as one thing. */
.subnav a::before{
  content:"";position:absolute;left:19px;top:0;bottom:0;
  border-left:1px solid var(--line);
}
.subnav a.on{
  background:var(--acc-soft);color:var(--acc-dark);font-weight:600;
  box-shadow:inset 3px 0 0 var(--acc);
}
.subnav a.on::before{border-left-color:var(--acc)}
.subnav a:hover{
  background:var(--hover);color:var(--ink);font-weight:600;
  box-shadow:inset 3px 0 0 var(--ink-2);
}
/* The hairline picks it up too, so the whole row reads as one target. */
.subnav a:hover::before{border-left-color:var(--ink-2)}
.subnav a.on:hover{background:var(--acc-soft-hover);color:var(--acc-dark)}
.subnav a.on:hover::before{border-left-color:var(--acc)}

/* A quiet line saying what this screen can do. It exists because three
   rounds were lost to a hover that was switched off by a media query, with
   no way for him to see that from his side. */

.side-foot{margin-top:auto;padding-top:14px;border-top:1px solid var(--line-soft)}
/* `.side-foot .who`, not a bare `.who`. This is the signed-in person's chip at
   the bottom of the sidebar, and as a bare class it was reaching two other
   places 1,900 lines away that happen to use the same word:

   - the FILE ROWS on a client record, where `align-items:center` on a COLUMN
     flex centers across, so every filename floated in the middle of an empty
     row instead of sitting beside its icon, with 6px of somebody else's
     padding under it;
   - the AUTHOR of a chart addendum, an inline span turned into a padded,
     rounded flex box.

   Fourth collision of this shape in this stylesheet, after `.feebox`, `.step`
   and `.chkfield`. The distance is the trap: you cannot see the other rule
   while you are editing this one. */
.side-foot .who{display:flex;align-items:center;gap:9px;padding:6px 8px;border-radius:8px;transition:background var(--t-color)}
/* It opens your own account, so it has to look like something you can press.
   Declared HERE, beside the rule that already defines this row -- the hover
   lives 125 lines up beside every other hover in the file, and a second
   `.side-foot .who{...}` up there would trip the duplicate-class lint that
   exists because `.feebox`, `.step`, `.chkfield` and `.who` itself have each
   been broken by a same-named rule too far away to see while editing. */
/* THE TEXT PUSHES THE BUTTONS RIGHT, and nothing else does.
   The `margin-left:auto` used to sit inline on the "look as" button -- which
   only OWNER AND MANAGER have. For every other role that button is hidden,
   and the push went with it: the Sign out button sat at x85-122 in a row
   ending at 217, floating in the middle with a gap after it. Measured
   2026-09-16 while wiring this row to open your own account, and the reason
   it mattered suddenly is that clicking the middle of your own name -- where
   somebody aims for "your account" -- landed on SIGN OUT.
   Found by measuring. It reads as slightly odd and never as a rule. */
.side-foot .who-text{flex:1 1 auto;min-width:0}
.side-foot .who{cursor:pointer}
.side-foot .who:active{transform:scale(.99)}
.side-foot .who:focus-visible{outline:2px solid var(--acc);outline-offset:2px}
.avatar{
  width:28px;height:28px;border-radius:50%;flex:none;
  background:var(--acc-soft);color:var(--acc-dark);
  display:grid;place-items:center;font-size:11px;font-weight:650;
}
.who-name{font-size:12.5px;font-weight:600;line-height:1.2}
.who-role{font-size:11px;color:var(--faint)}

/* ---------- main ---------- */
.main{display:flex;flex-direction:column;overflow:hidden}
.top{
  height:58px;flex:none;
  background:var(--surface);
  border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:14px;
  padding:0 22px;
}
.top h1{font-size:16px;font-weight:650;letter-spacing:-.015em;line-height:1.2}
.top .sp{flex:1}
/* The short labels exist only below 900px; above it the full words show. */
.top .short{display:none}
.top .btn{white-space:nowrap}
@media (max-width:900px){
  .top .full,.top #btnCheckout .lbl{display:none}
  .top .short{display:inline}
}

.btn{
  display:inline-flex;align-items:center;gap:7px;
  padding:7px 13px;border-radius:8px;
  font-size:13px;font-weight:600;
  border:1px solid var(--line);background:var(--surface);color:var(--ink-2);
  transition:background var(--t-color),border-color var(--t-color),transform var(--t-press);
}
.btn:active{transform:scale(.97)}
.btn.pri{background:var(--acc);border-color:var(--acc);color:#fff;box-shadow:var(--sh)}
.btn.sm{padding:5px 10px;font-size:12.5px}
.btn.gold{background:var(--gold);border-color:var(--gold);color:#fff}
.btn.ghost{border-color:transparent;background:transparent}

/* A button that removes somebody has to LOOK like a button.
   `ghost` strips the border, so `btn sm ghost danger` painted the Remove
   control as a line of bold text -- nothing to press, and no warning color,
   because no `.btn.danger` rule existed at all. */
.btn.danger{color:var(--danger);border-color:#EBC9C5;background:var(--surface)}
.btn.danger:hover:not(:disabled){background:var(--danger-soft);border-color:var(--danger)}

.body{flex:1;overflow:auto;padding:22px}
.screen{display:none}
.screen.on{display:block}

/* ---------- calendar ---------- */
.cal-bar{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.datenav{display:flex;align-items:center;gap:2px;background:var(--surface);border:1px solid var(--line);border-radius:9px;padding:3px}
.datenav button{width:28px;height:26px;border-radius:6px;color:var(--muted);font-size:14px;display:inline-grid;place-items:center;transition:background var(--t-color),color var(--t-color),transform var(--t-press)}
.datenav button:active{transform:scale(.94)}
.datenav .today{width:auto;padding:0 11px;font-size:12.5px;font-weight:600;color:var(--ink-2)}
/* THE DATE, AND THE COUNT BESIDE IT -- two different jobs.
   His report, 2026-09-11: "on the calendar make the Date Bold and visable."
   He was right, and the reason is that `.calday span` quieted BOTH of them.
   It was written for the count, and `#dayLabel` is a span too -- so the 650 on
   the parent never reached the date, which rendered faint gray at weight 500.
   The date is what tells you which day you are looking at; it leads. */
.calday{font-size:15.5px;letter-spacing:-.015em;line-height:1.25}
.calday #dayLabel{font-size:17px;font-weight:700;color:var(--ink)}
.calday #dayCount{color:var(--faint);font-weight:500;margin-left:8px;font-size:13px}


.cal{
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--sh);overflow:hidden;
  position:relative;
}
/* Stepping a day keeps the grid: the day already drawn dims and a pill names
   the one coming. Both wait 180ms, so a load that takes 40ms -- most of
   them -- changes nothing visible. */
.cal .cal-head,.cal .cal-grid{transition:opacity 120ms ease}
.cal.is-loading .cal-head,.cal.is-loading .cal-grid{opacity:.45;transition-delay:180ms}
.cal-loading{
  position:absolute;left:50%;top:14px;transform:translateX(-50%);z-index:9;
  display:inline-flex;align-items:center;gap:8px;
  background:var(--ink);color:#fff;font-size:12.5px;font-weight:550;
  padding:6px 12px;border-radius:999px;box-shadow:var(--sh-md);
  opacity:0;pointer-events:none;transition:opacity 120ms ease;
}
/* The delay is on the way IN only: a slow load earns the pill after 180ms,
   and the moment the day lands it goes at once. */
.cal.is-loading .cal-loading{opacity:1;transition-delay:180ms}
.cal-loading .spinner{border-color:rgba(255,255,255,.35);border-top-color:#fff}
.cal-head{
  display:grid;grid-template-columns:62px repeat(4,1fr);
  border-bottom:1px solid var(--line);background:var(--surface-2);
}
.cal-head div{
  padding:11px 12px;font-size:12.5px;font-weight:650;color:var(--ink-2);
  border-left:1px solid var(--line-soft);
}
.cal-head div:first-child{border-left:none}
.cal-head .rm-sub{display:block;font-size:10.5px;font-weight:500;color:var(--faint);margin-top:1px}

.cal-grid{display:grid;grid-template-columns:62px repeat(4,1fr);position:relative}
/* The week: seven columns of at least 150px. On a screen too narrow for all
   seven the whole card scrolls sideways, header and body together, so the
   headings stay over their own days. */
.cal.is-week{overflow-x:auto}
/* index.html gives the day's head and grid `min-width:max-content` so a day
   with many rooms scrolls. In the WEEK that sized the header by its headings
   and the body by its cards -- 175px columns over 386px columns -- so the
   headings sat over the wrong days. Seven minmax(150px,1fr) tracks are the
   minimum here, on both, and they scroll together. */
.cal.is-week .cal-head,.cal.is-week .cal-grid{min-width:0}
.times{border-right:1px solid var(--line-soft);position:relative}
.time{position:absolute;left:0;right:0;height:0}
.time span{
  position:absolute;top:-7px;right:9px;
  font-size:10.5px;color:var(--faint);font-weight:600;letter-spacing:.2px;
}
/* WHERE WE ARE NOW. His instruction, 2026-09-08.
   It spans the whole grid, gutter included, so the time in the margin and the
   line agree. `pointer-events:none` because it must never swallow a click
   meant for the card underneath it — the line is information, not a control. */
.nowline{
  position:absolute;left:0;right:0;height:0;z-index:6;pointer-events:none;
  border-top:2px solid var(--danger);
}
.nowline span{
  position:absolute;left:0;top:-9px;
  background:var(--danger);color:#fff;
  font-size:10px;font-weight:600;letter-spacing:.2px;
  padding:2px 6px;border-radius:4px;
}

/* SHE IS LATE. Deliberately a whole window rather than a quiet badge — the
   thing being asked for is that somebody picks up a phone. */
.latewarn{text-align:center;padding:6px 4px 2px}
.latewho{font-size:20px;font-weight:650;letter-spacing:-.02em;color:var(--ink);margin-bottom:6px}
.latewhen{font-size:14px;color:var(--ink-2);line-height:1.6}
.latesay{
  font-size:14px;color:var(--ink-2);line-height:1.6;margin:14px 0 0;
  background:var(--gold-soft);border:1px solid var(--gold);
  border-radius:10px;padding:11px 14px;
}

.col{border-left:1px solid var(--line-soft);position:relative}
/* One box per bookable slot, so a card and the box behind it share an edge. */
.srow{position:absolute;left:0;right:0;border-top:1px solid var(--line-soft)}
.srow.last{border-bottom:1px solid var(--line-soft)}
.srow.alt{background:#FCFCFD}

/* The time range on a card that is too short for its own time line. */
/* On a short card the time prefixes the treatment — it stays quiet so the
   treatment beside it is what the eye lands on. */
.t3{color:var(--faint);font-weight:550;margin-right:7px}
.appt{
  position:absolute;left:5px;right:5px;
  border-radius:8px;padding:7px 9px 7px 11px;
  overflow:hidden;cursor:pointer;
  border:1px solid;
  transition:transform var(--t-press),box-shadow var(--t-color);
}
.appt:active{transform:scale(.985)}
.appt .bar{position:absolute;left:0;top:0;bottom:0;width:3px}
.appt .t{font-size:10.5px;font-weight:600;opacity:.72;letter-spacing:.1px}
/* A long name or treatment WRAPS rather than being cut. A 75-minute card has
   the height for a second line; only the tiny cards below cannot spare it. */
.appt .n{font-size:12.5px;font-weight:650;letter-spacing:-.008em;margin-top:1px;
  white-space:normal;overflow-wrap:anywhere;line-height:1.28}
/* The treatment is what the room is actually DOING — it reads at nearly the
   weight of the client's name, not as a faded caption underneath it. */
.appt .s{font-size:12px;font-weight:600;opacity:1;margin-top:2px;
  white-space:normal;overflow-wrap:anywhere;line-height:1.3}
/* A 15- or 30-minute card is 22-34px tall: one line, so it must ellipsize. */
.appt.xs .n,.appt.sm .n,.appt.xs .s,.appt.sm .s{
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.appt.sm{padding:6px 9px 6px 11px}
.appt.xs{padding:5px 9px 5px 11px}
.appt .t2{font-weight:600;opacity:.66;margin-right:6px;font-size:10.5px}

/* Card color = the TREATMENT TYPE (--tc, straight from Acuity).
   Status is shown as readable text, not as the card's color. In Acuity the
   colored box IS the status, so staff have to memorise a code; here you can
   see the treatment and read the status at the same time. */
/* The treatment color, at real strength. It used to sit under an 86% white
   wash, which left about a seventh of the hue — the cards read as tinted grey.
   At 66% the color is properly present and the darkest treatment in use
   (#211B34) still gives 8.6:1 against the ink, well past the 4.5:1 floor. */
.appt{
  background:
    linear-gradient(0deg, rgba(255,255,255,.66), rgba(255,255,255,.66)),
    var(--tc, #73C1ED);
  border-color: color-mix(in srgb, var(--tc, #73C1ED) 62%, #fff);
  color: var(--ink);
}
.appt .bar{ background: var(--tc, #73C1ED); width:4px }
.appt .t{ color: var(--muted); }
.appt .s{ color: var(--ink) }

/* The top line is a flex row: the label sits at the end and is never allowed
   to shrink, so "Double confirmed" cannot be half-cut on a narrow card.
   Absolutely positioning it (the previous approach) let the card's overflow
   clip it whenever two appointments shared a column. */
.appt .top-line{
  display:flex; align-items:center; gap:6px; min-width:0;
}
.appt .top-line .grow{
  flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* On a lane-split card there is not room for both. The TIME must never
   truncate — "10:00…" is useless — so it never shrinks and the status
   label gives way instead. */
/* The status label ALWAYS renders whole; the time gives way instead.
   It used to be the other way round, which was right when a card read
   "10:00" — now it reads "10:00 – 11:15" and on a crowded day that was
   pushing "Double confirmed" out of view, the exact complaint from before. */
.appt .top-line .t.grow{
  flex:1 1 auto;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.appt .top-line .st-chip{ flex:0 0 auto; overflow:visible; text-overflow:clip }
/* The card never wraps or hides anything now — a busy day widens the column
   and the calendar scrolls sideways instead. */
.appt .st-chip{ flex:none }
.st-chip{
  display:inline-flex; align-items:center; gap:5px;
  font-size:10.5px; font-weight:600; letter-spacing:-.004em; text-transform:none;
  padding:2px 8px 2px 7px; border-radius:6px; white-space:nowrap;
  border:none; line-height:1.45;
}
/* The dot is the color; the text stays quiet and legible. */
.st-chip::before{
  content:''; width:5px; height:5px; border-radius:50%;
  background:currentColor; flex:none; opacity:.9;
}

/* Tints pulled well back so several chips on one card sit quietly together;
   the text color carries enough contrast to read at 10.5px. */
.sc-pending  {background:#F2F4F6; color:#5D666E}
.sc-confirmed{background:#FBF2E0; color:#87611A}
.sc-double   {background:#FAEAF1; color:#96345F}
.sc-arrived  {background:#E7EFFB; color:#28548F}
.sc-sold     {background:#E1F1E8; color:#1A6244}
.sc-noshow   {background:#EDEFF2; color:#565D65}
.sc-canceled {background:#EFEAF9; color:#5A3E96}

/* The note preview. Clamped to however many lines the card can actually
   spare — the line count is set inline, from the card's own height. This is
   the deliberate exception to "nothing is ever cut off": he asked for a
   glimpse, and the whole note is one click away in the panel. */
.appt .nt{
  margin-top:5px;padding-top:5px;
  border-top:1px solid rgba(0,0,0,.07);
  font-size:11px;line-height:1.36;color:var(--ink-2);opacity:.9;
  display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;
  overflow-wrap:anywhere;
}

/* A canceled appointment stays visible but clearly out of play. */
/* Canceled cards have to recede further now the live ones carry real color. */
.appt.is-canceled{ opacity:.5; filter:saturate(.55) }
.appt.is-canceled .n{ text-decoration:line-through }

/* The builder's preview drawn from boxes not yet saved: a warning, gold. */
.fb-unsaved{margin:0 0 12px;padding:10px 14px;border-radius:10px;background:var(--gold-soft);color:var(--gold);font-size:13px;border:1px solid rgba(138,90,11,.25)}
.fb-unsaved b{font-weight:650}

/* The line above a settings title, saying what KIND of setting the page holds.
   Monospace and small capitals so it reads as a signpost, not a heading.
   His decision, 2026-09-13. */
.page-h .eyebrow{
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:10.5px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  color:var(--acc-dark);margin:0 0 6px;
}

/* ---- WHAT A CARD SAYS WITHOUT BEING OPENED -----------------------------
   His decision, 2026-09-13, from the competitor read. Three things, all of
   them shapes rather than colors, so color keeps meaning new versus returning
   and nothing else. */
/* Who is treating: two letters in a soft circle, beside the name. */
.appt .who-line{display:flex;align-items:flex-start;gap:6px;min-width:0}
.appt .who-line .n{flex:1;min-width:0}
.av{
  display:inline-grid;place-items:center;flex:none;
  width:18px;height:18px;border-radius:50%;margin-top:2px;
  background:var(--acc-soft);color:var(--acc-dark);
  font-size:9.5px;font-weight:600;letter-spacing:.02em;line-height:1;
}
/* The add-ons, as chips under the treatment. A done one recedes. */
.appt .aos{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}
.appt .ao{
  font-size:10.5px;font-weight:600;line-height:1.5;padding:0 7px;border-radius:999px;
  background:rgba(255,255,255,.55);border:1px solid rgba(0,0,0,.08);color:var(--ink-2);
}
.appt .ao.done{opacity:.55;text-decoration:line-through}
/* The corner marks. The card reserves the corner with padding so the note
   preview -- which measures the room it has -- is clamped above them rather
   than run underneath. */
.appt.has-marks{padding-bottom:24px}
.appt .marks{
  position:absolute;right:8px;bottom:6px;display:flex;gap:6px;
  color:var(--ink-2);opacity:.72;pointer-events:none;
}
.appt .marks span{display:inline-flex}
/* Who is in a room today, in its heading. */
.cal-head .rm-name{display:flex;align-items:center;gap:8px}
.cal-head .rm-who{display:inline-flex;gap:4px}
.cal-head .rm-who .av{margin-top:0}

/* New-client marker. Same family as the status chip: sentence case, readable
   size, soft tint, no border. See DESIGN.md — badges are quiet, not shouty. */
.appt .flag{
  display:inline-block;font-size:10px;font-weight:600;
  letter-spacing:-.003em;padding:1px 6px;border-radius:5px;
  background:var(--acc-soft);color:var(--acc-dark);margin-left:6px;vertical-align:1px;
}

/* ---------- panel ---------- */
.scrim{position:fixed;inset:0;background:rgba(16,22,28,.32);opacity:0;pointer-events:none;transition:opacity 180ms ease;z-index:40}
.scrim.on{opacity:1;pointer-events:auto}
.panel{
  position:fixed;top:0;right:0;bottom:0;width:412px;max-width:92vw;
  background:var(--surface);border-left:1px solid var(--line);
  box-shadow:var(--sh-lg);
  transform:translateX(100%);
  /* Exit faster than enter: slow where the user decides, fast where the system responds. */
  transition:transform 200ms var(--ease-drawer);
  will-change:transform;
  z-index:41;display:flex;flex-direction:column;
}
.panel.on{transform:none;transition:transform 320ms var(--ease-drawer)}
.panel-top{padding:16px 20px;border-bottom:1px solid var(--line);display:flex;align-items:flex-start;gap:10px}
.panel-top h3{font-size:16px;font-weight:650;letter-spacing:-.015em;line-height:1.2}
.panel-top .sub{font-size:12.5px;color:var(--muted);margin-top:2px}
.panel-body{flex:1;overflow:auto;padding:18px 20px}
.panel-foot{padding:14px 20px;border-top:1px solid var(--line);display:flex;gap:8px;background:var(--surface-2)}
.x{margin-left:auto;width:28px;height:28px;border-radius:7px;color:var(--faint);font-size:17px;line-height:1;display:inline-grid;place-items:center;transition:background var(--t-color),color var(--t-color),transform var(--t-press)}
.x:active{transform:scale(.92)}

.kv{display:grid;grid-template-columns:98px 1fr;gap:9px 12px;font-size:13px}
.kv dt{color:var(--muted);font-weight:500}
.kv dd{font-weight:550;color:var(--ink-2)}

.sect{margin-top:20px}
.sect-h{
  font-size:10.5px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);margin-bottom:9px;display:flex;align-items:center;gap:8px;
}
.sect-h::after{content:"";flex:1;height:1px;background:var(--line-soft)}

.note{
  background:var(--surface-2);border:1px solid var(--line-soft);
  border-radius:9px;padding:10px 12px;font-size:12.5px;color:var(--ink-2);
  line-height:1.55;margin-bottom:7px;
}
.note .meta{font-size:10.5px;color:var(--faint);font-weight:600;margin-bottom:3px;letter-spacing:.2px}
.note.client{background:var(--gold-soft);border-color:#F0E2C8}

.alert{
  display:flex;gap:9px;align-items:flex-start;
  border-radius:9px;padding:10px 12px;font-size:12.5px;line-height:1.5;
  border:1px solid;margin-bottom:8px;
}
.alert b{font-weight:650}
.alert.warn{background:var(--gold-soft);border-color:#EEDDBE;color:#6B4E14}
.alert.info{background:var(--acc-soft);border-color:var(--acc-line);color:var(--acc-dark)}
.alert.bad{background:var(--danger-soft);border-color:#F3CFCB;color:#8E2B24}
.alert .i{font-size:14px;line-height:1.2;flex:none}

/* `.pill[hidden]`: the same trap as `.staleband` -- a class that sets
   display beats the browser's own [hidden]. #dataNote sat in the top bar as
   an empty 20px grey mark on every screen. Found in the 2026-09-13 scan. */
.pill[hidden]{display:none}
.avatar[hidden]{display:none}
.pill{
  display:inline-flex;align-items:center;gap:5px;
  font-size:11px;font-weight:600;letter-spacing:-.002em;
  padding:3px 10px;border-radius:20px;border:none;
}
.pill.teal{background:var(--acc-soft);color:var(--acc-dark)}
.pill.gold{background:var(--gold-soft);color:#8A6414}
.pill.grey{background:var(--surface-2);color:var(--muted)}
.pill.green{background:#E1F1E8;color:#1A6244}
.pill.red{background:var(--danger-soft);color:#8E2B24}
/* A pill with no tint still needs an edge, or it floats. */
.pill:not([class*=" "]){background:var(--surface-2);color:var(--muted)}

.stat-row{display:flex;gap:8px;flex-wrap:wrap}

/* ---------- client record ---------- */
/* ---- CLIENTS: THE LIST AND THE RECORD SIDE BY SIDE ----------------------
   His choice, 2026-09-13, from two mockups. Opened FROM the list, the record
   sits beside it and the list stays: moving between clients is one tap with
   no going back. Opened from anywhere else (the calendar, the till) it is the
   full page it always was. Below 1100px there is no room for both and the
   record has the page to itself, crumb and all. */
.body.split{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start}
/* The "looking as" banner is a child of .body too. In the split it became a
   THIRD column and squeezed the record to a sliver -- his report, 2026-09-15,
   from Look as Nurse. Anything in the body that is not a screen takes the
   whole first row. */
.body.split > :not(.screen){flex:0 0 100%}
/* At rest the list has who is in today beside it (2026-09-14 scan: the right
   60% of the screen was empty until somebody was picked). In the split the
   record takes that place. */
.cli-rest{display:grid;grid-template-columns:minmax(0,640px) minmax(0,1fr);gap:16px;align-items:start}
.body.split .cli-rest{display:block}
.body.split #cToday{display:none}
.body.split #sc-clients.on{flex:0 0 340px;position:sticky;top:0}
.body.split #sc-clients .card{max-width:none;margin-bottom:0}
.body.split #cResults{max-height:calc(100vh - 230px);overflow:auto}
.body.split #sc-client.on{flex:1;min-width:0}
.body.split #sc-client .crumb{display:none}
.res.cur{background:var(--acc-soft);box-shadow:inset 3px 0 0 var(--acc)}
@media (max-width:1099px){
  .cli-rest{grid-template-columns:1fr}
  .body.split{display:block}
  .body.split #sc-clients.on{display:none}
  .body.split #sc-client .crumb{display:block}
}
/* Whose chart: the client's name leads the sub-line under the treatment. */
.chart-top .ch-who{color:var(--ink);font-weight:650}
.crumb{font-size:12.5px;color:var(--muted);margin-bottom:12px}
/* The record's history: a quiet link in the meta line, and a plain table. */
.cli-hist{color:var(--acc-dark);font-weight:600}
.hist td{padding:9px 10px;border-bottom:1px solid var(--line-soft);vertical-align:top;font-size:13.5px}
.hist tr:last-child td{border-bottom:none}
.hist-when{white-space:nowrap;color:var(--muted)}
.hist-who{white-space:nowrap;font-weight:600}
.hist s{color:var(--muted)}
.crumb b{color:var(--ink-2);font-weight:600}

.cli-head{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--sh);padding:20px 22px;margin-bottom:16px;
  display:flex;gap:18px;align-items:flex-start;
}
/* WHAT A PROVIDER MUST SEE BEFORE TREATING HER.
   His instruction, 2026-09-08 — the marked answers belong on the profile, not
   three cards down behind a scroll. Loud enough to stop somebody, quiet enough
   that it is not shouting: a soft danger ground and one dot, the same way every
   other badge in this app carries its hue. */
.cli-alert{
  display:flex;gap:12px;align-items:flex-start;
  background:var(--danger-soft);border:1px solid var(--danger);
  border-radius:var(--r-lg);padding:13px 16px;margin-bottom:16px;
}
.cli-alert .dot{
  flex:none;width:9px;height:9px;border-radius:50%;background:var(--danger);margin-top:6px;
}
.cli-alert b{font-size:13px;font-weight:650;color:var(--ink);display:block}
.cli-alert span{font-size:13px;color:var(--ink-2);line-height:1.6}
/* No questionnaire at all is a DIFFERENT fact from nothing flagged, and it is
   a gap in the record rather than a danger — so it is gold, not red. */
.cli-alert.none{background:var(--gold-soft);border-color:var(--gold)}
.cli-alert.none .dot{background:var(--gold)}
/* A SALE SHE HAS NOT SIGNED FOR is a JOB, not an emergency. Nothing is wrong
   -- the sale is good, the money is in -- so it is gold like the missing
   questionnaire above rather than red like a pacemaker. A job painted as an
   emergency is one people learn to scroll past, which is the same reason a
   badge is never shouty. Added 2026-09-09 with the phone sale. */
.cli-alert.owed{background:var(--gold-soft);border-color:var(--gold);align-items:center}
.cli-alert.owed .dot{background:var(--gold);margin-top:0}
.cli-alert.owed .btn{flex:none;margin-left:auto}
.cli-flags{display:flex;flex-wrap:wrap;gap:7px;margin-top:7px}
.cli-flag{
  display:inline-flex;align-items:baseline;gap:6px;
  background:var(--surface);border:1px solid var(--danger);border-radius:99px;
  padding:5px 12px;font-size:12.5px;font-weight:600;color:var(--danger);
}
/* What she actually wrote, beside the warning rather than hidden behind it —
   "Has a pacemaker" and "Has a pacemaker, fitted in 2019" are not the same
   amount of information to somebody deciding whether to treat. */
.cli-flag i{font-style:normal;font-weight:500;color:var(--ink-2)}

/* WHAT SHE HAS TOLD US, as opposed to what should stop you.
   His instruction, 2026-09-08: a confirmed "not breastfeeding" belongs on the
   profile too. It is deliberately NOT red — painting reassurance the same as a
   warning makes the warning ordinary, and a nurse who sees six red chips every
   time stops reading any of them. */
.cli-known{
  display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:12px 16px;margin-bottom:16px;
}
.cli-known b{font-size:12px;font-weight:650;letter-spacing:.05em;text-transform:uppercase;color:var(--faint);flex:none}
.cli-known-chip{
  display:inline-flex;align-items:center;
  background:var(--surface-2);border:1px solid var(--line);border-radius:99px;
  padding:5px 12px;font-size:12.5px;font-weight:600;color:var(--ink-2);
}

.cli-av{
  width:56px;height:56px;border-radius:50%;flex:none;
  background:linear-gradient(140deg,var(--acc-soft),#D5EAE7);
  color:var(--acc-dark);display:grid;place-items:center;
  font-size:19px;font-weight:650;letter-spacing:-.02em;
}
/* Typography AND layout in one place. These were 1,200 lines apart, which is
   how a class ends up quietly overriding itself -- see the collision lint in
   crm_pointer_check. */
.cli-name{
  font-size:21px;font-weight:680;letter-spacing:-.022em;line-height:1.3;
  display:flex;align-items:center;gap:8px;
}
.cli-meta{font-size:12.5px;color:var(--muted);margin-top:3px}
.cli-meta b{color:var(--ink-2);font-weight:600}
.cli-actions{margin-left:auto;display:flex;gap:8px}

.tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin-bottom:18px}
.tabs button{
  padding:9px 14px;font-size:13.5px;font-weight:550;color:var(--muted);
  border-bottom:2px solid transparent;margin-bottom:-1px;
  transition:color var(--t-color),border-color var(--t-color);
}
.tabs button.on{color:var(--acc-dark);border-bottom-color:var(--acc);font-weight:650}

.tabview{display:none}
.tabview.on{display:block}

.cols{display:grid;grid-template-columns:1fr 336px;gap:16px;align-items:start}
@media(max-width:1100px){.cols{grid-template-columns:1fr}}

.card{
  background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--sh);margin-bottom:16px;
}
.card-h{
  padding:13px 18px;border-bottom:1px solid var(--line-soft);
  display:flex;align-items:center;gap:10px;
}
.card-h h3{font-size:13.5px;font-weight:650;letter-spacing:-.01em;line-height:1.3}
.card-h .sp{flex:1}
.card-b{padding:16px 18px}
/* A table wider than its card -- Treatments on an iPad held upright -- used
   to run under the page edge with nothing to scroll. Nothing is ever cut
   off: the card keeps its width and the table scrolls inside it. */
.card-b.tight,
.card-b:has(> table){overflow-x:auto}
.card-b.tight{padding:0}
/* ...but bare text in one still needs room, or it touches the card's edge. */
.card-b.tight > p,
.card-b.tight > .hint,
.card-b.tight > .empty{padding:15px 18px 17px;margin:0}

table{width:100%;border-collapse:collapse;font-size:13px}
th{
  text-align:left;font-size:10.5px;font-weight:650;letter-spacing:.6px;
  text-transform:uppercase;color:var(--faint);
  padding:10px 18px;border-bottom:1px solid var(--line-soft);
}
td{padding:11px 18px;border-bottom:1px solid var(--line-soft);color:var(--ink-2)}
tr:last-child td{border-bottom:none}
tbody tr{transition:background var(--t-color)}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
td b{font-weight:650;color:var(--ink)}

.pkg{border:1px solid var(--line);border-radius:11px;padding:16px 18px 15px;margin-bottom:11px;background:var(--surface-2)}
.pkg-top{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.pkg-name{font-size:13.5px;font-weight:650}
.pkg-left{margin-left:auto;font-size:12.5px;color:var(--muted);font-weight:600}
.pkg-left b{color:var(--acc-dark);font-size:15px}
.dots{display:flex;gap:5px}
.dots i{width:26px;height:7px;border-radius:20px;background:var(--line);display:block}
.dots i.used{background:var(--acc)}
.pkg-foot{display:flex;gap:8px;margin-top:11px;padding-top:11px;border-top:1px solid var(--line)}

/* ---------- checkout ---------- */
.co-cols{display:grid;grid-template-columns:1fr 340px;gap:16px;align-items:start}
@media(max-width:1180px){.co-cols{grid-template-columns:1fr}}

/* The numbered-step layout that used to live here is GONE. Nothing has
   rendered `.step-n` or `.co-cols` since the checkout became an overlay, but
   `.step{display:flex}` stayed behind and kept applying to the chart's
   before/after stepper 400 lines below -- which never sets `display`, so it
   was laying its label and its description out side by side instead of
   stacked.

   Found 2026-09-07 while auditing class collisions, after he spotted the same
   shape of bug on the Settings fee box: "why this looks yellow?" A class name
   declared twice, far apart, is the trap. */

.line{
  display:grid;grid-template-columns:1fr 62px 92px 92px 30px;
  gap:10px;align-items:center;padding:10px 18px;
  border-bottom:1px solid var(--line-soft);font-size:13px;
}
.line:last-of-type{border-bottom:none}
.line-name{font-weight:600;color:var(--ink)}
.line-sub{font-size:11px;color:var(--faint);font-weight:500}
.line input{
  width:100%;padding:6px 9px;border:1px solid var(--line);border-radius:7px;
  background:var(--surface);text-align:right;font-variant-numeric:tabular-nums;font-size:13px;
}
.line input:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft)}
.line .del{color:var(--faint);font-size:15px;width:26px;height:26px;border-radius:6px;transition:background var(--t-color),color var(--t-color),transform var(--t-press)}
.line .del:active{transform:scale(.9)}
.line-h{
  display:grid;grid-template-columns:1fr 62px 92px 92px 30px;gap:10px;
  padding:9px 18px;border-bottom:1px solid var(--line-soft);
  font-size:10.5px;font-weight:650;letter-spacing:.6px;text-transform:uppercase;color:var(--faint);
}
.line-h span:not(:first-child){text-align:right}

.field{margin-bottom:12px}
.field label{display:block;font-size:11.5px;font-weight:650;color:var(--muted);margin-bottom:5px;letter-spacing:.2px}
.field input,.field select{
  width:100%;padding:8px 11px;border:1px solid var(--line);border-radius:8px;
  background:var(--surface);font-size:13.5px;
}
.field input:focus,.field select:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft)}
/* A CHECKBOX AND ITS EXPLANATION, without going near `.field`.
   `.field label{display:block}` and `.field input{width:100%}` both beat
   `.chk` on specificity, so a checkbox dropped inside a `.field` loses its
   row and stretches to the full width of the window. It looked broken, and it
   was — twice, in the same window. This is the wrapper to use instead.
   `browser-tests/crm_pointer_check.mjs` fails on any stretched checkbox. */
.chkfield{margin-bottom:12px}
.chkfield .hint{margin-top:2px}

/* AND THE BOX ITSELF IS PROTECTED, whatever it is sitting inside.
   ---------------------------------------------------------------
   His report, 2026-09-10: "the tax checkbox looks terrible. Again same thing
   that always happens." He was right, and it was the THIRD time.
   The first two fixes both named the guilty container — `.field input
   {width:100%}` — and added `.chkfield` to avoid it. That protects against
   ONE class. The tax card sits in `.setup-b`, which has its own
   `input{width:100%}`, so the box rendered 994 PIXELS WIDE and 13 tall.
   Naming culprits does not scale: the next container will do it again.
   So this defends the CHECKBOX, not the container. `.chk input[type=checkbox]`
   is (0,2,1) and beats any `.something input` at (0,1,1), so it wins wherever
   the box is dropped — today and in a card nobody has written yet.
   `crm_pointer_check` now MEASURES every checkbox on every screen rather than
   reading the source for one class name, because a source lint can only ever
   know about the container somebody already thought of. */
.chk input[type=checkbox], .chk input[type=radio],
.chkfield input[type=checkbox], .chkfield input[type=radio],
.qpcheck input[type=checkbox], .qpcheck input[type=radio]{
  width:auto; min-width:0; flex:none;
  padding:0; margin:0; border-radius:3px;
  box-shadow:none; background:none;
}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.grid3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px}
/* The spelled-out date under the booking's Date box, full width under the
   row -- inside the 200px column "2026" sat alone on a second line. */
.bk-when{margin:-8px 0 12px}

.split-row{
  display:grid;grid-template-columns:1fr 116px 120px;gap:11px;align-items:center;
  padding:11px 0;border-bottom:1px solid var(--line-soft);
}
.split-row:last-child{border-bottom:none}
.split-who{display:flex;align-items:center;gap:9px}
.split-comm{font-size:12.5px;font-weight:650;color:var(--acc-dark);text-align:right;font-variant-numeric:tabular-nums}
.split-comm span{display:block;font-size:10.5px;color:var(--faint);font-weight:550}

.chips{display:flex;gap:6px;flex-wrap:wrap}
.chip{
  padding:5px 11px;border:1px solid var(--line);border-radius:20px;
  font-size:12.5px;font-weight:550;color:var(--ink-2);background:var(--surface);
  transition:background var(--t-color),border-color var(--t-color),color var(--t-color),transform var(--t-press);
}
.chip:active{transform:scale(.96)}
.chip.on{background:var(--acc);border-color:var(--acc);color:#fff;font-weight:600}

.sum{position:sticky;top:0}
.sum-row{display:flex;justify-content:space-between;align-items:baseline;padding:7px 0;font-size:13px}
.sum-row .l{color:var(--muted);font-weight:500}
.sum-row .v{font-weight:600;font-variant-numeric:tabular-nums}
.sum-row.big{border-top:1px solid var(--line);margin-top:7px;padding-top:12px}
.sum-row.big .l{color:var(--ink);font-weight:650;font-size:14px}
.sum-row.big .v{font-size:20px;font-weight:700;letter-spacing:-.022em;line-height:1.15}
.sum-row.due .v{color:var(--danger)}
.sum-row.paid .v{color:#12603F}

.mini{
  background:var(--acc-soft);border:1px solid var(--acc-line);border-radius:10px;
  padding:12px 14px;margin-top:14px;
}
.mini h4{font-size:11px;font-weight:650;letter-spacing:.6px;text-transform:uppercase;color:var(--acc-dark);margin-bottom:8px}
.mini .r{display:flex;justify-content:space-between;font-size:12.5px;padding:3px 0;color:var(--acc-dark)}
.mini .r b{font-weight:700;font-variant-numeric:tabular-nums}

.hint{font-size:11.5px;color:var(--faint);line-height:1.5;margin-top:8px}
.hint b{color:var(--muted);font-weight:650}

/* ---- THE EXPLANATION BEHIND A "!" -------------------------------------
   His instruction, 2026-09-13, pointing at the paragraph under the team list:
   "instead of always showing that explanation you can hover or click the !
   mark near the title of the section to read the explanation."

   A paragraph you have read once is furniture by the second day. It is still
   worth having -- these sentences say why the spa works the way it does --
   but it belongs beside the title, not across the card.

   THE BUBBLE IS ON THE BODY, at position:fixed. A card, a scrolling modal
   body and a table wrapper all clip an absolutely positioned child, and
   NOTHING IS EVER CUT OFF. Fixed escapes every one of them by construction.
   ----------------------------------------------------------------------- */
.whyb{
  display:inline-flex;align-items:center;justify-content:center;
  width:19px;height:19px;padding:0;flex:none;
  border:0;background:transparent;border-radius:50%;
  color:var(--faint);cursor:pointer;vertical-align:middle;
  transition:color .12s ease,background-color .12s ease,transform .08s ease;
}
/* `button.whyb`, not `.whyb`. The stylesheet's own safety net further down --
   `button:hover:not(:disabled){color:var(--ink)}` -- is (0,2,1) and beat a bare
   `.whyb:hover` at (0,2,0), so the teal was quietly repainted near-black.
   Matching it at (0,2,1) was not enough either: equal specificity, and the net
   is written further DOWN the file. Hence `:not(:disabled)` here too, at
   (0,3,1). Same trap as `.feebox` and `.chkfield`; visible only by measuring. */
button.whyb:hover:not(:disabled),
button.whyb[aria-expanded="true"]{color:var(--acc);background:var(--acc-soft)}
.whyb:focus-visible{outline:2px solid var(--acc);outline-offset:1px}
.whyb:active{transform:scale(.92)}

.whyp{
  position:fixed;top:0;left:0;z-index:60;max-width:310px;
  padding:10px 13px;border-radius:9px;
  background:rgba(18,22,26,.97);color:#EDF0F3;
  font-size:11.5px;line-height:1.55;
  box-shadow:0 10px 28px rgba(16,22,28,.26);
  /* A tooltip the pointer can enter is a tooltip that flickers. It is also
     how a tap on the bubble itself closes it, which is what a finger expects. */
  pointer-events:none;
  opacity:0;visibility:hidden;transform:translateY(-3px);
  transition:opacity .13s ease,transform .13s ease,visibility .13s;
}
.whyp.on{opacity:1;visibility:visible;transform:none}
.whyp b{color:#fff;font-weight:650}

.empty{padding:40px 20px;text-align:center;color:var(--faint)}
.empty .big{font-size:26px;margin-bottom:8px;opacity:.5}
.empty p{font-size:13px}


/* ============================================================
   Overrides — must stay LAST in the stylesheet.
   @media adds no specificity, so a rule placed earlier loses to an
   equally-specific rule later in the file. This block was originally
   mid-sheet and silently did nothing.
   ============================================================ */

/* Reduced motion means fewer and gentler animations, not zero. Movement goes;
   color and opacity stay, because killing those makes buttons feel broken. */
@media (prefers-reduced-motion:reduce){
  .btn:active,.nav a:active,.chip:active,.appt:active,
  .datenav button:active,.x:active,.line .del:active{transform:none}
  .panel{opacity:0;transform:none;transition:opacity 200ms ease}
  .panel.on{opacity:1;transform:none;transition:opacity 200ms ease}
}

/* Higher contrast: near-solid surfaces with a defined border. */
@media (prefers-contrast:more){
  :root{--line:#9AA1A9;--muted:#3F464D;--faint:#5A6169}
  .card,.cal,.panel{border-color:var(--line)}
}

/* ---- UNGATED MENU HOVER -------------------------------------------------
   Reported not working FOUR times. Each time I changed the color inside
   `@media (…){ }` and each time my own check passed, because my browser
   answers that query differently from his.

   The gate is the bug, whatever it currently reads. A gate can only ever fail
   one way — by switching the hover OFF — and that is the failure he keeps
   seeing. So the menu hover now sits at the end of the file with nothing in
   front of it: it applies on every browser, in every webview, at every zoom,
   and it wins on order against everything above.

   The sticky-tap problem the gate was written for is handled at the bottom
   instead, by UNDOING the hover only where the browser says nothing at all
   can hover — both conditions, so a desktop can never fall into it.
   Default working, exception narrow. That way round it fails safe. */
.nav a:hover{
  background:var(--hover);
  color:var(--ink);
  box-shadow:inset 3px 0 0 var(--ink-2);
}
.nav a.on:hover{
  background:var(--acc-soft-hover);
  color:var(--acc-dark);
  box-shadow:inset 3px 0 0 var(--acc);
}
.subnav a:hover{
  background:var(--hover);
  color:var(--ink);
  font-weight:600;
  box-shadow:inset 3px 0 0 var(--ink-2);
}
.subnav a:hover::before{border-left-color:var(--ink-2)}
.subnav a.on:hover{
  background:var(--acc-soft-hover);
  color:var(--acc-dark);
  box-shadow:inset 3px 0 0 var(--acc);
}
.subnav a.on:hover::before{border-left-color:var(--acc)}

/* A finger has no hover, so on a device where NOTHING can hover the tint
   would stick after a tap and read as "this is selected" when it is not.
   Both conditions must hold, which no laptop reports. */
@media (hover:none) and (any-hover:none){
  .nav a:hover,.subnav a:hover{
    background:transparent;color:var(--ink-2);box-shadow:none;
  }
  .nav a.on:hover,.subnav a.on:hover{
    background:var(--acc-soft);color:var(--acc-dark);
    box-shadow:inset 3px 0 0 var(--acc);
  }
  .subnav a:hover::before{border-left-color:var(--line)}
}

/* ---- EVERY CONTROL ANSWERS THE POINTER ---------------------------------
   His instruction, 2026-08-30: "I want it to work on every button on the
   crm."

   A sweep of all seven screens found 188 clickable things, of which 37 gave
   no answer at all: every tick box, every text field, and the three "back"
   links in the breadcrumb. They were not gated — they simply had no hover
   rule ever written for them.

   Each cue below is the FOCUS cue one step quieter, so hover and focus read
   as the same family rather than two unrelated effects, and nothing here is
   behind a media query. */

/* Text fields, drop-downs and text areas: the border warms up.
   Written as an EXCLUSION, not a list of types. The list version missed 36
   inputs that carry no `type` attribute at all — `input[type=text]` cannot
   match an absent attribute — and every one of the 19 color pickers. Naming
   the few kinds that must be left out is a shorter list and cannot silently
   miss a kind of field added later. */
input:hover:not(:disabled):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=button]):not([type=submit]):not([type=file]),
select:hover:not(:disabled),
textarea:hover:not(:disabled){
  border-color:var(--acc-line);
}

/* A native tick box cannot be recolored reliably across browsers, so it
   gets the soft ring focus uses instead of a fill. */
input[type=checkbox]:hover:not(:disabled),
input[type=radio]:hover:not(:disabled){
  box-shadow:0 0 0 3px var(--acc-soft);
}
input[type=checkbox]:hover:not(:disabled){border-radius:4px}
input[type=radio]:hover:not(:disabled){border-radius:50%}

/* The label is the bigger target — most people aim at the words, not the
   12px square — so hovering it lights the box as well as the text. */
label.chk:hover,label.gfitem:hover{color:var(--ink)}
label.chk:hover input:not(:disabled),
label.gfitem:hover input:not(:disabled){box-shadow:0 0 0 3px var(--acc-soft)}

/* The "back" link in a breadcrumb. It is the way out of the chart, the form
   builder and a treatment's set-up screen, and it looked like plain text. */
.crumb a:hover{
  color:var(--acc-dark);
  text-decoration:underline;
  text-underline-offset:3px;
}

/* A FLOOR under every button in the app, present and future.
   Deliberately low specificity — one class, one element — so anything with
   its own hover (.btn, .chip, .nav a, .datenav button …) overrides it
   without needing !important. It exists so the NEXT button someone adds
   cannot arrive silent: the worst case is now a text-color shift, not
   nothing at all. That is how `.today` slipped through — it is the date-nav
   button AND the small tab button in Settings, and outside `.datenav` no
   rule had ever covered it. */
button:hover:not(:disabled),
a[href]:hover{
  color:var(--ink);
}

/* `.today` in full: it sits outside `.datenav` when Settings uses it as a
   tab, so it needs its own cue rather than borrowing the date-nav's. */
.today{border-radius:6px}
.today:hover:not(:disabled){background:var(--surface-2);color:var(--ink)}

/* The license letters beside a clinical title. Quiet — the words are what
   you read, the letters are what you check. */
.tt-short{
  font-size:10.5px;font-weight:600;color:var(--muted);
  background:var(--surface-2);border-radius:4px;padding:1px 4px;margin-left:3px;
}
.pick-chip.on .tt-short{background:#fff;color:var(--acc-dark)}

/* ---- ONE COLUMN FOR SETTINGS -------------------------------------------
   His instruction, 2026-08-30: "organize the settings on each page, it looks
   weird but some stuff on the middle and some on the sides."

   The cause was that every card stretched to the full window. On a 1600px
   screen the Team table ran to 1324px wide, so a name sat at the far left and
   its two buttons at the far right with 300px of nothing between them, while
   the page heading above wrapped at 70 characters. Three different left
   edges and two different right edges on one page.

   Everything on a Settings page now shares one column: the heading, the
   cards, and the two-column grids inside them. Cards line up, and the row
   actions sit near what they act on instead of being flung to the edge. */
#sc-settings, #sc-setup{max-width:1140px}

/* A row's actions belong beside its content, not against the window edge. */
table.team td.num, table.tylist td.num{width:1%;white-space:nowrap}

/* ---- LISTS OF SHORT THINGS, ACROSS INSTEAD OF DOWN ----------------------
   Statuses, flags and rooms are a name and one small control. Down a single
   1140px column each of those became a page-wide row with its control
   marooned at the far right — the other half of what he meant by "some stuff
   on the middle and some on the sides".

   They flow across now, as many per line as fit, so the control sits beside
   the name it belongs to. The explanatory line underneath still spans the
   whole card. */
.lgrid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(290px,1fr));
  gap:2px 18px;
  align-content:start;
}
.lgrid > .hint,.lgrid > p{grid-column:1 / -1;margin-top:8px}
/* The figures on Reports are short -- a number and a label -- so they fit two
   across on an iPad held upright; at the list minimum they went one per row
   and the page ran to four screens. */
#sc-reports .card-b > .lgrid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}

/* The control at the end of every row is given one width, so the name fields
   above and below each other end at the same place. Without this the pill
   sets the width — "white" is narrower than "purple" — and three columns of
   inputs each stopped somewhere different, which reads as sloppy even though
   every row is correct. */
.lgrid .lrow > .pill,
.lgrid .lrow > .hclosed,
.lgrid .lrow > .cnt{flex:0 0 62px;text-align:center}
.lgrid .lrow > .hclosed{display:flex;align-items:center;gap:5px;flex-basis:auto;min-width:62px}

/* Retired consent forms, listed apart from the ones in use. Quiet, because
   they are history rather than something to act on — but reachable, so a
   form retired by mistake does not have to come back as a new version. */
/* DESIGN.md rule 7: a `tight` card body has NO padding -- it exists for
   tables that bring their own -- so anything else dropped into one has to
   bring its own or its text sits flush against the card's edge. The existing
   rule covers a bare `p`, `.hint` and `.empty`; this is a section, so it was
   not covered and every line in it ran to the edge. */
.retired-forms{
  padding:15px 18px 17px;
  border-top:1px solid var(--line-soft);
}
.retired-forms h4{
  /* Uppercase is allowed for a structural label, but DESIGN.md rule 5 sets
     the tracking at .06-.07em when it is. This was .0545em. */
  font-size:11px;font-weight:650;letter-spacing:.065em;text-transform:uppercase;
  color:var(--faint);margin-bottom:4px;
}
/* The button belongs BESIDE the name, not against the window edge.
   These rows sat in a single column, so the name was hard left and "Use
   again" was flung to the far right with a dead span between them --
   the very thing he objected to on the settings pages, rebuilt here a few
   hours later. They flow across now, like every other list of short things. */
.retired-forms .lgrid{gap:2px 18px;margin-top:10px}
.retired-forms .lrow{gap:10px}
/* Not flex:1. Stretching the name pushed the button to the column edge and
   reopened the same gap in miniature -- the action sits against the words it
   acts on. */
/* No ellipsis. A form's name is never cut off (DESIGN.md rule 3) -- if it is
   long the row grows. Added one here by reflex and caught it in the same
   breath as fixing the gap. */
.rf-name{flex:0 1 auto;color:var(--muted);font-size:13px}
.retired-forms .lrow{justify-content:flex-start}
.rf-name .sub{display:inline;color:var(--faint);margin-left:4px}
.retired-forms .lrow .btn{flex:0 0 auto}

/* ---- THE CONSENT LIST ON A CLIENT'S RECORD ------------------------------
   His instruction, 2026-09-11: "regarding the consent form menu on the
   client's profile, let's change how it looks, make it more premium."

   It was an `.lgrid` -- a two-column flow -- so a short title and a long one
   put their status in different places, the gap between a name and its pill
   was whatever was left over, and one signed form sat alone beside an empty
   column. Paperwork does not read like that.

   Full-width rows, a hairline between them, every status in one column
   whatever the name is, and a sheet-of-paper glyph so the row reads as a
   DOCUMENT rather than a table cell. */
.cf-body{ padding-top:4px }
.cf-head{
  margin:14px 0 6px;
  font-size:11px; font-weight:650; letter-spacing:.055em; text-transform:uppercase;
  color:var(--faint);
}
.cf-body > .cf-head:first-child{ margin-top:2px }
.cf-list{ display:flex; flex-direction:column }
.cf-row{
  display:flex; align-items:center; gap:11px;
  padding:10px 2px;
  border-top:1px solid var(--line);
}
.cf-list > .cf-row:first-child{ border-top:0 }

/* The glyph, and the only thing on the row that is a block of color. Boxed so
   it reads as an object rather than a stray mark beside the words. */
.cf-ico{
  flex:0 0 auto; display:inline-grid; place-items:center;
  width:30px; height:30px; border-radius:8px;
  background:var(--surface-2); color:var(--faint);
}
.cf-ico.owed{ background:var(--gold-soft,#FBF3E6); color:#9A7430 }

/* Takes the slack, so the status column lands in the same place on every row.
   `min-width:0` because a long form name must be allowed to wrap rather than
   push the pill off the end -- nothing is ever cut off. */
.cf-main{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:1px }
.cf-title{ font-size:13.5px; font-weight:600; color:var(--ink) }
.cf-v{ margin-left:6px; font-size:11.5px; font-weight:500; color:var(--faint) }
.cf-sub{ font-size:12px; color:var(--faint) }

/* A withdrawn or expired form is still evidence and still readable; it simply
   is not the one in force. Quieted, never hidden. */
.cf-row.past .cf-title{ color:var(--muted); font-weight:550 }
.cf-row.past .cf-ico{ opacity:.75 }

/* Where a delete button would be, on a row for somebody who may not delete.
   Without it the Open buttons on two rows stop lining up. */
.cf-gap{ flex:0 0 auto; width:28px }

/* ---- WHAT A CLIENT ANSWERED, inside the signed form ---------------------
   His instruction, 2026-09-11: "I want to be able to open the New client promo
   facial consent form and see the answers etc, not just the signature."
   Flags first and boxed, then the answers as a definition list grouped by the
   section the form asked them in -- the question on one line, what was said
   under it, so a nurse reads down one column rather than across a table. */
.ans{ margin:4px 0 18px }
.ans-flags{
  padding:11px 13px; margin-bottom:14px;
  background:var(--danger-soft); border:1px solid #F2C9C5; border-radius:10px;
}
.ans-flags > b{ display:block; font-size:12px; margin-bottom:7px; color:var(--danger) }
.ans-clear{ margin:0 0 14px; font-size:12.5px; color:var(--faint) }
.ans-sec + .ans-sec{ margin-top:14px }
.ans-h{
  margin:0 0 6px; font-size:11px; font-weight:650;
  letter-spacing:.055em; text-transform:uppercase; color:var(--faint);
}
/* ONE ROW PER ANSWER, question and answer on the same line. As a `dl` in two
   columns the pair drifted apart -- the answer sat a few pixels off its own
   question and the hairline ran under only half the row. */
.ans-list{ display:grid; grid-template-columns:1fr }
.ans-row{
  display:flex; align-items:baseline; gap:14px;
  padding:7px 0; border-top:1px solid var(--line);
}
.ans-q{ flex:1 1 auto; min-width:0; font-size:12.5px; color:var(--muted) }

/* ---- THE FACE SHE TAPPED, ON THE RECORD --------------------------------
   His instruction, 2026-09-13: "on the consent form with the face, I want to
   be able to see the face area selections when I open the consent form."

   The row wraps so the picture sits UNDER the question and its answer rather
   than squeezing them: the words stay the record, the picture is what the eye
   finds first. Marks are percentages of the illustration, which is what keeps
   them right at any size and on a printout.
   ----------------------------------------------------------------------- */
.ans-row.ans-wide{ flex-wrap:wrap }
.ans-row.ans-wide .ans-face{ flex:1 0 100% }
.ans-face{
  position:relative; width:100%; max-width:420px;
  margin:10px 0 4px; border-radius:10px; overflow:hidden;
  border:1px solid var(--line-soft); background:var(--surface);
}
.ans-face img{ display:block; width:100%; height:auto }
.ans-mark{
  position:absolute; border-radius:5px;
  /* TRANSLUCENT, because the thing being marked is INSIDE the picture. The
     ten areas sit over the labels printed on his illustration, so an opaque
     tint hid the very words it was pointing at -- a highlighter that blacks
     out the line. */
  background:rgba(14,124,116,.15); border:1.5px solid var(--acc);
  /* The tint has to survive Save as PDF, where a browser drops backgrounds by
     default and the marks would come out as empty outlines. */
  -webkit-print-color-adjust:exact; print-color-adjust:exact;
}
.ans-a{ flex:0 0 auto; font-size:13px; font-weight:650; color:var(--ink); text-align:right }
/* A no is the common answer and the one nobody needs to read. Quieting it is
   what makes the two that matter findable without coloring anything in. */
.ans-a.no{ font-weight:500; color:var(--faint) }
.ans-d{ display:block; font-weight:450; font-size:12px; color:var(--muted) }
/* Two columns once there is room. Seventeen questions in one column is a
   scroll on a screen with the width for two. */
@media (min-width:820px){
  .ans-list{ grid-template-columns:1fr 1fr; column-gap:30px }
  /* The face is tall, and in one column of two it left the other standing
     empty from the question down to the next section. It takes the full width
     of the card, where the picture has room to be looked at. */
  .ans-list .ans-row.ans-wide{ grid-column:1 / -1 }
}

@media (max-width:560px){
  /* The status and the buttons drop under the name rather than squeezing it. */
  .cf-row{ flex-wrap:wrap }
  .cf-main{ flex:1 1 100% }
  .cf-row > .pill,.cf-row > .btn,.cf-row > .iconbtn{ margin-left:0 }
  .cf-ico{ display:none }
}


/* ---- the good faith exam, one row per treatment ------------------------
   His instruction, 2026-08-30: "there will be the name of treatment (that
   requires GFE) HIFU and near it 3 check box we can mark only one, approved,
   deferred, declined. Also have the note underneith."

   The three sit BESIDE the name, not under it — the old screen stacked a
   checkbox above its own words, which is what he meant by "text is under the
   checkbox looks weird". */
.gfdeclist{
  border:1px solid var(--line);border-radius:10px;
  padding:2px 12px;max-height:300px;overflow-y:auto;
}
.gfrow{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:9px 0;border-bottom:1px solid var(--line-soft);
}
.gfrow:last-child{border-bottom:none}
.gfname{flex:1;min-width:150px;font-size:13px;font-weight:600;color:var(--ink-2)}
.gfname em{
  font-style:normal;font-size:10.5px;font-weight:600;margin-left:7px;
  background:var(--gold-soft);color:var(--gold);padding:1px 6px;border-radius:20px;
}
.gfdecs{display:flex;gap:6px;flex:0 0 auto}
.gfdec{font-size:12px;padding:5px 10px}

/* This dialog needs more room than the 520px default: at that width the
   three decisions wrapped UNDER the treatment name, which is the same
   complaint he made about the old screen ("text is under the checkbox looks
   weird"), just the other way round. */
.modal.wide{max-width:760px}
.gfdeclist textarea,.modal .field textarea{width:100%;box-sizing:border-box}
.gfrow{flex-wrap:nowrap}
@media (max-width:700px){ .gfrow{flex-wrap:wrap} }

/* The expiry warning on a good faith exam. Quiet, but not ignorable. */
.gfe-warn{color:var(--danger);font-weight:650}

/* A field that reads as text until you put the pointer in it. Used for the
   team's names and sign-in addresses, which are corrected in place. */
.cellname{
  width:100%;max-width:230px;padding:5px 7px;font-size:13px;font-weight:650;
  border:1px solid transparent;border-radius:7px;background:transparent;color:inherit;
}
.cellname:hover{border-color:var(--line);background:var(--surface)}
.cellname:focus{outline:none;border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft);background:var(--surface)}
td .cellname[data-personemail]{font-weight:500;color:var(--muted)}

/* Who the booking is for, once she is recognized, and the suggestions the
   four fields turn up as you type. */
.bk-who{display:flex;align-items:center;gap:10px;margin:2px 0 4px;min-height:0}
.bk-who:empty{display:none}
.pick-head{
  font-size:10.5px;font-weight:650;letter-spacing:.065em;text-transform:uppercase;
  color:var(--faint);padding:8px 12px 4px;
}

/* A care sheet on the treatment set-up screen: the spa's own PDF. */
.carebox{
  display:flex;align-items:center;gap:10px;
  padding:11px 13px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface);
}
.carebox .ic{
  font-size:10px;font-weight:600;letter-spacing:.06em;color:var(--danger);
  background:var(--danger-soft);border-radius:5px;padding:3px 5px;flex:none;
}
.care-name{flex:1;min-width:0;font-size:13px}
.care-name .sub{font-size:11.5px;color:var(--muted);margin-top:2px}
.care-old{
  font-size:12px;color:var(--muted);line-height:1.55;margin:7px 2px 0;
  padding-left:10px;border-left:2px solid var(--line);
}

/* A room row carries a name, a count and two actions. The name input used to
   stretch, which pushed the actions to the far edge of the cell -- the same
   marooned-control pattern, caught by the check this time rather than by him. */
.lgrid .lrow input[data-roomname]{flex:0 1 150px;min-width:90px}


/* ---- charting, in two steps --------------------------------------------
   His instruction, 2026-08-30: "I want to have 2 steps for charting, 1 is
   start charting and the other is complete charting." The strip says which
   one you are on and what belongs in each, so nobody has to remember. */
.steps{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:0 0 16px}
.step{
  padding:11px 14px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface);color:var(--muted);
}
.step b{display:block;font-size:13px;font-weight:650;color:var(--ink-2)}
.step span{display:block;font-size:11.5px;line-height:1.5;margin-top:3px}
.step.on{border-color:var(--acc-line);background:var(--acc-soft)}
.step.on b{color:var(--acc-dark)}
.step.done b::after{content:' ¹3';color:var(--acc)}
@media (max-width:760px){ .steps{grid-template-columns:1fr} }

/* The one question asked when a note is signed: did the treatment actually
   happen? A signed note and a performed treatment are not the same thing --
   a nurse charting a reaction has done neither less nor more than her job. */
.outcome{
  margin-top:14px;padding-top:14px;border-top:1px solid var(--line-soft);
  display:flex;flex-wrap:wrap;gap:8px;align-items:center;
}
.outcome .fld-l{flex:0 0 100%;margin:0 0 2px}
.outcome .hint{flex:0 0 100%;margin:4px 0 0}

/* A step that no longer applies: the treatment did not happen, so there is no
   "after" to fill in. */
.step.quiet{background:var(--gold-soft);border-color:var(--gold-line, var(--line))}
.step.quiet b{color:var(--gold)}

/* A number, what it is, and why it matters — in that order, because the
   number is what you came for. */
.statbox{padding:10px 2px}
.statbox .sv{font-size:26px;font-weight:700;letter-spacing:-.03em;color:var(--ink);line-height:1.1}
.statbox .sl{font-size:12.5px;font-weight:600;color:var(--ink-2);margin-top:3px}
.statbox .sw{font-size:11.5px;color:var(--muted);margin-top:2px;line-height:1.45}
#sc-reports{max-width:1140px}

/* The commission rate, on the person it belongs to. */
.commrow{display:flex;align-items:center;gap:7px;margin-top:6px}
.commrow label,.commrow span{font-size:11.5px;color:var(--muted);text-transform:none;letter-spacing:0}
.setup-b .pctbox,.pctbox{
  width:62px;flex:0 0 auto;padding:4px 7px;border:1px solid var(--line);border-radius:7px;
  background:var(--surface);font-size:12.5px;text-align:right;font-variant-numeric:tabular-nums;
}


/* ===============================================================
   DROP-DOWNS
   ===============================================================
   His complaint, 2026-09-01, with a screenshot of the Role menu:
   "it looks bad. Make the list more modern. it looks like windows XP."

   He was right, and it was every drop-down in the app, not only that one.
   Nothing had ever set `appearance`, so a <select> drew the OPERATING
   SYSTEM's own widget: a bevelled grey button down the right-hand side, a
   squarer border, and on Windows a different font from the rest of the page.
   Sitting next to text fields that ARE styled, it read as a control from
   another decade -- which is exactly what he said.

   `appearance:none` takes the OS widget away. Everything below puts a better
   one back. The arrow has to be a background image: a <select> cannot render
   a pseudo-element inside itself, so ::after is not available here.

   THE SPECIFICITY IS DELIBERATE. Six separate rules -- `.field select`,
   `.setup-g select`, `.tyrow select`, `.searchbox`, `.fb-props select` and
   `table.team select` -- each set `background` as a SHORTHAND, and a
   shorthand wipes out a background-image declared anywhere weaker. Two
   :not()s put this rule at (0,2,1), above every one of them, so the arrow
   cannot be erased by a rule added later in index.html either. [multiple]
   and [size] are excluded because those draw as a list box rather than a
   menu and have no arrow to place.
   =============================================================== */
select:not([multiple]):not([size]){
  appearance:none;-webkit-appearance:none;
  background-color:var(--surface);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%23767E87' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 11px center;
  background-size:10px 6px;
  padding-right:31px;
  font-family:inherit;color:var(--ink);
  cursor:pointer;
  transition:border-color 140ms ease,box-shadow 140ms ease,background-color 140ms ease;
}

/* Hover and focus warm the ARROW as well as the border, so the control
   answers as one object instead of lighting up only at its edge.

   Not behind a media query, per CLAUDE.md. A gate can only fail one way --
   by switching the hover off -- and it did, four times, on his machine while
   every check here passed. */
select:not([multiple]):not([size]):hover:not(:disabled),
select:not([multiple]):not([size]):focus:not(:disabled){
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%230E7C74' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
}

/* Pressed. A menu opens on mouse-DOWN, so this is the only frame in which a
   press can be shown at all; without it the control feels dead in the moment
   you actually click it. */
select:not([multiple]):not([size]):active:not(:disabled){
  background-color:var(--surface-2);
}

select:not([multiple]):not([size]):disabled{
  cursor:default;color:var(--muted);background-color:var(--surface-2);opacity:1;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%239AA1A9' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------------
   THE LIST THAT DROPS DOWN, where the browser allows it.

   Everything above styles the CLOSED control. The list itself is drawn by
   the operating system and has never been reachable from CSS -- which is the
   half that genuinely looks like Windows XP, because on Windows it IS a
   Windows widget: square corners, a hard border, a flat blue selection bar.

   Chrome and Edge 135 and later can hand that list to the page
   (`appearance:base-select`). Where that exists the menu becomes a real box:
   our radius, our shadow, our hover color, and rounded rows. Where it does
   not, not one line in this block applies -- so there is no fallback to keep
   in step and no way for it to break an older browser.
   --------------------------------------------------------------- */
@supports (appearance:base-select){
  select:not([multiple]):not([size]),
  select:not([multiple]):not([size])::picker(select){ appearance:base-select; }

  /* The browser supplies its own arrow here -- a small SOLID TRIANGLE, which
     is the heavy, dated shape he was complaining about in the first place.
     Drop it and keep the thin chevron from above, so the control looks
     identical whether or not the browser can style the list. */
  select:not([multiple]):not([size])::picker-icon{ display:none; }
  select:not([multiple]):not([size]){
    display:inline-flex;align-items:center;
    text-align:left;
  }
  /* Open: the chevron turns over, so the control says which state it is in.
     A background image cannot be rotated, so this is the same chevron drawn
     the other way up rather than a transform. */
  select:not([multiple]):not([size]):open{
    border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft);
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%230E7C74' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 5l4-4 4 4'/%3E%3C/svg%3E");
  }

  select:not([multiple]):not([size])::picker(select){
    border:1px solid var(--line);
    border-radius:13px;
    background:var(--surface);
    box-shadow:var(--sh-lg);
    padding:5px;
    margin:6px 0;   /* a gap on BOTH sides: the list flips above the control when there is more room up there */
    /* Nothing is ever cut off: a long menu scrolls rather than truncating. */
    max-height:min(340px,60vh);
    overflow-y:auto;
    opacity:1;transform:translateY(0) scale(1);
    /* `allow-discrete` is what lets a popover animate at all -- without it
       display:none removes the element before a single frame is drawn. */
    transition:
      opacity 140ms ease,
      transform 200ms var(--ease-out),
      overlay 200ms allow-discrete,
      display 200ms allow-discrete;
  }
  @starting-style{
    select:not([multiple]):not([size])::picker(select){ opacity:0;transform:translateY(-5px) scale(.97); }
  }
  select:not([multiple]):not([size])::picker(select):not(:popover-open){ opacity:0;transform:translateY(-5px) scale(.97); }

  select:not([multiple]):not([size]) option{
    display:flex;align-items:center;gap:9px;
    padding:8px 10px;border-radius:9px;
    font-size:13.5px;line-height:1.45;color:var(--ink);
    cursor:pointer;
    transition:background-color 90ms ease;
  }
  select:not([multiple]):not([size]) option:hover{ background:var(--hover); }
  /* The one already chosen stays legible while you point at another, or you
     lose your place halfway through the reach -- the same rule the sidebar
     follows. Quiet: weight and the accent, never a filled blue bar. */
  select:not([multiple]):not([size]) option:checked{ color:var(--acc);font-weight:650; }
  select:not([multiple]):not([size]) option:checked:hover{ background:var(--acc-soft); }
  select:not([multiple]):not([size]) option::checkmark{
    order:1;margin-left:auto;color:var(--acc);font-size:12px;
  }
  select:not([multiple]):not([size]) option:disabled{ color:var(--faint);cursor:default; }
  select:not([multiple]):not([size]) hr{
    border:0;border-top:1px solid var(--line-soft);margin:4px 6px;
  }
}

/* A username is typed exactly as written, so it is set in a face where l, 1
   and I are three different shapes rather than one. */
.mono{
  font-family:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  font-size:.94em;letter-spacing:0;
}



/* ---------------------------------------------------------------
   A room, and the pencil that opens it.

   His instruction, 2026-09-01: "in rooms, change the edit button to Add Room
   and on each room create a small pencil icon so we can click and edit it."
   --------------------------------------------------------------- */
/* Packed LEFT, not spread across the row.
   The name was flex:1, which pushed the count 207px away from it and left a
   dead lane down the middle of every room -- caught by the marooned-control
   check, which measures the gaps between adjacent text rather than trusting
   that a row looks fine. */
.roomrow{ padding:3px 0;justify-content:flex-start;gap:10px; }
.roomrow .rname{ flex:0 1 auto;font-size:13.5px;font-weight:600;color:var(--ink); }
.lgrid .roomrow > .cnt{ flex:0 0 auto;min-width:0;text-align:left; }
.roomrow.off .rname{ color:var(--muted);font-weight:500; }

/* Small, quiet, and still a real target: 28px is the smallest a finger finds
   reliably on the front-desk iPad. */
.iconbtn{
  flex:0 0 auto;display:inline-grid;place-items:center;
  width:28px;height:28px;padding:0;
  border:1px solid transparent;border-radius:8px;
  background:transparent;color:var(--muted);cursor:pointer;
  transition:background-color 120ms ease,color 120ms ease,border-color 120ms ease;
}
.iconbtn:hover{ background:var(--surface-2);border-color:var(--line);color:var(--acc); }
.iconbtn:active{ background:var(--hover); }

/* ---- A CONTROL THAT TAKES SOMETHING AWAY --------------------------------
   His instruction, 2026-09-04, after seeing it on the receipt: "make the x
   adjustments you did to entire CRM, I liked it."

   Quiet until reached for, which is the rule the badges keep -- a list of six
   things must not be six shouting buttons. Faint circle at rest, the soft
   danger tint and a real red under the pointer, and a 0.94 press so it answers
   the finger. Every remove in the app wears this: a note, an add-on on a visit,
   a line on a package, a bookable time, a way to pay, a line on a receipt.

   IT IS A TRASH CAN, AND IT IS ALREADY RED. His instruction, 2026-09-11, and
   his choice from three: "Do it as a small red trash can symbol ... Anything
   that is delete, instead of small X do the same trash symbol (in the entire
   CRM)", soft red at rest rather than gray.

   `--danger-rest` is the muted red, not `--danger` itself. At full strength,
   ten rows of them is the loudest thing on the card and the eye stops reading
   past it -- the same reason a badge is never shouty. Muted, the shape and the
   hue both say "this destroys something" from across the room, and reaching
   for one still changes it. */
.iconbtn.danger{
  border-radius:999px;
  line-height:1;
  color:var(--danger-rest);
  transition:background-color 140ms ease-out, border-color 140ms ease-out,
             color 140ms ease-out, transform 140ms ease-out;
}
.iconbtn.danger:hover{
  background:var(--danger-soft);
  border-color:#F2C9C5;
  color:var(--danger);
}
.iconbtn.danger:active{ transform:scale(.94); }
.iconbtn.danger:focus-visible{ outline:2px solid var(--danger); outline-offset:2px; }

/* The same control, sized for the chip it sits inside. A bookable time is a
   pill with the cross tucked into it, so it cannot be 28px square. */
.slotx{
  display:inline-grid; place-items:center;
  width:18px; height:18px; margin-left:5px; padding:0;
  border:1px solid transparent; border-radius:999px;
  background:transparent; color:var(--danger-rest);
  line-height:1; cursor:pointer;
  transition:background-color 140ms ease-out, border-color 140ms ease-out,
             color 140ms ease-out, transform 140ms ease-out;
}
.slotx:hover{ background:var(--danger-soft); border-color:#F2C9C5; color:var(--danger); }
.slotx:active{ transform:scale(.92); }
.slotx:focus-visible{ outline:2px solid var(--danger); outline-offset:2px; }
.iconbtn:focus-visible{ outline:2px solid var(--acc);outline-offset:2px; }
/* The row lights the pencil too, so the whole row reads as one target rather
   than a name with a mystery button parked beside it. */
.roomrow:hover .iconbtn{ color:var(--acc); }

/* The treatments a room allows. Wraps rather than truncating -- a spa with
   nineteen treatments must be able to see all nineteen. */
.rmtreats{
  display:flex;flex-wrap:wrap;gap:6px 18px;
  max-height:230px;overflow-y:auto;
  padding:10px 12px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface-2);
}
.rmtreats .chk{ flex:0 1 auto;font-size:13px; }
.rmfoot{ margin-top:16px;padding-top:14px;border-top:1px solid var(--line-soft); }

/* ---------------------------------------------------------------
   THE REST OF THE PALETTE

   Seven chip styles existed for twelve Acuity color names, and the flag map
   pointed `red` at `sc-cancel-ish` -- a class defined nowhere, so every red
   flag has been rendering with no chip style at all. These are the five that
   were missing, built the same way as the seven that were there: a soft
   ground and text dark enough to read on it.
   --------------------------------------------------------------- */
.sc-black    {background:#DDE1E6; color:#2B3239}
.sc-orange   {background:#FCEBDD; color:#8A4A18}
.sc-red      {background:#FDECEA; color:#8E2B24}
.sc-sky      {background:#E4F1FA; color:#1F5B7A}
.sc-cyan     {background:#DFF1F1; color:#155E5E}

/* The color a status or flag wears, next to the menu that changes it. */
.swatch{
  flex:0 0 auto;width:15px;height:15px;border-radius:5px;
  background:var(--c,#F2F4F6);
  /* A ring, because White on white is otherwise an empty square. */
  box-shadow:inset 0 0 0 1px rgba(16,24,40,.16);
}

/* A color well inside a normal field.
   `.field input` gives every input width:100% and 8px of padding, which
   flattens a color input into a thin line -- there is nothing left of the
   swatch once the padding is taken out of a 34px box. It needs a height of
   its own and almost no padding. */
.field input[type=color]{
  width:100%;max-width:120px;height:38px;
  padding:3px;border-radius:8px;cursor:pointer;
}
.field input[type=color]::-webkit-color-swatch-wrapper{padding:0}
.field input[type=color]::-webkit-color-swatch{border:none;border-radius:5px}

/* The pencil inside the team table sits in a right-aligned cell, so it needs
   to be pushed to the end rather than centered in whatever width is left. */
table.team td .iconbtn{ margin-left:auto; }
table.team tr.opens:hover .iconbtn{ color:var(--acc);background:var(--surface-2);border-color:var(--line); }

/* ===============================================================
   A STATUS WEARING ITS OWN COLOR
   ===============================================================
   His instruction, 2026-09-01: "instead of putting the color next to it, I
   want you to color the box of the status where it says the name as the
   color. Also make it look more premium."

   So the row IS the label, in the same tint and the same ink it carries on a
   card on the calendar. Reading this page is now reading the calendar. A
   swatch parked beside a white box was a description of the thing; this is
   the thing.

   The tint and the text color both come from the `sc-*` class the palette
   assigns, so there is one source of truth for what "pink" looks like and
   these can never drift from the chips on the schedule.
   =============================================================== */
.chipgrid{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:10px 10px;
}
.chipgrid > .hint{ flex:0 0 100%;margin:14px 0 0; }
.chipwrap{ display:inline-flex;align-items:center;gap:7px; }

.lchip{
  display:inline-flex;align-items:center;gap:7px;
  height:34px;padding:0 5px 0 13px;border-radius:999px;
  /* An inset ring rather than a border: it does not add to the box, so the
     pill keeps its exact height whatever the tint behind it. */
  box-shadow:inset 0 0 0 1px rgb(16 24 40 / .07);
  transition:background-color 220ms var(--ease-out),color 220ms var(--ease-out),
             box-shadow 160ms ease,transform 160ms var(--ease-out);
}
/* The name is text now, not a field: it is changed in the window the pencil
   opens, along with the color and, for a flag, whether it is offered at all. */
.lchip > b{
  font-size:13px;font-weight:600;letter-spacing:-.005em;
  white-space:nowrap;
}

/* THE PENCIL, ON THE CHIP.
   His instruction, 2026-09-01: "make it on the box itself and not near it".
   So it sits inside the pill and borrows the chip's own ink, which keeps it
   quiet on six different tints without a rule for each one. Always visible,
   never hover-only: there is no hover on the treatment-room iPad. */
.chipedit{
  display:inline-grid;place-items:center;flex:0 0 auto;
  width:22px;height:22px;padding:0;margin-right:-2px;
  border:0;border-radius:50%;
  background:transparent;color:currentColor;opacity:.5;
  cursor:pointer;
  transition:opacity 140ms ease,background-color 140ms ease,transform 140ms var(--ease-out);
}
.chipedit:hover{ opacity:1;background:rgb(16 24 40 / .09); }
.chipedit:active{ transform:scale(.92); }
.chipedit:focus-visible{ outline:2px solid var(--acc);outline-offset:2px;opacity:1; }
.lchip:hover .chipedit{ opacity:.85; }

/* Picking a color: the twelve, and the one you are on. */
.palette{
  display:flex;flex-wrap:wrap;gap:9px;
  padding:11px 12px;border:1px solid var(--line);border-radius:11px;
  background:var(--surface-2);
}
.pswatch{
  width:26px;height:26px;padding:0;border:0;border-radius:50%;
  background:var(--c);cursor:pointer;
  box-shadow:inset 0 0 0 1px rgb(16 24 40 / .18);
  transition:transform 160ms var(--ease-out),box-shadow 160ms ease;
}
.pswatch:hover{ transform:scale(1.1); }
.pswatch:active{ transform:scale(.95); }
.pswatch:focus-visible{ outline:2px solid var(--acc);outline-offset:3px; }
.pswatch.on{
  box-shadow:inset 0 0 0 1px rgb(16 24 40 / .25), 0 0 0 2px var(--surface-2), 0 0 0 4px var(--acc);
}
/* What you are about to get, before you commit to it. */
.lbprev{ display:flex;align-items:center;gap:10px;margin-top:12px; }


/* Settings -> Payments. A fee is money the spa loses, so it is shown as a sum
   rather than left as a percentage nobody has done the arithmetic on. */
.feeon{ color:var(--danger);font-variant-numeric:tabular-nums; }
.feeoff{ color:var(--faint);font-weight:500;font-variant-numeric:tabular-nums; }
.commrow .dollar{ color:var(--muted);font-size:13px; }
.sumtab{ width:100%;max-width:380px;border-collapse:collapse;margin-top:4px; }
.sumtab td{ padding:7px 0;font-size:13px;border-bottom:1px solid var(--line-soft); }
.sumtab td.num{ text-align:right;font-variant-numeric:tabular-nums; }
.sumtab tr.zero td{ color:var(--faint); }
.sumtab tr.tot td{ font-weight:650;border-bottom:0;border-top:1px solid var(--line);padding-top:9px; }

/* ---------------------------------------------------------------
   Settings -> Rules.

   His question, 2026-09-01: "a lot of the rules we made are not the same at
   each spa, do you think we should set up a page for it?"
   --------------------------------------------------------------- */
.rulerow{ padding:12px 0;border-bottom:1px solid var(--line-soft); }
.rulerow:last-child{ border-bottom:0;padding-bottom:0; }
.rulerow:first-child{ padding-top:0; }
.rulerow .hint{ margin:6px 0 0;max-width:62ch; }
.ruleset{ display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:2px; }
.ruleset .chk{ font-size:13px; }

/* The rules that are not settings. Quiet, and deliberately NOT drawn as
   switched-off controls -- a greyed-out toggle says "somebody could turn this
   on", and nobody can. */
.card.locked > .card-b{ background:var(--surface-2); }
.fixedlist{ display:grid;gap:2px;margin-top:12px; }
.fixedrule{
  padding:11px 13px;border-radius:10px;background:var(--surface);
  border:1px solid var(--line-soft);
}
.fixedrule .fx-n{ font-size:13px;font-weight:650;color:var(--ink);letter-spacing:-.005em; }
.fixedrule .fx-w{ font-size:12.5px;color:var(--muted);margin-top:3px;line-height:1.5;max-width:70ch; }
.fixedrule .fx-l{
  font-size:11px;color:var(--faint);margin-top:5px;
  font-variant-numeric:tabular-nums;
}

/* A tick and its words stay on one line. In a wrapping row they were being
   squeezed into three-word columns -- "She does / not turn / up". */
.ruleset .chk{ flex:0 0 auto;white-space:nowrap; }

/* What happens when a session does not happen, and what it costs if anything.
   The fee sits beside its outcome, never on a line of its own. */
.outcomerow{ display:flex;align-items:center;gap:10px;flex-wrap:wrap; }
.feebox{ display:inline-flex;align-items:center;gap:6px; }

/* ---------------------------------------------------------------
   Treatments -> Charting.

   A form is only distinguishable by what it asks for, so that is what the
   row leads with rather than a name and a blank column.
   --------------------------------------------------------------- */
.askfor{ display:block;color:var(--faint);font-size:11.5px;line-height:1.45;max-width:46ch; }
.asklist{ display:flex;flex-wrap:wrap;gap:5px;margin-top:5px; }
.askpill{
  font-size:11px;font-weight:600;letter-spacing:-.004em;
  padding:3px 8px;border-radius:999px;
  background:var(--acc-soft);color:var(--acc-dark);
}
.asklist .none{ font-size:11.5px;color:var(--faint);font-style:italic; }

/* Picking what a form asks for. Each one says what it will make the nurse
   fill in AND what she is warned about if she does not -- ticking a box on a
   clinical form should never be a guess. */
.secpicks{ display:grid;gap:8px; }
.secpick{
  display:flex;gap:11px;align-items:flex-start;cursor:pointer;
  padding:11px 13px;border:1px solid var(--line);border-radius:11px;
  background:var(--surface);
  transition:border-color 140ms ease,background-color 140ms ease;
}
.secpick:hover{ border-color:var(--acc-line);background:var(--surface-2); }
.secpick.on{ border-color:var(--acc-line);background:var(--acc-soft); }
.secpick input{ margin-top:2px;flex:0 0 auto; }
.secpick b{ display:block;font-size:13px;font-weight:650;color:var(--ink); }
.secpick i{ display:block;font-size:12.5px;color:var(--muted);font-style:normal;margin-top:2px;line-height:1.5; }
.secpick em{ display:block;font-size:11.5px;color:var(--gold);font-style:normal;margin-top:4px;line-height:1.45; }
.secpick input:disabled{ cursor:default; }
.secpick:has(input:disabled){ cursor:default;opacity:.75; }
.secpick:has(input:disabled):hover{ border-color:var(--line);background:var(--surface); }

/* ---------------------------------------------------------------
   Settings -> Inventory.

   His instruction, 2026-09-02: "let's add an Inventory set up section in the
   settings where we upload the services and products and pricing."
   --------------------------------------------------------------- */
.upbar{ display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap; }
.uppaste{
  width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:10px;
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:12.5px;line-height:1.6;background:var(--surface);resize:vertical;
}
.uppaste:focus{ outline:none;border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft); }

/* The shelf, grouped by brand the way it actually stands. */
.brandhead{
  display:flex;align-items:baseline;gap:8px;
  padding:14px 18px 6px;font-size:11px;font-weight:650;
  letter-spacing:.06em;text-transform:uppercase;color:var(--faint);
}
.brandhead span{ font-weight:600;letter-spacing:0;text-transform:none;font-size:11.5px; }
.money{ font-variant-numeric:tabular-nums;font-weight:600; }

/* WHAT AN UPLOAD WOULD DO, before it does any of it. Every row can be
   unticked, because a bulk change nobody inspected is how a shelf gets
   repriced by a stray column in a spreadsheet. */
.planbox{
  margin-top:14px;border:1px solid var(--line);border-radius:12px;
  background:var(--surface);overflow:hidden;
}
.plansum{ display:flex;flex-wrap:wrap;gap:7px;padding:13px 15px;background:var(--surface-2); }
.planpill{
  font-size:11.5px;font-weight:600;padding:4px 10px;border-radius:999px;
  background:var(--surface);box-shadow:inset 0 0 0 1px rgb(16 24 40 / .08);
}
.planpill b{ font-weight:700; }
.planpill.new    { background:#E1F1E8;color:#1A6244; }
.planpill.reprice{ background:#FBF2E0;color:#87611A; }
.planpill.same   { background:#F2F4F6;color:#5D666E; }
.planpill.none   { background:#EDEFF2;color:#767E87; }
.plantab{ margin:0; }
.plantab tr.off td{ opacity:.5; }
.plantab td{ vertical-align:middle; }
.planfoot{ display:flex;align-items:center;padding:12px 15px;border-top:1px solid var(--line-soft); }

/* ---- THE TREATMENTS LIST ------------------------------------------------
   Its columns had no widths at all, because the one rule that sets them is
   written for `table.team` and `table.tylist` and this table carried no class
   -- so "Set up" broke in half into "Set / up" and the remove button dropped
   underneath it looking like a mistake. His report, 2026-09-02: "on the
   treatments section it got weird with the buttons because now we filled more
   text there."

   Everything that is a label gets to stay on one line; the two columns that
   hold real text -- the treatment and what it is booked as -- take whatever
   is left. Nothing is truncated to achieve it: a long list of appointment
   types wraps onto another line and the row grows. */
table.treatlist{ table-layout:auto }
table.treatlist th:nth-child(2), table.treatlist td:nth-child(2),
table.treatlist th:nth-child(3), table.treatlist td:nth-child(3){
  width:1%; white-space:nowrap;
}
table.treatlist td:nth-child(2) .pill,
table.treatlist td:nth-child(3) .pill{ white-space:nowrap }
/* The two badges sit side by side now that the row is one line high. They are
   different questions, so they keep a real gap between them. */
table.treatlist td:nth-child(2){ line-height:1.9 }
table.treatlist td:nth-child(2) .pill{ margin-left:6px }
/* WITH "BOOKED AS" GONE, THE NAME NO LONGER NEEDS THE PAGE.
   It moved into the treatment on 2026-09-07 -- "we can remove the Booked as
   section ... this way it will look more clean" -- which left the name column
   holding 26% of a table that no longer has anything wide in it, and the two
   chips stranded out on the right. The name takes what it needs; the chips sit
   beside it; the slack goes at the end where it reads as margin. */
table.treatlist th:first-child, table.treatlist td:first-child{ width:1%; white-space:nowrap }
table.treatlist th:nth-child(3), table.treatlist td:nth-child(3){ padding-right:26px }
table.treatlist th:last-child, table.treatlist td:last-child{ width:auto }
table.treatlist th:last-child, table.treatlist td:last-child{
  width:1%; white-space:nowrap; vertical-align:middle;
}
table.treatlist td:last-child{ text-align:right }
table.treatlist td{ vertical-align:middle }
/* The row lights the pencil, so the whole row reads as one target -- the same
   as a room row. Unconditional, never behind a media query. */
table.treatlist tbody tr:hover .iconbtn{ color:var(--acc) }

/* ---- ADD-ONS, UNDER THE THINGS SHE ACTUALLY BOOKS -----------------------
   His instruction, 2026-09-02: "Gold mask is an add on so don't even put it
   as an appointment but as an Add on. Do the same with all the add ons."
   They keep their price and their treatment, so they stay in this list -- but
   below a line that says they are a different kind of thing, because the list
   above it is the booking list and this is not. */
.tysplit{
  padding:16px 18px 7px; border-top:1px solid var(--line-soft);
  font-size:11px; font-weight:650; letter-spacing:.5px;
  text-transform:uppercase; color:var(--faint);
}
.tysplit span{
  text-transform:none; letter-spacing:0; font-weight:500;
  color:var(--muted); margin-left:8px;
}

/* ===============================================================
   THE TILL
   ===============================================================
   The receipt being built, and what is owed, side by side -- because the
   number she is about to be charged must never be somewhere you have to
   scroll to find. On an iPad in portrait the two stack, total first.
   =============================================================== */
.till{ display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:18px; align-items:start; }
.till-main{ display:flex; flex-direction:column; gap:18px; min-width:0; }
.till-side{ display:flex; flex-direction:column; gap:14px; position:sticky; top:18px; }

@media (max-width:1000px){
  .till{ grid-template-columns:minmax(0,1fr); }
  .till-side{ position:static; order:-1; }
}

.till-who{ display:flex; align-items:center; gap:12px; }

.till-results{ display:flex; flex-direction:column; gap:2px; margin-top:8px; }
.till-hit{
  display:flex; align-items:baseline; gap:10px; width:100%;
  padding:9px 12px; border:1px solid transparent; border-radius:9px;
  background:transparent; text-align:left; cursor:pointer; font:inherit; color:var(--ink);
  transition:background-color 120ms ease,border-color 120ms ease;
}
.till-hit:hover{ background:var(--surface-2); border-color:var(--line); }
.till-hit:active{ transform:scale(.995); }
.till-hit:focus-visible{ outline:2px solid var(--acc); outline-offset:2px; }

table.tilllines{ width:100%; border-collapse:collapse; }
/* SAY WHAT THE COLUMNS ARE. His instruction, 2026-09-03: "when I add products,
   show me what everything means, name quantity price etc." Three unlabeled
   boxes reading "1", "item", "250" leave the person at the till working out
   which number she is allowed to type over. */
table.tilllines th{
  text-align:left; font-size:10.5px; font-weight:650; letter-spacing:.6px;
  text-transform:uppercase; color:var(--faint);
  padding:0 14px 8px 0; border-bottom:1px solid var(--line-soft);
}
/* A heading sits over the BOX it names, not over the cell.
   `td.num` is width:1% so the cell shrinks to its content, which puts the
   cell's left edge on the input's left edge -- and right-aligning the heading
   in it parked QUANTITY over the word "session" instead of over the number.
   His report, 2026-09-04: "the title of quantity and the number itself not on
   the same line." */
table.tilllines th.num{ text-align:left; white-space:nowrap; }
table.tilllines th:last-child{ width:1%; }
/* Every cell keeps its own air. `padding:9px 0` gave the columns none at all,
   so "Avinichi — Noni Clarifying Lather Purifier" ran straight into the
   quantity box and the word "qty" touched the price beside it -- his report,
   2026-09-03: "fix the text when I add products, it is still close to the
   edge." */
table.tilllines td{ padding:10px 14px 10px 0; border-bottom:1px solid var(--line-soft); vertical-align:middle; }
table.tilllines tr:last-child td{ border-bottom:0; }
table.tilllines td.num{ width:1%; white-space:nowrap; text-align:right; }
/* The name takes the room; it wraps rather than being cut, and never crowds
   the control next to it. */
table.tilllines td:first-child{ padding-right:24px; line-height:1.45; }

/* LINED UP WITH THE CARD'S OWN HEADING.
   `.card-b.tight` is padding:0 so a table can run the full width, but that
   left every row starting at the card's edge while "What she is buying" sat
   18px in -- his report, 2026-09-03. The 18px is the card header's own inset,
   so the two read as one column rather than two. */
.card-b.tight > table.tilllines td:first-child,
.card-b.tight > table.tilllines th:first-child{ padding-left:18px; }
.card-b.tight > table.tilllines td:last-child,
.card-b.tight > table.tilllines th:last-child{ padding-right:18px; }
.card-b.tight > table.tilllines thead th{ padding-top:12px; }
.card-b.tight > .till-add,
.card-b.tight > .till-charge{ padding-left:18px; padding-right:18px; }
.card-b.tight > .till-charge{ padding-bottom:18px; }

/* Inside the signing window the modal supplies the inset, so this one does
   not add its own on top. */
table.tilllines.slim td{ padding:7px 14px 7px 0; }
table.tilllines.slim td:first-child{ padding-left:0; }
table.tilllines.slim td:last-child{ padding-right:0; }

/* A count and its word travel together, with a gap, so "6" and "sessions" and
   the price beside them stay three separate things. */
.till-count{ display:inline-flex; align-items:center; gap:7px; white-space:nowrap; }
.till-count .hint{ font-size:12px; }
.tilllines .tt-sum td{ border-top:1px solid var(--line); border-bottom:0; padding-top:10px; }
input.tillqty{ width:64px; }
input.tillamt{ width:96px; text-align:right; }
input.tillqty, input.tillamt{
  padding:5px 8px; border:1px solid var(--line); border-radius:8px;
  background:var(--surface); color:var(--ink); font:inherit; font-size:13px;
}
input.tillqty:focus-visible, input.tillamt:focus-visible{ outline:2px solid var(--acc); outline-offset:1px; }

/* The row that adds something, and -- when there is nothing yet -- the line
   that says so. They were two separate blocks with their own padding, which
   left a gap above and the select hard against the card's bottom edge. His
   report, 2026-09-03: "also this looks bad." */
.till-add{ display:flex; gap:10px; align-items:center; margin-top:14px; flex-wrap:wrap; }
.till-add select{ flex:1 1 240px; min-width:0; }
.till-add.first{ margin-top:0; }
.till-empty{ flex:1 0 100%; margin:0 0 2px; }

/* Nothing sits on the card's edge. `.card-b.tight` is padding:0 so a table can
   run the full width, which leaves the LAST thing in the card with no room
   under it unless it says so itself. */
.card-b.tight > .till-add:last-child{ padding-bottom:16px; }
.card-b.tight > .till-add{ padding-top:2px; }

.till-pay{ display:grid; grid-template-columns:minmax(0,1fr) 110px 28px; gap:8px; align-items:center; margin-bottom:8px; }
.till-pay input{ text-align:right; }
.till-pay-sp{ display:block; width:28px; }

.tt-row{ display:flex; justify-content:space-between; align-items:baseline; gap:12px; padding:4px 0; font-size:13.5px; }
.tt-row span{ color:var(--muted); }
.tt-due{ border-top:1px solid var(--line); margin-top:6px; padding-top:10px; font-size:16px; }
.tt-due b{ font-size:20px; }
.tt-left b{ color:var(--gold-ink, #8a6d1f); }
.tt-over b{ color:var(--danger-ink, #b3261e); }

.till-go{ width:100%; padding:13px; font-size:14.5px; }
.till-go[disabled]{ opacity:.45; cursor:not-allowed; }
.till-why{ margin:0; text-align:center; }

/* THE OTHER TWO WAYS TO FINISH A SALE LOOK LIKE BUTTONS.
   "Charge without signing" and "She is not here" are both `.btn.ghost`, which
   is transparent on purpose everywhere else in the app — and stacked under a
   filled primary with a paragraph of hint text between them, they read as
   bold sentences rather than as things you can press. A picture of the screen
   is what showed it.
   His standard, 2026-09-07: design BOTH answers, and THE QUIET ONE IS AN
   OUTLINE, never a second fill. That is exactly this pair. */
/* `.btn.ghost` is TWO classes and beats a single `.till-phone`, so the outline
   below has to name all three or it silently loses and the button goes on
   looking like a paragraph. Exactly the specificity trap that made `.chkfield`
   necessary on 2026-09-08. */
.btn.ghost.till-quick, .btn.ghost.till-phone{
  width:100%; padding:11px; font-size:14px;
  border-color:var(--line); background:var(--surface);
}
.btn.ghost.till-quick:hover, .btn.ghost.till-phone:hover{
  background:var(--surface-2); border-color:var(--acc-line);
}

/* Money OFF, so it cannot be mistaken for another thing being bought. */
table.tilllines tr.till-off td{ color:var(--muted); }
/* What he TYPED is never greyed. Muting the row muted the text in its own
   inputs too, so a discount he had named read like empty placeholder text. */
table.tilllines tr.till-off .tillamt,
table.tilllines tr.till-off .tilldesc{ color:var(--ink); }
.till-minus{ margin-right:4px; color:var(--muted); }
input.tilldesc{ width:100%; max-width:280px; padding:5px 9px; font-size:13px; }

/* The normal price, and what she is actually charged -- the shape his
   Membership app uses, which is the one his consultants already know. */
.till-charge{ margin-top:16px; padding-top:14px; border-top:1px solid var(--line-soft); }
.till-charge .field{ margin-top:8px; }
.till-charge input{ max-width:220px; }
.till-charge .opt{ color:var(--muted); font-weight:400; }
.till-saved{ margin:8px 0 0; color:var(--acc); }
/* What the method asked for, sitting under the row it belongs to -- and SAID,
   not only hinted, because a placeholder vanishes the moment it is answered
   and "10432" on its own does not say it is a check number. */
.till-ref{ display:grid; grid-template-columns:minmax(0,1fr) 110px 28px; gap:8px;
           align-items:center; margin:-2px 0 10px; }
.till-ref label{ font-size:12px; color:var(--muted); text-align:right; grid-column:1; }
.till-ref input{ grid-column:2; width:100%; max-width:none; font-size:13px; padding:6px 10px; text-align:right; }

/* ---- ADD-ONS ON A VISIT ------------------------------------------------
   What else to do while she is here. The booking stays one appointment; this
   is the instruction riding on it. */
.addon-list{ display:flex; flex-direction:column; gap:2px; }
.addon-row{ display:flex; align-items:center; gap:8px; padding:6px 0; }
.addon-row .iconbtn{ margin-left:auto; }
.addon-row.done .addon-name,
.addon-row.done .addon-tick span{ color:var(--muted); }
.addon-tick{ display:flex; align-items:center; gap:8px; cursor:pointer; }
.addon-tick input{ width:16px; height:16px; cursor:pointer; accent-color:var(--acc); }
.addon-tick:hover span{ color:var(--acc); }
.addon-tick:active{ transform:scale(.995); }
.addon-name{ font-size:13.5px; }

/* An add-on sits under the treatment it was added to, so five peels with two
   dermaplanes reads as two of THOSE peels having one -- not as two loose
   dermaplanes nobody can account for. */
.card-b.tight > table.tilllines tr.till-child td:first-child{ padding-left:36px; }
table.tilllines tr.till-child td:first-child{ padding-left:18px; }
table.tilllines tr.till-child b{ font-weight:600; color:var(--ink-2, var(--ink)); }
.till-tee{ color:var(--faint); margin-right:2px; }
.till-over{ margin:8px 0 0; color:var(--gold-ink, #8a6d1f); }

/* A note's date line carries its own controls, pushed to the far end.
   The whitespace between the wrapper and `.meta` used to be a text node, and a
   block box after one gets its own empty line -- which is the band of nothing
   above the date he sent a screenshot of on 2026-09-03. The markup has no
   stray whitespace now; this keeps the row honest. */
.note .meta{ display:flex; align-items:center; gap:8px; }
.note .note-acts{ margin-left:auto; display:flex; gap:2px; }
.note .note-acts .iconbtn{ width:24px; height:24px; }
.note .note-body{ white-space:pre-wrap; }

/* Notes taken off a record. Kept, and findable where the notes are -- his
   question, 2026-09-03: "so where can I see that audit when I delete a note?"
   The honest answer had been nowhere. */
.note-gone{ margin-top:12px; }
.note-gone > summary{
  cursor:pointer; font-size:12px; color:var(--muted); font-weight:600;
  padding:6px 0; list-style:none; display:flex; align-items:center; gap:6px;
}
.note-gone > summary::-webkit-details-marker{ display:none }
.note-gone > summary::before{ content:'\25B8'; transition:transform 140ms ease; }
.note-gone[open] > summary::before{ transform:rotate(90deg); }
.note-gone > summary:hover{ color:var(--acc); }
.note-gone > summary:active{ transform:scale(.995); }
.note.gone{ opacity:.72; background:var(--surface-2); border-color:var(--line-soft); }
.note.gone .gone-by{ margin:6px 0 0; font-style:italic; }

/* HER RECORD IS ONE COLUMN.
   His instruction, 2026-09-03: "instead of putting this contact box on the
   side of each client, just put a pencil on the box above it where it shows
   the info of the client in case we need to edit it and allow the other boxes
   to spread to the entire screen because now they have the room." Her phone
   and email were already on the head box; the card beside it repeated them and
   took 336px from everything else. */
.cols.one{ grid-template-columns:minmax(0,1fr); }
.cli-name .iconbtn{ width:26px; height:26px; }
.cli-head:hover .cli-name .iconbtn{ color:var(--acc); }

/* One treatment's set-up, in a window. Its title bar carries the name and its
   footer carries Save, so the old page header would have repeated both. */
.setup-reach{ margin:0 0 14px; }
#setupBody .card{ margin-bottom:14px; }
#setupBody .card:last-child{ margin-bottom:0; }

/* WHO IS PAYING: today's list first, search underneath.
   The person at the desk is almost always somebody just treated. */
.till-today{ display:flex; flex-direction:column; gap:2px; margin-bottom:4px; }
.till-hit.today{ display:grid; grid-template-columns:52px 10px minmax(0,auto) 1fr; gap:10px; align-items:center; }
.till-hit.today .t{ font-variant-numeric:tabular-nums; color:var(--muted); font-size:12.5px; }
.till-hit.today .sw{ width:10px; height:10px; border-radius:3px; display:block; }
.till-hit.today .hint{ text-align:right; }
.till-orsearch{ margin:12px 0 6px; }



/* ---- THE TWO CONTROLS ON A RECEIPT LINE --------------------------------
   "Instead of + button for adding add on I want you to change it to Add on
   button" and "make the x button to remove look better and premium as well,
   maybe color it with Red" -- his instructions, 2026-09-04.

   Both are QUIET until you reach for them, which is the same rule the badges
   keep: a receipt with six lines must not be six shouting buttons. The add-on
   is an outlined pill that fills with the house green on hover; the remove is
   a circle that only turns red when the pointer is on it, because red sitting
   there permanently reads as an error on every line.

   Press feedback is 140ms and a 0.97 scale, and the transitions name the
   properties they animate rather than `all`. */
table.tilllines td:last-child{ white-space:nowrap; }
table.tilllines td:last-child .till-addon{ margin-right:6px; }

.btn.till-addon{
  white-space:nowrap;
  padding:5px 12px;
  border:1px solid var(--acc-line);
  border-radius:999px;
  background:var(--surface);
  color:var(--acc);
  font-size:12px;
  font-weight:650;
  letter-spacing:.1px;
  transition:background-color 140ms ease-out, border-color 140ms ease-out,
             color 140ms ease-out, transform 140ms ease-out;
}
.btn.till-addon:hover{
  background:var(--acc);
  border-color:var(--acc);
  color:#fff;
}
.btn.till-addon:active{ transform:scale(.97); }
.btn.till-addon:focus-visible{ outline:2px solid var(--acc); outline-offset:2px; }

/* The row makes it VISIBLE; only reaching for it makes it red. */
table.tilllines tbody tr:hover .iconbtn.danger{ color:var(--muted); }
table.tilllines tbody tr:hover .iconbtn.danger:hover{ color:var(--danger); }

/* REDUCED MOTION GOES LAST, or it does not win.
   The block further up cannot cover rules written after it -- at equal
   specificity the later one takes it -- so anything added below it needs its
   own, and that own has to be the last word in the file. */
@media (prefers-reduced-motion:reduce){
  .btn.till-addon:active,
  .iconbtn.danger:active,
  .slotx:active,
  .till-hit:active,
  .addon-tick:active,
  .note-gone > summary:active{ transform:none; }
  .btn.till-addon,.iconbtn.danger,.slotx{ transition:background-color 1ms, color 1ms, border-color 1ms; }
}

/* LOOKING AS SOMEBODY ELSE.
   A view that lies about permission is worse than no view, so the banner says
   plainly that only the VIEW changed. Gold rather than red: nothing is wrong,
   he is just not seeing his own screen. */
.viewas-bar{
  display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  margin:0 0 14px; padding:10px 16px;
  border:1px solid #EBD9A8; border-radius:10px;
  background:var(--gold-soft, #FBF4E4); color:var(--gold-ink, #7A5F1A);
  font-size:13px; line-height:1.5;
}
.viewas-bar b{ font-weight:700; }
.viewas-bar .btn{ margin-left:auto; }

/* ---- HER PAPERWORK -----------------------------------------------------
   Everything she signed, and everything taken of her, in one list. */
.paper-list{ display:flex; flex-direction:column; }
.paper-row{
  display:grid; grid-template-columns:26px minmax(0,1fr) auto auto;
  gap:12px; align-items:center; width:100%; text-align:left;
  padding:12px 18px; border:0; border-bottom:1px solid var(--line-soft);
  background:transparent; font:inherit; color:var(--ink); cursor:pointer;
  transition:background-color 140ms ease-out;
}
.paper-row:last-child{ border-bottom:0; }
.paper-row:hover{ background:var(--surface-2); }
.paper-row:active{ background:var(--hover); }
.paper-row:focus-visible{ outline:2px solid var(--acc); outline-offset:-2px; }
.paper-row .ic{ font-size:15px; }
.paper-row .who{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.paper-row .who .hint{ font-size:12px; }
.paper-row .when{ white-space:nowrap; font-size:12.5px; }

/* ---- A DOCUMENT, ON SCREEN AND ON PAPER --------------------------------
   "Download as PDF" is the browser's own Save as PDF driven by this, the same
   way his other five apps produce their documents: no library, and what he
   sees is what comes out. */
.doc{ font-size:13.5px; line-height:1.6; color:var(--ink); }
.doc-head{ display:flex; justify-content:space-between; gap:20px; align-items:flex-start;
  padding-bottom:14px; margin-bottom:16px; border-bottom:1px solid var(--line); }
.doc-spa{ font-weight:700; font-size:16px; }
.doc-who{ text-align:right; }
.doc-void{ margin:0 0 14px; padding:8px 12px; border-radius:8px;
  background:var(--danger-soft); color:var(--danger); font-weight:650; }
.doc-body{ white-space:pre-wrap; margin-bottom:16px; }
table.doc-lines{ width:100%; border-collapse:collapse; margin-bottom:20px; }
table.doc-lines td{ padding:7px 0; border-bottom:1px solid var(--line-soft); vertical-align:top; }
table.doc-lines td.num{ text-align:right; white-space:nowrap; width:1%; padding-left:20px; }
table.doc-lines tr.sub td:first-child{ padding-left:16px; color:var(--muted); }
table.doc-lines tr.doc-total td{ border-top:1px solid var(--line); border-bottom:0; padding-top:11px; }
dl.doc-fields{ display:grid; grid-template-columns:auto 1fr; gap:6px 16px; margin:0 0 18px; }
dl.doc-fields dt{ color:var(--muted); }
dl.doc-fields dd{ margin:0; }
.doc-sign{ margin-top:22px; padding-top:14px; border-top:1px solid var(--line); }
.doc-sign img{ display:block; max-width:280px; max-height:90px; margin-bottom:6px; }
.doc-signline b{ display:block; }

/* WHAT THE PRINTER GETS: the document, and nothing else.
   `printing` is put on the body for the duration, so a stray print from
   anywhere else in the app still prints the app. */
/* SAVE AS PDF, AND THE THREE BLANK PAGES IN FRONT OF IT.
   His report, 2026-09-13: "whenever I try to save the PDF of the policy the new
   client signed it is opening 3 pages that are blank."

   The document was never blank. It was PUSHED DOWN THE PAGE by things that had
   no business printing at all: measured in print media, his one-page policy
   started 1,157px in, behind the appointment panel and the till.

   `body.printing .modal-wrap{ position:static; display:block }` was the cause.
   `#tillWrap` is ALSO a `.modal-wrap` -- and the till is hidden by being a
   fixed overlay at zero opacity, never by `display`, so turning it static put
   a closed till back into the flow. Same shape as the empty gold strip: a rule
   that defeats how something is hidden.

   There are TWO print jobs here and they want opposite things, which is why one
   class could not serve both. A DOCUMENT is the window and the app behind it
   goes; a SCREEN -- the payroll page -- IS the app, and hiding `.body` printed
   nothing at all. They say which they are now. */
@media print{
  /* NAMED THE ONE THAT STAYS, NOT THE ONES THAT GO.
     The old rule listed what to hide, and the list was never going to keep up:
     it missed the shell, the panel, the till, the hand-over, the toast. Every
     overlay added since has been one more empty page nobody would connect to
     the overlay that caused it. Whatever is on the body tomorrow is covered. */
  body.printdoc > *:not(#modalWrap){ display:none !important; }
  body.printscreen > *:not(.app){ display:none !important; }

  /* PRINTING A DOCUMENT: the window becomes the page. */
  body.printdoc #modalWrap:not(.on){ display:none !important; }
  body.printdoc #modalWrap.on{ position:static; background:none; display:block; }
  /* `!important`, and `#modalWrap` too: `.modal.wide` pins 760px from the
     inline stylesheet in index.html, which is written after this file and wins
     every tie -- so the document was printing at 760px across an 816px sheet
     however wide the paper was. The wrapper's 20px padding is the same story. */
  body.printdoc #modalWrap{ padding:0 !important; }
  /* `max-height:90vh; overflow:auto` is what makes a window a window, and in
     print it is a guillotine: a two-page consent printed its first sheet and
     the rest was simply cut off. Nothing is ever cut off. */
  body.printdoc .modal{ box-shadow:none !important; border:0 !important; margin:0 !important;
    max-width:none !important; width:100% !important;
    max-height:none !important; overflow:visible !important; }
  body.printdoc .modal-b{ overflow:visible; max-height:none; padding:0; }
  /* `.modal-top`, not `.modal-t`. The old rule named a class that does not
     exist anywhere in this app, so the window's own title bar -- the word
     "Policy" and a close cross -- printed onto every document ever saved, and
     no check had ever looked at the output to notice. */
  body.printdoc .modal-f, body.printdoc .modal-top{ display:none !important; }
  body.printdoc{ background:#fff !important; }
  body.printdoc .modal{ border-radius:0 !important; }

  /* PRINTING A SCREEN: the screen IS the page. The shell is a grid a full
     viewport tall with its own scroller inside, and both have to let go or the
     page is cropped at whatever happened to be on screen. */
  body.printscreen .side, body.printscreen .top,
  body.printscreen #viewAsBar, body.printscreen .staleband{ display:none !important; }
  body.printscreen .app{ height:auto !important; display:block !important; }
  body.printscreen .body{ overflow:visible !important; height:auto !important; }

  body.printing .doc{ font-size:12pt; }
  body.printing .doc-void{ border:1px solid currentColor; }

  /* A SIGNED PDF SAVES AS THE PDF ITSELF.
     His report, 2026-09-13: "PDF inside of white paper. Just open and have me
     save the signed pdf it self."

     It was printing a PICTURE OF THE SCREEN: his policy inside the white card
     the app draws it in, on a tinted page, with his name and the date printed
     AGAIN underneath -- while the page already carries both, in the boxes he
     signed them in. A document that repeats itself is a document somebody has
     to read twice to be sure it says one thing.

     The sheet has no margin of its own; a page that is an image fills it, and
     a document that is TYPED (a receipt) gets its margin back below. */
  @page{ margin:0 }
  body.printdoc .modal{ background:none !important; }
  body.printdoc .doc{ padding:12mm; }
  body.printdoc .doc-pages{ padding:0 !important; }
  body.printdoc .doc-pages .doc-head,
  body.printdoc .doc-pages .doc-sign{ display:none !important; }
  /* A PAGE IS FITTED BY HEIGHT AS WELL AS WIDTH.
     Stretched to the full width, a page whose shape is a hair taller than the
     sheet spills a single pixel onto the next one -- and `break-after:page`
     then turns that pixel into a blank sheet. Two uploaded pages came out as
     four, every other one empty. Letter is 1:1.294 and this policy is 1:1.295,
     which is all it takes; A4 artwork on Letter paper is not close at all.
     The canvas is bounded BOTH ways and the page shrink-wraps it, so the boxes
     drawn on top -- positioned as percentages of the page -- still land in the
     right places. */
  /* BOUNDED IN INCHES, NOT PERCENTAGES.
     A percentage resolves against the containing block, and the containing
     block here shrink-wraps its own contents -- so `max-width:100%` on the
     picture meant "no wider than yourself", which is no limit at all. The page
     then overflowed the sheet and `break-after:page` turned the overflow into
     a blank one. Letter with no margin is 8.5 x 11in; 8.3 x 10.7 leaves room
     for rounding, and the page is fitted to whichever of the two bites first.

     IT ONLY SHOWED AT TWO DEVICE PIXELS PER POINT. The picture is rasterized
     at the width it is drawn at TIMES the screen's ratio, so on a 1x test
     browser it was 760px and slipped under every limit, while on his Mac it is
     1520px and has to be scaled to fit. The check runs at 2x for that reason. */
  body.printdoc .doc-signed{ display:block !important; gap:0 !important; }
  body.printdoc .doc-page{
    box-shadow:none !important; border:0 !important; border-radius:0 !important;
    display:block !important; width:fit-content !important; height:auto !important;
    max-width:none !important; margin:0 auto !important;
    break-after:page; break-inside:avoid;
  }
  body.printdoc .doc-page canvas{
    display:block; width:auto !important; height:auto !important;
    max-width:8.3in !important; max-height:10.7in !important;
  }
  body.printdoc .doc-page:last-child{ break-after:auto; }

  /* The screen print keeps a margin, since @page no longer provides one. */
  body.printscreen .app{ padding:10mm; }
}

/* A file on her record. Same row as the paperwork list, but not a button --
   the controls inside it are. */
.paper-row.as-row{ cursor:default; }
.paper-row.as-row:hover{ background:transparent; }
.paper-row.as-row .btn, .paper-row.as-row .iconbtn{ flex:0 0 auto; }

/* ---- ONE APPOINTMENT, AND EVERYTHING ABOUT IT -------------------------
   His instruction, 2026-09-04: the treatment note is not a thing on its own,
   it is what was written at a visit. So it sits with the visit, alongside the
   add-ons, the notes and the status. */
.vdoc-top{ display:flex; justify-content:space-between; gap:16px; align-items:flex-start;
  padding-bottom:14px; border-bottom:1px solid var(--line-soft); }
.vdoc-what{ display:flex; align-items:center; gap:8px; font-size:15px; margin-bottom:4px; }
.vdoc-chips{ display:flex; gap:6px; flex-wrap:wrap; justify-content:flex-end; }
.vdoc-sect{ padding-top:16px; }
.vdoc-h{ font-size:10.5px; font-weight:650; letter-spacing:.6px; text-transform:uppercase;
  color:var(--faint); margin-bottom:8px; }
.vdoc-line{ display:flex; align-items:center; gap:8px; padding:6px 0; font-size:13.5px; }
.vdoc-note{ padding:8px 12px; margin-bottom:6px; border-radius:9px;
  background:var(--surface-2); font-size:13px; line-height:1.55; }
.vdoc-note .hint{ margin-right:6px; }

/* ---- COMING UP, AND PAST ----------------------------------------------
   His report, 2026-09-04: "make the coming up and Past more visible as I did
   not notice at first and thought why the appointments look different in
   size." They were faint grey capitals with no ground under them, which reads
   as a column header rather than a divider -- so the list looked like one run
   of rows of inconsistent height, and the taller ones (a visit carrying a
   note) looked like a bug.

   A band with its own ground, a rule above it, and the count beside it. Now it
   separates. */
.vgroup{
  display:flex; align-items:center; gap:10px;
  padding:9px 18px;
  background:var(--surface-2);
  border-top:1px solid var(--line);
  border-bottom:1px solid var(--line-soft);
  font-size:11px; font-weight:650; letter-spacing:.7px; text-transform:uppercase;
  color:var(--ink-2, var(--ink));
  position:sticky; top:0; z-index:1;
}
.vgroup:first-child{ border-top:0; }
.vgroup .n{
  margin-left:auto;
  padding:1px 8px; border-radius:999px;
  background:var(--surface); border:1px solid var(--line-soft);
  font-size:10.5px; font-weight:600; letter-spacing:0; color:var(--muted);
}

/* Backups card (Settings -> Rules). Its own prefix so it cannot collide with
   the inline <style> in index.html, which wins every argument it is part of. */
.bk-sub{font-size:12px;color:var(--muted)}
#backupCard .bk-rows{display:flex;flex-direction:column;gap:2px}
#backupCard .bk-row{display:grid;grid-template-columns:170px auto 1fr;align-items:center;
  gap:12px;padding:11px 2px;border-bottom:1px solid var(--line)}
#backupCard .bk-row:last-child{border-bottom:0}
#backupCard .bk-row b{font-size:13px}
#backupCard .pill{white-space:nowrap;justify-self:start}
@media (max-width:760px){
  #backupCard .bk-row{grid-template-columns:1fr auto;row-gap:4px}
  #backupCard .bk-row .bk-sub{grid-column:1/-1}
}

/* ---------------------------------------------------------------
   QUICK CHECKOUT — the cart on an appointment card.

   Visible without shouting: a busy day has thirty of these, so it sits at
   partial opacity and comes forward on hover. It is deliberately NOT in the
   tab order — the card is already `role="button"`, and a focusable button
   inside it would nest one control in another. The keyboard route to the same
   thing is the appointment panel, which has its own Take payment.
   --------------------------------------------------------------- */
.appt .cartbtn{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  width:20px; height:20px; padding:0; margin:-2px -2px -2px 0;
  border:0; border-radius:6px; background:transparent; color:inherit;
  opacity:.66; cursor:pointer; -webkit-appearance:none;
  transition:opacity 140ms ease, background-color 140ms ease, transform 120ms ease;
}
.appt.sm .cartbtn,.appt.xs .cartbtn{ width:18px; height:18px }
.appt .cartbtn:hover{ opacity:1; background:rgba(0,0,0,.09) }
.appt .cartbtn:active{ transform:scale(.94); opacity:1 }
.appt .cartbtn:focus-visible{ outline:2px solid var(--acc); outline-offset:1px; opacity:1 }

/* The cart glyph in the top bar sits with its label. */
#btnCheckout{ display:inline-flex; align-items:center; gap:6px }

@media (prefers-reduced-motion:reduce){
  .appt .cartbtn{ transition:opacity 1ms, background-color 1ms }
  .appt .cartbtn:active{ transform:none }
}

/* ---------------------------------------------------------------
   THE TILL AS AN OVERLAY — his instruction, 2026-09-05: "I want the checkouts
   to be on the same page and not on a different page."

   Below the general modal (z-index 50) on purpose, so signing a receipt opens
   ON TOP of the sale rather than replacing it.
   --------------------------------------------------------------- */
.till-wrap{ z-index:45 }
.modal.till-modal{ max-width:1060px }
/* The header stays put and only the sale scrolls, so Charge never scrolls away
   on a long receipt. */
.modal.till-modal{ display:flex; flex-direction:column; max-height:92vh; overflow:hidden }
.modal.till-modal .modal-top{ flex:0 0 auto }
.modal.till-modal #checkoutBody{ flex:1 1 auto; overflow:auto }

/* ---------------------------------------------------------------
   QUICK CHECKOUT — the same till in one narrow column.

   His choice, 2026-09-05, from four mock-ups: the sale that takes twenty
   seconds should not be laid out like a sales desk. Big targets, no typing.
   --------------------------------------------------------------- */
.till.quick{
  display:flex; flex-direction:column; gap:14px; max-width:560px; margin:0 auto;
  /* `.till` sets align-items:start for the wide two-column layout. Inherited
     here it made every card shrink to its own content, so they came out four
     different widths down one column. */
  align-items:stretch;
}
.till.quick .card-b{ padding:14px 16px }

/* THE THINGS SOLD AT THE DOOR, as one tap each.
   His choice, 2026-09-05, from four looks: squares, three across, the price
   large underneath. Two across on a narrow window, because "Noni Clarifying
   Lather Purifier" has nowhere to go in a third of 560px. */
.qpicks{ display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:8px; margin-bottom:14px }
.qpick{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
  min-height:82px; padding:10px 8px; text-align:center; cursor:pointer;
  font:inherit; font-size:11.5px; font-weight:600; line-height:1.25; color:var(--ink-2);
  background:var(--surface-2); border:1px solid var(--line); border-radius:12px;
  /* Two lines, then stop. A long name must not make one square taller than
     the rest of the row. */
  overflow:hidden;
  transition:background-color 140ms ease, border-color 140ms ease, transform 120ms ease;
}
.qpick > span:first-child{
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.qpick .qprice{
  font-size:14px; font-weight:650; color:var(--ink); font-variant-numeric:tabular-nums;
}
.qpick:hover{ border-color:var(--acc); background:var(--acc-soft, #E6F3F1) }
.qpick:active{ transform:scale(.97) }
.qpick:focus-visible{ outline:2px solid var(--acc); outline-offset:2px }
/* "Other" is a square like the rest, but plainly not a thing with a price. */
.qpick-other{ border-style:dashed }
.qpick-other .qprice{ font-size:17px; line-height:1; color:var(--acc) }

/* the rest of the ways to pay, behind a small button */
.qmore{
  display:block; width:100%; margin:0 0 10px; padding:8px; border-radius:9px; cursor:pointer;
  font:inherit; font-size:12.5px; font-weight:600; color:var(--acc);
  background:none; border:1px dashed var(--line);
  transition:background-color 140ms ease, border-color 140ms ease;
}
.qmore:hover{ border-color:var(--acc); background:var(--acc-soft, #E6F3F1) }
.qmore.on{ border-style:solid; border-color:var(--acc) }
.qmore-row{ flex-wrap:wrap }
.qmore-row .qmethod{ flex:1 1 calc(50% - 4px); font-size:12.5px; padding:10px 8px }
.qfee{ display:block; font-size:10.5px; font-weight:600; opacity:.7; margin-top:2px }
@media (max-width:560px){ .qpicks{ grid-template-columns:repeat(2, minmax(0, 1fr)) } }

/* what is on the receipt, one row each */
.qlines{ display:flex; flex-direction:column; gap:8px; margin-bottom:12px }
.qline{ display:grid; grid-template-columns:1fr 64px 92px auto auto; gap:8px; align-items:center }
.qline.kid{ padding-left:14px }
.qline b{ font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.qline input{ font-variant-numeric:tabular-nums; text-align:right }

/* the tip, as four buttons and a box */
.qtips{ display:flex; gap:8px; align-items:center; flex-wrap:wrap }
.qtip{
  font:inherit; font-size:13px; font-weight:650; padding:9px 15px; border-radius:10px; cursor:pointer;
  border:1px solid var(--line); background:var(--surface); color:var(--ink);
  transition:background-color 140ms ease, border-color 140ms ease, color 140ms ease, transform 120ms ease;
}
.qtip.on{ border-color:var(--acc); background:var(--acc-soft, #EAF5F3); color:var(--acc) }
.qtip:hover{ border-color:var(--acc) }
.qtip:active{ transform:scale(.97) }
/* Two classes deliberately: `.searchbox{width:100%}` lives in the inline
   <style> in index.html, which wins over this file at equal specificity. */
.qtips .qtipother{ width:96px; text-align:right; flex:0 0 auto }
.qwho{ display:block; margin-top:12px }
.qwho .hint{ display:block; margin-bottom:4px }

/* how she is paying */
.qmethods{ display:flex; gap:8px; margin-bottom:10px }
.qmethod{
  flex:1; font:inherit; font-size:13px; font-weight:650; padding:12px 8px; border-radius:10px; cursor:pointer;
  border:1px solid var(--line); background:var(--surface); color:var(--ink);
  transition:background-color 140ms ease, border-color 140ms ease, color 140ms ease, transform 120ms ease;
}
.qmethod.on{ border-color:var(--acc); background:var(--acc-soft, #EAF5F3); color:var(--acc) }
.qmethod:hover{ border-color:var(--acc) }
.qmethod:active{ transform:scale(.97) }
.qpay .qother,.qpay .qamt{ margin-bottom:10px; width:100% }
.qamt{ text-align:right; font-variant-numeric:tabular-nums }

.qtotal{ display:flex; justify-content:space-between; align-items:baseline; margin:14px 0 12px }
.qtotal span{ font-size:15px; font-weight:650 }
.qtotal b{ font-size:26px; font-weight:700; letter-spacing:-.02em; font-variant-numeric:tabular-nums }

.qcharge{ width:100%; padding:14px; font-size:15px }
.qwhy{ text-align:center; margin:9px 0 0 }
.qtop{ display:flex; justify-content:flex-end; margin:-2px 0 -6px }
.qtop .qlink{ width:auto; margin:0; padding:6px 10px }
.qlink{
  display:block; width:100%; margin:14px 0 0; padding:8px; font:inherit; font-size:13px; font-weight:600;
  background:none; border:0; color:var(--acc); cursor:pointer; border-radius:8px;
  transition:background-color 140ms ease;
}
.qlink:hover{ background:var(--acc-soft, #EAF5F3) }
.qlink:active{ background:var(--acc-soft-hover, #D6EBE7) }

/* the narrow window it lives in */
.modal.till-modal.quick-size{ max-width:620px }

@media (prefers-reduced-motion:reduce){
  .qpick,.qtip,.qmethod{ transition:background-color 1ms, border-color 1ms, color 1ms }
  .qpick:active,.qtip:active,.qmethod:active{ transform:none }
}

/* "Show on the quick checkout" — the tick that decides which things get a
   square. His question, 2026-09-05: "how can I edit the products and services
   that are there?" */
.qpcheck{
  display:flex; gap:10px; align-items:flex-start; margin:14px 0 4px;
  padding:12px 13px; border:1px solid var(--line); border-radius:10px; cursor:pointer;
  transition:border-color 140ms ease, background-color 140ms ease;
}
.qpcheck input{ margin-top:2px; flex:0 0 auto; accent-color:var(--acc) }
.qpcheck span{ display:block }
.qpcheck b{ display:block; font-size:13px; font-weight:650 }
.qpcheck .hint{ display:block; margin-top:2px }
.qpcheck:hover{ border-color:var(--acc) }
.qpcheck:has(input:checked){ border-color:var(--acc); background:var(--acc-soft, #E6F3F1) }
@media (prefers-reduced-motion:reduce){ .qpcheck{ transition:none } }

/* "Paying now" — the box he could not identify. */
.qamt-row{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:10px }
.qamt-row .hint{ flex:0 0 auto }
.qtips .qtipother,.qamt-row .qamt{ width:120px; text-align:right; flex:0 0 auto;
  font-variant-numeric:tabular-nums }
.qleft{ display:flex; justify-content:space-between; align-items:baseline; margin:-6px 0 12px }
.qleft span{ font-size:12.5px; color:var(--muted) }
.qleft b{ font-size:14px; font-weight:650; color:var(--gold, #8A6A1F); font-variant-numeric:tabular-nums }

/* everything the spa sells, in a window */
.inv-results{ max-height:52vh; overflow:auto; margin-top:10px; display:flex; flex-direction:column; gap:2px }
.inv-hit{
  display:grid; grid-template-columns:1fr auto auto; gap:10px; align-items:center; width:100%;
  padding:11px 12px; text-align:left; font:inherit; cursor:pointer;
  background:none; border:0; border-radius:9px; color:var(--ink);
  transition:background-color 120ms ease;
}
.inv-hit b{ font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.inv-price{ font-weight:650; font-variant-numeric:tabular-nums; font-size:13.5px }
.inv-hit:hover{ background:var(--acc-soft, #E6F3F1) }
.inv-hit:active{ background:var(--acc-soft-hover, #D6EBE7) }

/* SHE IS SHORT — the balance, asked about rather than swallowed. */
.qshort{ margin:0 0 12px; padding:12px 13px; border:1px solid var(--gold, #B08542);
  border-radius:11px; background:var(--gold-soft, #FBF3E6) }
.qshort-h b{ font-size:14px; font-weight:650; font-variant-numeric:tabular-nums }
.qshort .hint{ margin:2px 0 9px }
.qshort .qmethods{ margin-bottom:6px }
.qshort .qlink{ margin:0; padding:6px }

/* which kind of tip it is */
.qtipway{ margin:10px 0 0 }
.qlink.qtipswap{ display:inline; width:auto; margin:0 0 0 4px; padding:0; font-size:12.5px }

/* what payroll still owes on tips */
.tip-owed{ color:var(--gold, #B08542) }

/* Reports: a custom span, and the invoice list */
.rp-custom{ display:flex; align-items:flex-end; gap:10px; flex-wrap:wrap; margin-bottom:16px }
.rp-custom label{ display:flex; flex-direction:column; gap:3px; font-size:12px; color:var(--muted) }
.rp-custom input[type="date"]{
  font:inherit; font-size:13px; padding:7px 10px; border:1px solid var(--line);
  border-radius:9px; background:var(--surface); color:var(--ink);
}
table.team tbody tr[data-invoice]{ cursor:pointer }
table.team tbody tr[data-invoice]:hover{ background:var(--acc-soft, #E6F3F1) }
/* A void is SHOWN and does not count. Hiding it would leave a hole in a
   numbered sequence, which is the first thing an auditor asks about. */
tr.inv-void td{ opacity:.55; text-decoration:line-through }
tr.inv-void td:last-child{ text-decoration:none }
table.team tfoot td{ border-top:2px solid var(--line); padding-top:10px; font-variant-numeric:tabular-nums }

/* ---------------------------------------------------------------
   SALARIES — one person, one period, what they are owed.
   His instruction, 2026-09-06.
   --------------------------------------------------------------- */
.sal-owedcell{ color:var(--acc) }
table.team tbody tr[data-who]{ cursor:pointer }
table.team tbody tr[data-who]:hover{ background:var(--acc-soft, #E6F3F1) }
/* THE PICKER — people as tiles, grouped by what they do. His instruction,
   2026-09-06: "a selection like we did on the products and services on the
   quick checkout", with categories by role. */
/* ONE TILE, USED IN MORE THAN ONE PLACE.
   His instruction, 2026-09-10: "on the calendar settings, make the Rooms boxes
   similar to the boxes in the Salaries page." So the Salaries tile became the
   shared one rather than being copied -- two copies drift, and the second one
   is always the one nobody remembers to fix.

   `.picktile`, NOT `.tile`: `.tile` is ALREADY the calendar day-strip filter
   button, defined further down this file and therefore winning on source
   order. The rooms boxes silently took its padding, its radius and its hover
   and looked almost right, which is the worst way for a collision to land.
   Third name collision in this project -- `SETUP`, `FACE_AREAS`, now this. */
/* The payroll picker: one card, each group a row with its label beside the
   tiles, so the whole team fits one screen. */
.sal-groups{ padding-top:2px; padding-bottom:2px }
.sal-group{ display:grid; grid-template-columns:150px minmax(0,1fr); gap:14px; align-items:start; padding:11px 0; border-bottom:1px solid var(--line-soft) }
.sal-group-h{ white-space:nowrap }
.sal-group:last-child{ border-bottom:none }
.sal-group-h{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:10.5px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--acc-dark); padding-top:16px; margin:0 }
.sal-group-h span{ color:var(--faint); font-weight:500 }
@media (max-width:700px){ .sal-group{ grid-template-columns:1fr; gap:6px } .sal-group-h{ padding-top:0 } }
.sal-tiles,.picktiles{ display:grid; grid-template-columns:repeat(auto-fill, minmax(210px, 1fr)); gap:9px }
.sal-tile,.picktile{
  display:flex; align-items:center; gap:11px; width:100%; text-align:left; cursor:pointer;
  padding:12px 13px; font:inherit; color:var(--ink);
  background:var(--surface-2); border:1px solid var(--line); border-radius:12px;
  transition:border-color 140ms ease, background-color 140ms ease, transform 120ms ease;
}
.sal-tile-b,.picktile-b{ display:block; min-width:0 }
.sal-tile b,.picktile b{ display:block; font-size:13.5px; font-weight:650;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.sal-tile .hint,.picktile .hint{ font-size:11.5px }
.sal-tile:hover,.picktile:hover{ border-color:var(--acc); background:var(--acc-soft, #E6F3F1) }
.sal-tile:active,.picktile:active{ transform:scale(.98) }
.sal-tile:focus-visible,.picktile:focus-visible{ outline:2px solid var(--acc); outline-offset:2px }
@media (prefers-reduced-motion:reduce){
  .sal-tile,.picktile{ transition:none } .sal-tile:active,.picktile:active{ transform:none }
}

/* A retired room is still readable -- quiet, never invisible. */
.picktile.off{ opacity:.62 }
.picktile.off:hover{ opacity:1 }
.picktile .picktile-go{ margin-left:auto; flex:none; opacity:.55 }
.picktile:hover .picktile-go{ opacity:1 }

.sal-people{ display:grid; grid-template-columns:repeat(auto-fill, minmax(240px, 1fr)); gap:10px }
.sal-who{
  display:flex; align-items:center; gap:11px; width:100%; text-align:left; cursor:pointer;
  padding:12px 13px; font:inherit; color:var(--ink);
  background:var(--surface); border:1px solid var(--line); border-radius:11px;
  transition:border-color 140ms ease, background-color 140ms ease, transform 120ms ease;
}
.sal-who-b{ display:block; min-width:0 }
.sal-who b{ display:block; font-size:13.5px; font-weight:650;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.sal-who:hover{ border-color:var(--acc); background:var(--acc-soft, #E6F3F1) }
.sal-who:active{ transform:scale(.99) }

.sal-head{ display:flex; align-items:center; gap:12px; margin:4px 0 14px }
.sal-head h3{ font-size:19px; font-weight:650; letter-spacing:-.015em }

.sal-tot{ display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:14px }
.sal-tot > div{ display:flex; flex-direction:column; gap:1px }
.sal-tot b{ font-size:22px; font-weight:700; letter-spacing:-.02em; font-variant-numeric:tabular-nums }
.sal-tot .sal-owed b{ color:var(--acc) }

.sal-pay{
  display:flex; justify-content:space-between; align-items:baseline;
  margin:16px 0 4px; padding:13px 15px; border-radius:11px;
  background:var(--acc-soft, #E6F3F1); border:1px solid var(--acc);
}
.sal-pay span{ font-size:14px; font-weight:650; color:var(--acc) }
.sal-pay b{ font-size:26px; font-weight:700; letter-spacing:-.02em; color:var(--acc);
  font-variant-numeric:tabular-nums }

@media (prefers-reduced-motion:reduce){
  .sal-who{ transition:none }
  .sal-who:active{ transform:none }
}
.sal-pick{ min-width:190px }
.sal-pick select{ width:100% }

/* Salaries: the two choices, in his order — person, then when. */
.sal-controls{ display:flex; flex-wrap:wrap; gap:22px; align-items:flex-start; margin-bottom:18px }
.sal-controls .sal-pick{ display:flex; flex-direction:column; gap:4px; min-width:230px }
.sal-controls .sal-pick > span{ font-size:12px; color:var(--muted) }
.sal-when{ display:flex; flex-direction:column; gap:4px }
.sal-when > .hint{ margin:0 }

/* A fee owed, and the decision to let it go. Loud enough that nobody at the
   desk misses it, quiet once it has been waived.

   NOT `.feebox` -- that name was already taken, by the small "$ [amount]"
   wrapper beside a cancellation rule in Settings. This rule sits later in the
   file, so it won, and a plain number input grew a cream background and a
   border. He spotted it: "why this looks yellow?" */
.feenotice {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; margin: 0 0 14px;
  border: 1px solid var(--gold); border-radius: 10px;
  background: var(--gold-soft); color: var(--ink); font-size: 13px; line-height: 1.45;
}
.feenotice span { flex: 1; }
.feenotice button { flex: none; }
.feenotice.off { border-color: var(--line); background: var(--surface-2); color: var(--muted); }
.pkline.gone .pkcount { color: var(--muted); font-style: italic; }

/* A US phone field, so it reads as one before anybody types.
   The +1 sits OUTSIDE the box: it is never part of what she gives you, and
   putting it in the value is how "+1 (858)..." ends up stored as the number. */
.phonebox {
  display: flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface); overflow: hidden;
  transition: border-color .16s ease, box-shadow .16s ease;
}
.phonebox .cc {
  display: flex; align-items: center; padding: 0 10px;
  background: var(--surface-2); border-right: 1px solid var(--line);
  color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums;
  user-select: none;
}
.phonebox input {
  flex: 1; min-width: 0; border: 0; border-radius: 0; background: none;
  font-variant-numeric: tabular-nums; letter-spacing: .2px;
}
.phonebox input:focus { outline: none; box-shadow: none; }
.phonebox:focus-within { border-color: var(--acc); box-shadow: 0 0 0 3px var(--acc-soft); }

/* Wrong, and said so quietly — the message itself comes on save. */
input.bad, .phonebox:has(input.bad) { border-color: var(--danger); }
.phonebox:has(input.bad) { box-shadow: none; }

/* ---------------------------------------------------------------
   WHAT THE VA CAN BOOK
   ---------------------------------------------------------------
   The first version put twenty-three treatments into five columns and
   truncated every one of them. Four different laser hair removals all read
   "Laser Hair Remov..." -- so the one control he actually has to operate could
   not tell him which thing he was taking away. The Medical pill then wrapped
   into the next column and broke the alignment of every row after it.

   So: FEWER, WIDER columns; the name allowed to wrap rather than truncate; and
   the pill pinned to the right edge of its own row, where it cannot push
   anything. */
.valist {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
  gap: 6px;
  margin-top: 4px;
}

.varow {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 13px 10px 15px;
  border: 1px solid var(--line-soft);
  border-radius: 10px;
  background: var(--surface);
  cursor: pointer;
  font-size: 13.5px; line-height: 1.35;
  transition: border-color 140ms ease, background-color 140ms ease, opacity 140ms ease;
}
/* The treatment's own calendar color, as a rail rather than a dot beside a
   checkbox -- two round things side by side read as two controls. */
.varow::before {
  content: ''; position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 3px; border-radius: 0 3px 3px 0;
  background: var(--tc, var(--acc));
  transition: opacity 140ms ease;
}
.varow:hover { border-color: var(--line); background: var(--surface-2); }
.varow:active { transform: scale(.994); }
.varow.off { background: var(--surface-2); }
.varow.off .vaname { color: var(--muted); }
.varow.off::before { opacity: .28; }
.varow.saving { opacity: .55; }

.varow .vaname {
  flex: 1; min-width: 0;
  overflow-wrap: anywhere;          /* wrap a long name; never truncate it */
  color: var(--ink);
}
.varow .vamed { flex: none; align-self: center; }

/* The native box is kept for the keyboard and for screen readers, and drawn
   over -- the OS blue is the one color on the page that belongs to no palette
   at all. */
.varow input[type="checkbox"] {
  position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0;
  pointer-events: none;
}
.varow .vabox {
  flex: none;
  width: 18px; height: 18px; border-radius: 5px;
  border: 1.5px solid var(--line);
  background: var(--surface);
  display: grid; place-items: center;
  color: #fff;
  transition: background-color 140ms ease, border-color 140ms ease;
}
.varow .vabox svg { opacity: 0; transform: scale(.7); transition: opacity 120ms ease-out, transform 120ms cubic-bezier(0.23, 1, 0.32, 1); }
.varow input:checked + .vabox { background: var(--acc); border-color: var(--acc); }
.varow input:checked + .vabox svg { opacity: 1; transform: scale(1); }
.varow input:focus-visible + .vabox { outline: 2px solid var(--acc); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .varow, .varow::before, .varow .vabox, .varow .vabox svg { transition: none; }
  .varow:active { transform: none; }
}

/* The roles list sits under the people list and reads the same way, so the
   second table needs no second explanation. */
table.team td.rl-blurb { color: var(--muted); font-size: 12.5px; }
#rolesCard .pill { margin-left: 7px; }

/* ---------------------------------------------------------------
   THE ROLE PAGE
   --------------------------------------------------------------- */

/* A role and one sentence about it is a HEADING, not a card with a body.
   It was a card-h and a card-b with a hairline between them -- two bands of
   chrome around eleven words. */
.rolehead { margin: 0 0 18px; padding: 2px 2px 0; }
.rolehead-t { display: flex; align-items: center; gap: 10px; }
.rolehead h2 {
  margin: 0; font-size: 21px; font-weight: 640;
  letter-spacing: -.016em; color: var(--ink);
}
.rolehead p {
  margin: 6px 0 0; color: var(--muted);
  font-size: 13px; line-height: 1.5; max-width: 70ch;
}

/* A segmented pair, so two related choices read as one control. Ghost buttons
   here rendered as two bare words -- nothing said they could be pressed. */
.seg {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface); overflow: hidden;
}
.seg button {
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: 6px 12px; font: inherit; font-size: 12.5px; font-weight: 550;
  color: var(--ink-2); white-space: nowrap;
  transition: background-color 140ms ease, color 140ms ease;
}
.seg button + button { border-left: 1px solid var(--line); }
.seg button:hover { background: var(--surface-2); color: var(--ink); }
.seg button:active { background: var(--hover); }
.seg button:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
.seg button:disabled { opacity: .5; cursor: default; }

/* Who has the role. A list of people, not a two-column table stretched the
   width of the page with a canyon between the name and the address. */
.wholist { display: flex; flex-direction: column; gap: 4px; }
.whorow {
  appearance: none; background: var(--surface); cursor: pointer;
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 11px; border: 1px solid transparent; border-radius: 10px;
  font: inherit; text-align: left; color: var(--ink);
  transition: background-color 140ms ease, border-color 140ms ease;
}
.whorow:hover { background: var(--surface-2); border-color: var(--line-soft); }
.whorow:active { transform: scale(.996); }
.whorow:focus-visible { outline: 2px solid var(--acc); outline-offset: -2px; }
.whorow.off { opacity: .6; }
.whorow .avt {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--acc-soft); color: var(--acc);
  font-size: 12.5px; font-weight: 650;
}
.whorow .whot { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.whorow .whot b { font-size: 13.5px; font-weight: 600; }
.whorow .whot .mono { font-size: 11.5px; color: var(--muted); }
.whorow .chev { flex: none; color: var(--faint); font-size: 17px; line-height: 1; }

@media (prefers-reduced-motion: reduce) {
  .seg button, .whorow { transition: none; }
  .whorow:active { transform: none; }
}

/* ---------------------------------------------------------------
   A YES AND A NO THAT BELONG TO EACH OTHER
   ---------------------------------------------------------------
   His report, 2026-09-07: "whenever I mark that a treatment needs a GFE it
   looks great on the treatment menu, when it is not required it is just
   showing a text says no."

   A filled teal pill against the bare word "no": one answer designed, the
   other left as a shrug. Both carry the same amount of information, so both
   get the same chip -- same height, same shape, same place in the row. Only
   the weight differs, which is what lets you read a column of them without
   reading any of them.

   The quiet one is an OUTLINE, not a second fill. A table of loud pills is as
   unreadable as a table of bare words. */
.state {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px 3px 8px;
  border: 1px solid transparent; border-radius: 20px;
  font-size: 11.5px; font-weight: 600; line-height: 1.5;
  white-space: nowrap; vertical-align: middle;
}
.state svg { flex: none; opacity: .85 }
.state.on      { background: var(--acc-soft); color: var(--acc); border-color: var(--acc-line) }
.state.neutral { background: var(--surface-2); color: var(--ink-2); border-color: var(--line); padding-left: 10px }
.state.off {
  background: transparent; color: var(--faint);
  border-color: var(--line); font-weight: 550;
}
.state.off svg { opacity: .6 }

/* WHAT THIS TREATMENT IS SOLD AS.
   It was a column in the treatments list, where it was the only cell carrying
   a paragraph -- so it set the height of every row in a table whose other
   columns are one chip each. His call, 2026-09-07: move it into the treatment,
   where there is room to read it. */
.soldas {
  margin: 0 0 16px; padding: 12px 14px;
  border: 1px solid var(--line-soft); border-radius: 11px;
  background: var(--surface-2);
}
.soldas-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap }
.soldas-h > span { color: var(--muted); font-size: 12.5px }
.soldas-l { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px }
.soldas-i {
  padding: 4px 10px; border-radius: 7px;
  background: var(--surface); border: 1px solid var(--line);
  font-size: 12.5px; color: var(--ink-2);
}

/* THE QUICK-CHECKOUT SQUARES, AS A COMPACT ORDERED LIST.
   His instruction, 2026-09-08: "make it look better and have it stacked near
   each other as much as you can to save space on the page."
   One row per square, one line each, hairlines rather than gaps — six squares
   in the height the old version used for three. */
.qplist{border:1px solid var(--line);border-radius:10px;overflow:hidden}
.qprow{
  display:flex;align-items:center;gap:10px;
  padding:0 10px 0 4px;min-height:34px;
  border-bottom:1px solid var(--line-soft);background:var(--surface);
}
.qprow:last-child{border-bottom:none}
.qprow:hover{background:var(--surface-2)}
/* Its position on the till, so the order reads without counting rows. */
.qpn{
  flex:none;width:18px;text-align:center;
  font-size:11px;font-weight:650;color:var(--faint);font-variant-numeric:tabular-nums;
}
.qpname{
  font-size:13px;font-weight:600;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* Pushed to the right so the prices line up down the column and the eye can
   run them without reading the names. */
.qpmeta{margin-left:auto;flex:none;font-size:12px;color:var(--muted);font-variant-numeric:tabular-nums}
.qpwarn{
  flex:none;font-size:10.5px;font-weight:600;color:var(--gold);
  background:var(--gold-soft);border-radius:99px;padding:2px 8px;
}
.qprow.off .qpname,.qprow.off .qpmeta{color:var(--faint)}
.qprow .iconbtn{flex:none;width:26px;height:26px}

/* DRAGGING ONE INTO PLACE.
   Pointer events rather than HTML5 drag-and-drop, because `dragstart` never
   fires on touch and this spa runs on iPads. */
.qpgrab{
  flex:none;display:grid;place-items:center;width:22px;height:30px;
  color:var(--line);cursor:grab;
  /* Without this the iPad scrolls the page instead of moving the row. */
  touch-action:none;
  transition:color 140ms ease;
}
.qprow:hover .qpgrab{color:var(--faint)}
.qpgrab:active{cursor:grabbing}
.qprow.dragging{
  background:var(--acc-soft);
  box-shadow:0 4px 14px rgba(16,24,40,.10);
  position:relative;z-index:3;
}
.qplist.dragging .iconbtn{pointer-events:none;opacity:.35}

/* ---------------------------------------------------------------
   THE HAND-OVER — her side of the sale
   ---------------------------------------------------------------
   His instruction, 2026-09-09, after the shape of his Membership app: build
   the package, sign the policy, photograph the ID, keep anything else.

   Sized for AN IPAD HELD BY A CLIENT, not for a consultant's laptop: bigger
   type, bigger targets, one thing on screen at a time. The 44px floor is the
   same one `crm_intake_check` enforces on the intake form, and for the same
   reason — this is the other screen a client actually touches.
   --------------------------------------------------------------- */
.ho-wrap{ z-index:46 }                 /* above the till, below the modal */
.ho-modal{ max-width:760px }

/* The steps across the top. His app's "1 Package · 2 Policy · 3 Photo ID". */
.ho-steps{
  display:flex; flex-wrap:wrap; gap:6px; align-items:center;
  padding:10px 18px 12px; border-bottom:1px solid var(--line-soft);
  background:var(--surface-2);
}
.ho-stp{
  display:inline-flex; align-items:center; gap:7px;
  font-size:12.5px; font-weight:600; color:var(--faint);
  padding:5px 11px 5px 5px; border-radius:999px;
}
.ho-stp .num{
  display:inline-flex; align-items:center; justify-content:center;
  width:21px; height:21px; border-radius:50%;
  background:var(--line); color:var(--muted);
  font-size:11.5px; font-weight:650;
}
.ho-stp.on{ color:var(--ink); background:var(--acc-soft) }
.ho-stp.on .num{ background:var(--acc); color:#fff }
/* Done is QUIET. A row of filled badges is as unreadable as a row of bare
   words — the same rule the yes/no chips follow. */
.ho-stp.ok{ color:var(--muted) }
.ho-stp.ok .num{ background:transparent; color:var(--acc); box-shadow:inset 0 0 0 1.5px var(--acc-line) }

.ho-why{
  margin:0 0 12px; font-size:13px; color:var(--gold);
  background:var(--gold-soft); border-radius:10px; padding:9px 12px;
}
.ho-lead{ margin:0 0 14px; font-size:15px; line-height:1.55; color:var(--ink-2) }

/* The wording she reads. It SCROLLS rather than being cut off — the house
   rule — and it is deliberately tall enough that scrolling is obvious. */
/* AN UPLOADED DOCUMENT INSIDE THE HAND-OVER.
   The same pages her own record draws, on the iPad she is holding. It scrolls
   in a box of its own so a two-page consent cannot push the name field, the
   signature and the buttons off the bottom of the window. */
.ho-doc{
  max-height:46vh; overflow:auto;
  border:1px solid var(--line); border-radius:10px;
  padding:8px; background:var(--surface-2);
}
.ho-doc .doc-page{ margin:0 auto 8px }
.ho-doc .doc-page:last-child{ margin-bottom:0 }

.ho-paper{
  max-height:38vh; overflow-y:auto; -webkit-overflow-scrolling:touch;
  border:1px solid var(--line); border-radius:12px;
  padding:16px 18px; background:var(--surface-2);
  font-size:14.5px; line-height:1.65; color:var(--ink-2);
}
.ho-paper p{ margin:0 0 10px }
.ho-paper p:last-child{ margin-bottom:0 }
.ho-acks{ margin-top:14px; display:flex; flex-direction:column; gap:9px }
.ho-acks .chk{ font-size:14.5px; line-height:1.5 }
.ho-ackhint{ margin:10px 0 0 }
/* The name box needs real air under the acknowledgements, or the hint above it
   and the label below it read as one run-on line. `.field` is one class and so
   is this, so this has to come LATER in the file to win — it does, and the
   demo screenshots are what showed 18px was still not enough. */
.ho-namefield{ margin-top:26px }
.ho-namefield > label{ margin-bottom:6px }

/* Where a photo goes before there is one. */
.ho-drop{
  border:2px dashed var(--line); border-radius:14px;
  padding:34px 20px; text-align:center; background:var(--surface-2);
}
.ho-drop .icon{ font-size:34px; line-height:1 }
.ho-drop .t{ margin-top:10px; font-weight:650; color:var(--ink) }
.ho-drop .s{ margin-top:4px; font-size:13px; color:var(--muted) }

.ho-extras{ display:flex; flex-direction:column; gap:8px; margin-top:4px }
.ho-ex{
  display:flex; align-items:center; gap:8px;
  border:1px solid var(--line); border-radius:10px; padding:9px 12px;
  font-size:14px; background:var(--surface);
}

/* A file input that looks like a button. The input covers the whole control so
   the tap target is the button, not a 1px sliver of native chrome. */
.filebtn{ position:relative; overflow:hidden; cursor:pointer; min-height:44px }
.filebtn input[type=file]{
  position:absolute; inset:0; opacity:0; cursor:pointer;
  font-size:200px;                       /* Safari sizes the tap area from this */
}

.ho-done{ text-align:center; padding:26px 10px 10px }
.ho-done .tickbig{
  display:inline-flex; align-items:center; justify-content:center;
  width:56px; height:56px; border-radius:50%;
  background:var(--acc-soft); color:var(--acc); margin-bottom:12px;
}
.ho-done .tickbig svg{ width:26px; height:26px }
.ho-done h3{ margin:0 0 6px; font-size:19px }

/* SHE IS NOT HERE. A quiet outline, never a second filled button beside
   "Hand over to sign" — two loud buttons make neither of them the answer. */
.till-phone{ margin-top:10px }
.till-phone-why{ margin:6px 0 0; text-align:center }

/* An iPad held in a hand, and a phone at the desk. Nothing is cut off; the
   window grows and the body scrolls. */
@media (max-width:820px){
  .ho-modal{ max-width:none; width:100%; height:100%; max-height:100%; border-radius:0 }
  .ho-paper{ max-height:34vh }
  .ho-steps{ padding:8px 12px 10px }
  .ho-stp{ font-size:11.5px; padding:4px 9px 4px 4px }
  .ho-lead{ font-size:14.5px }
}

/* Sales tax on the quick till: named, and quieter than the total it is part
   of. A front desk that cannot see WHY the figure is $216.10 rather than $200
   has no way to answer the client who asks. */
.qtotal.qtax{ font-size:13px; color:var(--muted); border-top:0; padding-bottom:0 }
.qtotal.qtax b{ font-weight:600; color:var(--ink-2) }

/* ---------------------------------------------------------------
   SETTING THE SPA UP
   ---------------------------------------------------------------
   A LIST, NOT A BANNER. It sits above the day's work because until the spa
   has rooms and a menu there is no day's work — and it removes itself the
   moment nothing is outstanding, because a banner that never goes away is a
   banner nobody reads.

   Gold, like the other things that are a JOB rather than an emergency: an
   empty spa is not broken, it is new. Red here would be the CRM telling a
   brand-new owner that something has gone wrong on their first morning.
   --------------------------------------------------------------- */
.setup-card{
  border:1px solid var(--gold); background:var(--gold-soft);
  border-radius:var(--r-lg); padding:14px 16px; margin-bottom:14px;
}
.setup-h{ display:flex; align-items:center; gap:12px; margin-bottom:10px }
/* `.sp` is a spacer only where somebody has said so — there is no global rule
   for it, so "Hide for now" sat against the subtitle instead of at the far
   right. Visible only in a picture of the card. */
.setup-h .sp{ flex:1 }
.setup-h b{ display:block; font-size:14px; color:var(--ink) }
.setup-h .hint{ display:block; margin-top:2px }

.setup-rows{ display:flex; flex-direction:column; gap:8px }
.setup-row{
  display:flex; align-items:center; gap:12px;
  background:var(--surface); border:1px solid var(--line);
  border-radius:var(--r-md); padding:10px 12px;
}
.setup-dot{
  flex:none; width:9px; height:9px; border-radius:50%;
  background:var(--gold);
}
.setup-txt{ flex:1; min-width:0 }
.setup-txt b{ display:block; font-size:13.5px; font-weight:650; color:var(--ink) }
/* NOTHING IS EVER CUT OFF — the reason wraps rather than being trimmed to fit,
   which is the whole point of saying why it matters. */
.setup-txt span{ display:block; font-size:12.5px; color:var(--muted); line-height:1.5 }
.setup-act{ flex:none; display:flex; align-items:center; gap:6px }

.setup-aside{
  display:flex; align-items:center; flex-wrap:wrap; gap:7px;
  margin-top:12px; padding-top:10px; border-top:1px solid var(--gold);
}

/* A narrow screen stacks the buttons under the reason rather than squeezing
   three of them into a column 90px wide. */
@media (max-width:860px){
  .setup-row{ flex-wrap:wrap }
  .setup-act{ width:100%; justify-content:flex-end }
}


/* WHO SHE IS SIGNING WITH.
   His question, 2026-09-10: "what about the package receipt we have the client
   sign, that can also be used for a receipt isn't it?" It can, and it is the
   copy carrying her signature -- so the screen she signs has to name the spa.
   Quiet by design: it identifies the document without competing with the
   amount she is agreeing to. */
.sigspa{
  display:flex; flex-direction:column; gap:1px;
  padding:0 0 10px; margin:0 0 12px;
  border-bottom:1px solid var(--line);
}
.sigspa b{ font-size:13.5px; font-weight:650 }
.sigspa .hint{ font-size:11.5px }

.ho-spa{ padding:0 var(--modal-pad, 20px) }
.ho-spa:empty{ display:none }
.ho-spa .sigspa{ padding-top:10px; margin-bottom:0 }


/* The consent row's rules live in the page's own <style>, beside the rest of
   the appointment panel -- see `.cons-row` in index.html. They were added here
   too on 2026-09-10 and the page won, so the stylesheet was edited and nothing
   happened. `test/design.test.js` caught it; that check is the reason this
   comment is a pointer rather than a second copy. */


/* HOW DID SHE DO IT. Three equal answers, so three equal buttons -- none of
   them is the "right" one, and making one primary would be a nudge toward
   recording something that did not happen. */
.howgrid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(120px, 1fr)); gap:9px }
.howgrid .btn.how{ justify-content:center; padding:14px 12px; font-size:13.5px }


/* AN ADD-ON UNDER ITS TREATMENT.
   His report, 2026-09-11: an add-on was reading as a second treatment on her
   record. Indented and quieter, with its own session count beside it, because
   five treatments can legitimately carry three dermaplanings. */
.pkg-l.pkg-add .pkg-ln{ padding-left:14px; color:var(--ink-2); font-size:12.5px }
.pkg-l.pkg-add .pkg-c{ color:var(--ink-2) }
.pkg-l.pkg-add .pkg-bar i{ transform:scale(.82) }


/* A SIGNED DOCUMENT, READ BACK.
   His report, 2026-09-11: opening a signed policy showed the form's wiring
   rather than the policy. It shows the pages now, with what was signed painted
   into the boxes -- and nothing on it is editable, because a signed document
   is evidence, not a form. */
.doc-signed{ display:grid; gap:10px; justify-items:center }
.doc-signed .doc-page{ max-width:100%; box-shadow:0 1px 3px rgba(0,0,0,.10) }
/* The read-back rules live in the page beside the ones they beat -- see
    in index.html. */
@media print{
  .doc-signed .doc-page{ box-shadow:none; break-inside:avoid }
}


/* THE SERVER IS BEHIND THE SCREENS.
   Loud on purpose, and the only loud thing in the app: everything else here is
   quiet by house rule, because everything else is a job rather than a warning
   that what you are looking at may not be true. Gold, not red -- nothing is
   broken and no client is at risk; the app simply cannot be trusted to be
   telling you what it normally would. */
/* HIDDEN MEANS HIDDEN.
   Setting `display:flex` on an element that uses the `hidden` attribute BEATS
   the browser's own `[hidden]{display:none}` -- so this bar sat at the top of
   his spa as a 19px empty gold strip, all day, with the attribute correctly
   set and the element correctly empty.
   He asked what it was three times. I checked the server, shipped four fixes,
   blamed his browser cache, and had him open a private window. It was one
   missing line of CSS, and a DOM scan found it in seconds -- which is what I
   should have run the first time he asked. */
.staleband[hidden]{ display:none }
.staleband{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:9px 16px;
  background:var(--gold-soft);
  border-bottom:1px solid var(--gold);
  color:var(--gold);
  font-size:12.8px; line-height:1.4;
}
.staleband b{ font-weight:650 }
.staleband .what{ flex:1 1 320px; min-width:0 }
.staleband .how{ color:inherit; opacity:.85 }

/* ---- PAST DUE, and the button that fixes it ---------------------------
   His instruction, 2026-09-15: "show a warning message that says the payment
   is past due and they will lose access in 7 days ... and also do a button on
   the warning they can click to make that payment."

   `[hidden]` FIRST, and on its own line. A class that sets `display` beats
   the browser's own `[hidden]{display:none}`, which is exactly how an empty
   gold strip sat across the top of his spa for a day. Same trap, third time
   it has been written down; the lint in crm_pointer_check reads the source
   and fails on any class that can defeat `hidden`. */
.billband[hidden]{ display:none }
.billband{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:10px 16px;
  background:var(--gold-soft);
  border-bottom:1px solid var(--gold);
  color:var(--gold);
  font-size:13px; line-height:1.45;
}
.billband .what{ flex:1 1 320px; min-width:0; font-weight:600 }
/* The last two days are the only time this stops being a note and starts
   being a deadline. Red, because by then it is one. */
.billband.soon{
  background:var(--danger-soft);
  border-bottom-color:var(--danger);
  color:var(--danger);
}
.billband .pay{
  flex:none;
  background:var(--gold); color:#fff;
  border:1px solid var(--gold);
  border-radius:8px; padding:6px 13px;
  font-size:12.5px; font-weight:650; text-decoration:none;
  transition:transform 140ms ease-out, filter 140ms ease-out;
}
.billband.soon .pay{ background:var(--danger); border-color:var(--danger) }
/* Unconditional. A hover is NEVER behind a media query in this file. */
.billband .pay:hover{ filter:brightness(1.08) }
.billband .pay:active{ transform:scale(.97) }


/* ---- APPROVED DEVICES, and who signed in ------------------------------
   His choice, 2026-09-15. A row per browser: what it is, whose it is, and
   the decision. Not a table, because the middle column is a sentence and the
   last is two buttons -- the shape the consent list already uses. */
.devrow{
  display:flex;align-items:center;gap:12px;
  padding:13px 18px;border-bottom:1px solid var(--line-soft);
}
.devrow:last-of-type{border-bottom:none}
.devrow.off{opacity:.6}
.devi{flex:none;color:var(--muted);display:grid;place-items:center}
.devwho{flex:1;min-width:0}
.devwho b{font-size:13.5px;font-weight:650;display:block;overflow-wrap:anywhere}
.devwho .m{font-size:11.5px;color:var(--faint);margin-top:2px;display:block;overflow-wrap:anywhere}
.devwhen{font-size:12px;color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums}
@media (max-width:700px){
  /* Nothing is ever cut off: on a narrow screen the row becomes two lines
     rather than squeezing the browser's name to nothing. */
  .devrow{flex-wrap:wrap}
  .devwho{flex:1 0 100%;order:-1}
}

/* ---- YOUR ACCOUNT ------------------------------------------------------
   His instruction, 2026-09-16: a way to set your own password. Every role
   has one, so this is the one window in Settings-shaped clothing that no
   capability gates. */
.acct-head{display:flex;align-items:center;gap:12px;padding-bottom:14px;margin-bottom:4px;
  border-bottom:1px solid var(--line-soft)}
.acct-name{font-size:15.5px;font-weight:650;color:var(--ink);
  overflow-wrap:anywhere}
.acct-sub{font-size:12.5px;color:var(--muted);margin-top:1px;overflow-wrap:anywhere}
.avatar.big{width:40px;height:40px;font-size:14px;flex:none}
