/* LUNOX ZP pilot — light, KumoDoc-inspired theme. Brand #470054. */
:root {
  --brand: #470054;
  --brand-dark: #33003d;
  --brand-light: #6a2178;
  --brand-tint: #f5ecf7;
  --brand-tint-2: #ede0f1;
  --bg: #f4f2f7;
  --card: #ffffff;
  --text: #2a1f2e;
  --muted: #7a6c80;
  --line: #e9e2ec;
  --ok: #1a7a3c;      --ok-bg: #e3f4e8;
  --warn: #9a5b00;    --warn-bg: #fdf0dc;
  --bad: #b3261e;     --bad-bg: #fde7e5;
  --info: #0b5fa5;    --info-bg: #e3effa;
  --neutral-bg: #efeaf2;
  --shadow: 0 2px 12px rgba(71, 0, 84, .07);
  --shadow-lift: 0 8px 24px rgba(71, 0, 84, .13);
  /* mattress material colours (always paired with a printed name) */
  --mat-latex: #f6f0da;
  --mat-firm-latex: #e4cf8e;
  --mat-memory: #d4ecd0;
  --mat-soft: #f8e9a8;
  --mat-medium: #e0d2f0;
  --mat-hard: #d2e5f7;
  --radius: 16px;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font: 15px/1.5 "Segoe UI", system-ui, sans-serif;
  background: var(--bg); color: var(--text);
  min-height: 100vh;
}
a { color: var(--brand); }
h1 { font-size: 1.4rem; margin: 0 0 .8rem; font-weight: 700; }
h2 { font-size: 1.05rem; margin: 1rem 0 .5rem; font-weight: 700; }
h3 { font-size: .95rem; margin: .75rem 0 .35rem; font-weight: 700; }

/* ---- layout: light sidebar, airy content ---- */
.shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 232px; flex: 0 0 232px; background: var(--card); color: var(--text);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
  overflow-y: auto; border-right: 1px solid var(--line);
}
.sidebar .logo { padding: 18px 16px 12px; }
.sidebar .logo .logo-mark { display: none; }
.login-brand {
  font-size: 1.05rem; font-weight: 800; letter-spacing: .42em;
  color: var(--brand, #470054); margin: 6px 0 2px;
}
.sidebar .brandname {
  font-size: .72rem; font-weight: 800; letter-spacing: .34em;
  color: var(--brand, #470054); opacity: .85; margin: 4px 0 2px; padding-left: 2px;
}
.login-brand {
  font-size: 1.05rem; font-weight: 800; letter-spacing: .42em;
  color: var(--brand, #470054); margin: 6px 0 2px;
}
.sidebar .brandname { display: none; }
/* The rail toggle sits at the foot of the menu, out of the way of the links. */
.rail-btn {
  align-self: flex-end; margin: 0 10px 12px;
  width: 26px; height: 26px; min-height: 0; padding: 0; border-radius: 50%;
  background: var(--brand-tint); color: var(--brand); border: 1px solid var(--line);
  font-size: .9rem; line-height: 1; box-shadow: none; flex: none;
}
@media (hover: hover) { .rail-btn:hover { background: var(--brand-tint-2); box-shadow: none; } }

/* ---- collapsed rail (Dr Daniel, 2026-08-24) ----
   Icons only, with the labels arriving on hover. The sidebar goes FIXED and the
   page is offset by the rail's width, so opening the flyout draws over the
   content instead of shoving it — a menu that moves the screen every time the
   mouse drifts past is worse than one that is simply wide. */
.shell.rail .sidebar {
  position: fixed; top: 0; left: 0; height: 100vh; z-index: 40;
  width: var(--rail-w, 64px); overflow-x: hidden;
  transition: width .16s ease, box-shadow .16s ease;
}
.shell.rail .main { margin-left: var(--rail-w, 64px); }
.shell.rail .sidebar:focus-within {
  width: 232px;
  box-shadow: 14px 0 34px rgba(43, 15, 51, .16);
}
@media (hover: hover) { .shell.rail .sidebar:hover {
  width: 232px;
  box-shadow: 14px 0 34px rgba(43, 15, 51, .16);
} }
/* Collapsed: the wordmark gives way to the round mark, and the branch line and
   footer note have nowhere to go. */
.shell.rail .sidebar .logo { padding: 14px 0 10px; text-align: center; }
.shell.rail .sidebar .logo img { display: none; }
.shell.rail .sidebar .logo .logo-mark {
  display: inline-block; width: 34px; height: 34px; margin: 0 auto;
}
.shell.rail .sidebar > .sub,
.shell.rail .sidebar .foot { opacity: 0; pointer-events: none; }
.shell.rail .sidebar nav { padding: 6px 8px; }
.shell.rail .rail-btn { align-self: center; margin: 0 0 12px; }
.shell.rail .sidebar:focus-within .rail-btn { align-self: flex-end; margin: 0 10px 12px; }
@media (hover: hover) { .shell.rail .sidebar:hover .rail-btn { align-self: flex-end; margin: 0 10px 12px; } }
.shell.rail .sidebar nav a { justify-content: center; padding: 10px 0; }
.shell.rail .sidebar nav a .lbl {
  opacity: 0; width: 0; overflow: hidden; white-space: nowrap;
  transition: opacity .12s ease;
}
/* …and back again when the flyout opens. */
.shell.rail .sidebar:focus-within .logo { padding: 18px 16px 12px; text-align: left; }
@media (hover: hover) { .shell.rail .sidebar:hover .logo { padding: 18px 16px 12px; text-align: left; } }
.shell.rail .sidebar:focus-within .logo img { display: block; }
@media (hover: hover) { .shell.rail .sidebar:hover .logo img { display: block; } }
.shell.rail .sidebar:focus-within .logo .logo-mark { display: none; }
@media (hover: hover) { .shell.rail .sidebar:hover .logo .logo-mark { display: none; } }
.shell.rail .sidebar:focus-within > .sub, .shell.rail .sidebar:focus-within .foot { opacity: 1; pointer-events: auto; }
@media (hover: hover) { .shell.rail .sidebar:hover > .sub, .shell.rail .sidebar:hover .foot { opacity: 1; pointer-events: auto; } }
@media (hover: hover) { .shell.rail .sidebar:hover nav { padding: 6px 10px 10px; } }
.shell.rail .sidebar:focus-within nav a { justify-content: flex-start; padding: 9px 12px; }
@media (hover: hover) { .shell.rail .sidebar:hover nav a { justify-content: flex-start; padding: 9px 12px; } }
.shell.rail .sidebar:focus-within nav a .lbl { opacity: 1; width: auto; }
@media (hover: hover) { .shell.rail .sidebar:hover nav a .lbl { opacity: 1; width: auto; } }

/* On a phone the shell already stacks; a fixed rail there would just cover the
   page, so the collapse is desktop-only. */
@media (max-width: 900px) {
  /* A complete no-op, not a partial one (2026-09-26): the 100vh height pushed
     the page a screen down, and the :hover / :focus-within flyout (232px,
     animated) snapped the header narrow on every tap — that was the phone
     "flicker". The three selectors match the flyout's specificity so they
     win by order; they are resets, so they stay outside the hover gate. */
  .shell.rail .sidebar,
  .shell.rail .sidebar:hover,
  .shell.rail .sidebar:focus-within {
    position: sticky; width: 100%; height: auto; box-shadow: none; transition: none;
  }
  .shell.rail .main { margin-left: 0; }
  .rail-btn { display: none; }
  /* …and the collapsed lockup gives way to the ordinary phone header. */
  .shell.rail .sidebar .logo { padding: 18px 16px 12px; text-align: left; }
  .shell.rail .sidebar .logo img { display: block; }
  .shell.rail .sidebar .logo .logo-mark { display: none; }
  .shell.rail .sidebar > .sub, .shell.rail .sidebar .foot { opacity: 1; pointer-events: auto; }
  .shell.rail .sidebar nav { padding: 6px 10px 10px; }
  .shell.rail .sidebar nav a { justify-content: flex-start; padding: 7px 10px; }
  .shell.rail .sidebar nav a .lbl { opacity: 1; width: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .shell.rail .sidebar, .shell.rail .sidebar nav a .lbl { transition: none; }
}
/* aspect-ratio reserves the logo's box BEFORE the file arrives (1770x543 as
   drawn). Without it the unloaded <img> lays out its alt text instead, the
   menu is briefly ~200px taller, and anything measured against it in that
   moment — the restored scroll offset — lands in the wrong place. */
.sidebar .logo img { display: block; width: 100%; max-width: 168px; height: auto; aspect-ratio: 1770 / 543; }
.sidebar > .sub {
  font-size: .68rem; color: var(--muted); letter-spacing: .1em; margin-top: -4px;
  font-weight: 600; padding: 0 16px 12px;
}
.sidebar nav { flex: 1; padding: 6px 10px 10px; }
.sidebar nav a {
  display: flex; gap: 11px; align-items: center; color: var(--muted); text-decoration: none;
  padding: 9px 12px; font-size: .9rem; border-radius: 10px; margin: 1px 0; font-weight: 500;
  transition: background .12s, color .12s;
}
@media (hover: hover) { .sidebar nav a:hover { background: var(--brand-tint); color: var(--brand); } }
.sidebar nav a.active { background: var(--brand); color: #fff; font-weight: 600; box-shadow: var(--shadow); }
/* An icon standing in for what used to be an emoji (2026-08-28). It sits
   INLINE with its label, so it scales with whatever type surrounds it and
   lands on the baseline instead of shoving the line box around.
   The svg is given a real size HERE rather than `width:100%` on a nested
   span: a span inside an unsized parent has no width of its own to resolve
   against, so the svg fell back to its intrinsic default and the icons went
   all over the place the first time this shipped. */
.ic {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 1.15em; height: 1.15em;
  vertical-align: -0.17em;
}
.ic > span { display: contents; }
.ic svg { width: 1.15em; height: 1.15em; display: block; }
/* No margin on the icon: buttons are flex rows with their own gap:7px, and an
   extra margin just made that 8.8px. Inline uses (a table cell, a sentence)
   get their space from the label text itself. */

/* The sidebar keeps its own fixed size: those icons sit in a rail, not in a
   sentence, and must not shrink with the label text. */
.sidebar nav a .ic { width: 21px; height: 21px; }
.sidebar nav a .ic svg { width: 21px; height: 21px; }
.sidebar .foot {
  padding: 10px 16px 14px; font-size: .7rem; color: var(--muted);
  border-top: 1px solid var(--line);
}

/* ---- the sidebar wears the branch's colour (2026-08-28) ----------------
   Same four colours as the branch switcher's chips, so the two read as one
   idea: ZP brand purple, 8TR teal, RU amber, CS blue. Only the SIDEBAR is
   re-tinted — the rest of the app stays brand purple, because a whole screen
   changing colour on a branch switch is alarming rather than informative.
   ZP needs no rule: it is the brand, and the default. */
/* No coloured edge: a 2px rule in the branch colour read as a black line down
   the screen at ZP, whose brand purple is nearly black (Dr Daniel, 2026-08-28).
   The branch already shows in the active nav item, the hover tint and the lit
   chip, which is enough of a cue without drawing a bar beside the content. */
body[data-branch="8TR"] .sidebar {
  --brand: #0e7490; --brand-light: #1596b5;
  --brand-tint: #e6f4f8; --brand-tint-2: #d3ebf2;
}
body[data-branch="RU"] .sidebar {
  /* Deep rose (Dr Daniel, 2026-09-06: the brown was "so ugly"). */
  --brand: #a13d63; --brand-light: #c9628e;
  --brand-tint: #f9edf3; --brand-tint-2: #f3dbe7;
}
body[data-branch="CS"] .sidebar {
  --brand: #0b5fa5; --brand-light: #1a7bc9;
  --brand-tint: #e8f1fa; --brand-tint-2: #d5e5f5;
}

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  background: var(--card); border-bottom: 1px solid var(--line);
  padding: 10px 22px; display: flex; align-items: center; gap: 14px;
  position: sticky; top: 0; z-index: 10;
}
.topbar .pagetitle { font-weight: 700; font-size: 1.05rem; }
.topbar .who {
  margin-left: auto; display: flex; align-items: center; gap: 9px;
  background: var(--bg); border-radius: 999px; padding: 4px 14px 4px 4px;
  font-size: .82rem; color: var(--muted);
}
.topbar .avatar {
  width: 30px; height: 30px; border-radius: 50%; background: var(--brand); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: .85rem; flex: none;
}
.topbar .iconbtn {
  width: 38px; height: 38px; min-height: 0; padding: 0; border-radius: 50%;
  background: var(--brand-tint); color: var(--brand); border: none; flex: none;
}
@media (hover: hover) { .topbar .iconbtn:hover { background: var(--brand-tint-2); } }
.topbar .iconbtn svg { width: 19px; height: 19px; }
/* The topbar has to survive a 375px phone (Dr Daniel, 2026-08-29: the name chip
   and the log-out button ran off the right edge). Flex items refuse to shrink
   below their content by default, so every one of them is given permission to,
   and the least useful text is dropped first: the page title ellipses, then the
   role goes, then the name — leaving the avatar and the log-out button, which
   never shrink because they are the two things you always need. The environment
   tag keeps its place in the queue ahead of the title -- it may only ever
   truncate, never disappear, because nobody may miss that they are on a copy;
   its full text stays reachable as a tooltip. */
.topbar > * { min-width: 0; }
.topbar .pagetitle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.envtag {
  background: var(--bad-bg, #fde8e6); color: var(--bad, #a3352c); font-weight: 700;
  padding: 4px 12px; border-radius: 999px; font-size: .8rem; letter-spacing: .03em;
  white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.topbar .who { min-width: 0; }
.topbar .who-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.topbar .who-role { white-space: nowrap; flex: none; }
.topbar .who-role::before { content: '\00b7'; margin: 0 5px; }
@media (max-width: 900px) {
  .topbar { padding: 8px 14px; gap: 10px; }
  .topbar .who-role { display: none; }
}
@media (max-width: 560px) {
  .topbar .who { padding: 3px; }
  .topbar .who-name { display: none; }
  /* The sidebar already shows which page is open, so the title is the one
     thing here that carries no information a phone user does not have. */
  .topbar .pagetitle { display: none; }
}

/* The page sits in the middle of whatever room it has, and takes more of it
   on a wide monitor (Dr Daniel, 2026-09-13: "a wide monitor, the right side
   of the screen is empty"). The 1320px cap hugged the LEFT edge, so a 2560px
   screen showed a third of itself blank; boards and registers here are
   tables, and a table with room to breathe beats a fixed measure. The
   calendar has always gone full-bleed — this brings the rest in line. */
.content { padding: 20px 22px; max-width: 1320px; width: 100%; margin-inline: auto; }
@media (min-width: 1500px) { .content { max-width: 1760px; } }
@media (min-width: 2000px) { .content { max-width: none; } }
/* The calendar page trims its top padding (Dr Daniel, 2026-09-06: "These
   up-down gap make it narrower") — the frozen toolbar carries its own. */
.content:has(.cal-full) { padding-top: 4px; }
/* The factory portal hugs the top the same way (Dr Daniel, 2026-09-06:
   "The gap above factory portal row make it smallest"). */
.content:has(.fo-filters) { padding-top: 2px; }

/* ---- components ---- */
.card {
  background: var(--card); border: none; border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 14px; box-shadow: var(--shadow);
}
.grid { display: grid; gap: 12px; }
/* Wider tiles and a figure that can shrink (2026-08-28): a year-to-date of
   RM5.2M is eight characters more than the tile was built for, and the old
   fixed 1.5rem pushed it straight out of the card. */
.kpis { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
/* Two rows with a shape of their own (Dr Daniel, 2026-09-14: "1,2,3... one
   row / abc... one row"): the money across the top, the day's traffic under
   it. They keep their rows on a wide screen and wrap on a narrow one rather
   than squeezing six tiles into a phone. */
/* Four money chips a row, two rows of them (Dr Daniel, 2026-09-14: "no need
   squeeze all of them in one row dy, but the same category shall be same
   size") — every chip in a row the same width and the same height, which a
   grid of equal tracks gives for nothing. Eight across only on a screen wide
   enough that a chip is still wider than the longest ringgit figure. */
.kpis-money { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.kpis-day { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (min-width: 1900px) { .kpis-money { grid-template-columns: repeat(8, minmax(0, 1fr)); } }
/* The money keeps ONE row down to a 1100px screen and the day's traffic keeps
   ONE row down to 1300 (Dr Daniel, 2026-09-14: "make them in one row too").
   That is only possible because each chip is built to its own content: the
   money chips are the wide ones — a figure of eleven characters that must
   never break mid-number — and the counts are the narrow ones. */
/* ONE shape for both rows (Dr Daniel, 2026-09-14: "I want the chips and gaps
   to be like this!! First row also wan same thickness as this!!!"). Same
   padding, same icon, same three single lines — the only way two independent
   grids come out the same thickness. Each selector names the chip as well as
   the row: .kpi-fun sizes the icon and the figure at the same specificity and
   further down the file, so a plainer selector here would lose to it in
   silence. */
.kpis-money, .kpis-day { gap: 14px; margin-bottom: 14px; }
.kpis-money > .kpi, .kpis-day > .kpi { margin-bottom: 0; }
/* Taller chips with the words closer together (Dr Daniel, 2026-09-14: "make
   it taller and slighly shorter... the gap btween the word's rows make it
   narrower") — the height comes from the padding, not from air between the
   lines. */
.kpis-money .kpi-fun, .kpis-day .kpi-fun { padding: 15px 12px; gap: 10px; }
.kpis-money .kpi-fun .v, .kpis-day .kpi-fun .v { line-height: 1.1; }
.kpis-money .kpi-fun .l, .kpis-day .kpi-fun .l { margin-top: 0; line-height: 1.2; }
.kpis-money .kpi-cmp, .kpis-money .kpi-spent,
.kpis-day .kpi-spent, .kpis-day .kpi-cmp { margin-top: 1px; }
.kpis-money .kpi-fun .kpi-ic, .kpis-day .kpi-fun .kpi-ic {
  font-size: 1.02rem; width: 31px; height: 31px; flex: 0 0 31px; border-radius: 10px;
}
.kpis-money .kpi-fun .v, .kpis-day .kpi-fun .v { font-size: 1.28rem; }
.kpis-money .kpi-fun .l, .kpis-day .kpi-fun .l { font-size: .71rem; margin-top: 1px; }
/* Six money chips of about a sixth of the page: below 1700 that is under
   240px, and "RM16,281.20" has to fit beside its icon inside it. */
@media (max-width: 1700px) {
  .kpis-money .kpi-fun .v, .kpis-day .kpi-fun .v { font-size: 1.18rem; }
}
@media (max-width: 1300px) {
  /* Narrower chips, but no shorter: the padding that makes them tall is the
     last thing a smaller screen should take away (Dr Daniel, 2026-09-14). */
  .kpis-money .kpi-fun, .kpis-day .kpi-fun { padding: 15px 10px; gap: 9px; }
  .kpis-money .kpi-fun .kpi-ic, .kpis-day .kpi-fun .kpi-ic {
    font-size: .94rem; width: 27px; height: 27px; flex: 0 0 27px; border-radius: 9px;
  }
  .kpis-money .kpi-fun .v, .kpis-day .kpi-fun .v { font-size: 1.12rem; }
  .kpis-money .kpi-fun .l, .kpis-day .kpi-fun .l { font-size: .67rem; }
  .kpis-money .kpi-cmp, .kpis-money .kpi-spent, .kpis-day .kpi-spent { font-size: .64rem; }
}
/* The label is one line, always: what will not fit is cut and said in full on
   hover. The line under it may take two — "Showed up 9 · RM4,580.00" does not
   fit a seventh of the screen in one — but never three, so both rows come out
   the same thickness. */
.kpis-money .kpi-fun .l, .kpis-day .kpi-fun .l {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpis-money .kpi-cmp, .kpis-money .kpi-spent, .kpis-day .kpi-spent, .kpis-day .kpi-cmp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; white-space: normal; line-height: 1.25;
  /* Room for two lines whether or not this one needs them: a row whose chips
     reserve the same space as the other row's is the same thickness as it at
     every width, not only at the one it was measured on. */
  min-height: 2.5em;
}
/* Everything in a chip reads from one left margin (Dr Daniel, 2026-09-14,
   after seeing both: "just revert back ba, make them aligned to the left") —
   figure, label and the line under it, under the mark at the chip's edge. */
.kpis-money .kpi-fun > div, .kpis-day .kpi-fun > div { min-width: 0; }
/* Six money tiles fold 6 -> 3 -> 2 -> 1, so a row is never part empty
   (Dr Daniel, 2026-09-14: "this view is so messy") — the old seven broke
   into 4 + 3 and left a hole the width of a tile on the right. The five
   count tiles are slighter, so they hold their single row much further
   down; when they do fold, an odd one out takes the whole row rather than
   sitting beside a gap. */
@media (max-width: 1100px) {
  .kpis-money { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Five is prime, so a plain fold leaves a hole: below 1100px the row becomes
   six invisible tracks and the chips take two each on the first row and three
   each on the second — 3 + 2, both rows edge to edge. */
@media (max-width: 1100px) {
  .kpis-day { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .kpis-day > .kpi { grid-column: span 2; }
  .kpis-day > .kpi:nth-child(n+4) { grid-column: span 3; }
}
@media (max-width: 900px) {
  .kpis-money, .kpis-day { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis-day > .kpi, .kpis-day > .kpi:nth-child(n+4) { grid-column: auto; }
  .kpis-day > .kpi:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .kpis-money, .kpis-day { grid-template-columns: minmax(0, 1fr); }
  .kpis-day > .kpi { grid-column: auto; }
}
/* Today's figure reads like every other chip's; yesterday's sits on the line
   under it with the direction between them. Two figures side by side needed a
   width a seventh of the screen does not have. */
.kpi-vs .kpi-vs-a.up { color: #166534; }
.kpi-vs .kpi-vs-a.down { color: #be185d; }
/* What the people in a traffic tile spent today. */
.kpi-spent { font-size: .72rem; font-weight: 700; color: var(--brand); margin-top: 3px; }
.kpi { text-align: left; min-width: 0; }
.kpi .v {
  font-size: clamp(1.15rem, 1.1rem + 0.7vw, 1.5rem);
  font-weight: 800; color: var(--brand); letter-spacing: -.01em;
  font-variant-numeric: tabular-nums; line-height: 1.15;
  /* A figure longer than anything anticipated wraps inside the card rather
     than escaping it — never pretty, but never broken either. */
  overflow-wrap: anywhere;
}
.kpi .l { font-size: .76rem; color: var(--muted); font-weight: 600; margin-top: 2px; }
/* Livelier dashboard (Dr Daniel, 2026-09-02): each tile carries its own mark
   and a tone stripe; the charts below are hand-rolled SVG. */
.kpi-fun { display: flex; gap: 10px; align-items: center; border-left: 4px solid var(--kt, var(--brand)); }
.kpi-fun > div { min-width: 0; }
.kpi-fun .kpi-ic {
  font-size: 1.25rem; width: 40px; height: 40px; flex: 0 0 40px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--kt, var(--brand)) 12%, #fff);
}
/* A money figure never wraps mid-number: it shrinks to fit instead. */
.kpi-fun .v {
  color: var(--kt, var(--brand));
  word-break: keep-all; overflow-wrap: normal; white-space: nowrap;
  font-size: clamp(.98rem, .86rem + .6vw, 1.4rem);
}
/* Roomier tracks for the Kumo cards (Dr Daniel, 2026-09-04: "very messy
   and cramp") — wide charts span the full row via .span2. */
.dash-charts { grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); }
.dash-charts .card h2 { margin-top: 0; }
.donut-wrap { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; }
.donut-hole { position: relative; width: 148px; height: 148px; flex: 0 0 148px; }
.donut { width: 100%; height: 100%; transform: rotate(0deg); }
.donut circle { transition: stroke-width .15s; }
@media (hover: hover) { .donut circle:hover { stroke-width: 8; } }
.donut-centre {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; pointer-events: none;
}
.donut-centre strong { font-size: .95rem; letter-spacing: -.01em; }
.donut-centre span { font-size: .66rem; color: var(--muted); text-transform: uppercase; letter-spacing: .06em; }
.donut-legend { flex: 1; min-width: 150px; display: flex; flex-direction: column; gap: 5px; }
.dl-row { display: flex; align-items: center; gap: 7px; font-size: .78rem; }
.dl-dot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }
.dl-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-v { font-weight: 700; font-variant-numeric: tabular-nums; }
.dl-p { color: var(--muted); width: 34px; text-align: right; font-variant-numeric: tabular-nums; }
/* ---- dashboard charts (rebuilt 2026-09-02 to the mark specs) ----------
   Columns: a CSS grid of equal tracks - even spacing by construction.
   Bars <= 24px wide, 4px rounded data-end, square baseline, 2px gaps.
   The line chart's SVG carries shapes only; dots and text are HTML. */
.col-plot {
  display: grid; align-items: end; height: 128px; column-gap: 2px;
  padding-top: 18px; /* room for the two selective value labels */
}
.col-cell {
  position: relative; height: 100%;
  display: flex; align-items: flex-end; justify-content: center;
}
@media (hover: hover) { .col-cell:hover { background: color-mix(in srgb, var(--brand) 5%, transparent); border-radius: 6px; } }
.col-bars {
  display: flex; align-items: flex-end; justify-content: center; gap: 2px;
  width: 100%; height: 100%;
}
.col-bar {
  width: 100%; max-width: 24px; border-radius: 4px 4px 0 0;
  background: var(--brand);
  transition: filter .15s;
}
.col-bar.ghost { background: #ddd2f2; }
.col-bar.hot { background: #4c1d95; }
@media (hover: hover) { .col-cell:hover .col-bar { filter: brightness(1.15); } }
.col-val {
  position: absolute; left: 50%; transform: translateX(-50%);
  font-size: .66rem; font-weight: 700; color: var(--text);
  white-space: nowrap; pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.axis-row {
  display: grid; column-gap: 2px;
  font-size: .64rem; color: var(--muted); padding-top: 3px;
}
.axis-row span { text-align: center; overflow: hidden; }
/* the week line */
.line-wrap { position: relative; height: 150px; }
.line-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.line-hit {
  position: absolute; width: 20px; height: 20px; transform: translate(-50%, -50%);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.line-dot {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--brand); border: 2px solid var(--card-bg, #fff);
  box-sizing: border-box; transition: transform .15s;
}
@media (hover: hover) { .line-hit:hover .line-dot { transform: scale(1.4); } }
.line-val {
  position: absolute; transform: translate(-50%, -170%);
  font-size: .68rem; font-weight: 700; color: var(--text);
  white-space: nowrap; pointer-events: none; font-variant-numeric: tabular-nums;
}
.line-val.end { transform: translate(-80%, -170%); }
/* shared */
.chart-tip {
  position: fixed; z-index: 4000; pointer-events: none;
  background: #241228; color: #fff; font-size: .76rem; font-weight: 700;
  padding: 5px 10px; border-radius: 8px; box-shadow: 0 4px 14px rgba(0,0,0,.25);
  white-space: nowrap; animation: fade .12s ease-out;
}
.cmp-legend {
  display: flex; align-items: center; gap: 6px; justify-content: center;
  font-size: .68rem; color: var(--muted); margin-top: 6px;
}
.cmp-dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.cmp-legend .cmp-dot:not(:first-child) { margin-left: 10px; }
.donut circle { cursor: pointer; transition: stroke-width .15s; }
@media (hover: hover) { .donut circle:hover { stroke-width: 8; } }
.dbar { border-radius: 8px; transition: background .15s; }
@media (hover: hover) { .dbar:hover { background: var(--brand-tint, #f5eefa); } }
.dbar .dbar-f { transition: width .5s cubic-bezier(.22,.9,.3,1), filter .15s; }
@media (hover: hover) { .dbar:hover .dbar-f { filter: brightness(.92) saturate(1.2); } }
.dbar-medal { width: 26px; flex: 0 0 26px; text-align: center; }
/* The hero strip: clinician of the day, tracking vs last month, average bill. */
.dash-hero {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px; margin-bottom: 14px; padding: 14px 18px;
}
@media (max-width: 720px) { .dash-hero { grid-template-columns: minmax(0, 1fr); } }
.hero-cell { display: flex; gap: 12px; align-items: center; flex: 1 1 260px; min-width: 240px; }
/* Each half is a half: the clinician starts at the middle of the strip. */
.dash-hero .hero-cell { min-width: 0; }
.hero-body { min-width: 0; }
.hero-body > * + * { margin-top: 1px; }
.hero-t { line-height: 1.25; }
.hero-v { line-height: 1.18; }
/* Today's takings lead the page: the one figure written out in full, in the
   money tone, a size above everything around it. */
.hero-ic-money { background: #d8f2ed; }
.hero-v.hero-money {
  font-size: clamp(1.25rem, 1.1rem + .7vw, 1.7rem); color: #0d9488;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.hero-ic {
  font-size: 1.4rem; width: 46px; height: 46px; flex: 0 0 46px; border-radius: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand-tint, #f3ecf7);
}
.hero-t { font-size: .72rem; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.hero-v { font-size: 1.02rem; font-weight: 800; letter-spacing: -.01em; }
.hero-v.up { color: #166534; }
.hero-v.down { color: #be185d; }
.hero-v.muted-v { color: var(--muted); font-weight: 600; }
.yt-wrap { display: flex; gap: 18px; flex-wrap: wrap; }
/* Figures stay INSIDE the card (Dr Daniel, 2026-09-06: "out of box") —
   the pair may shrink, the money never wraps, and the type is a notch
   smaller so RM five-figure days fit beside their arrow. */
.yt-cell { flex: 1; min-width: 0; text-align: center; }
.yt-pair { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 8px; min-width: 0; }
.yt-fig { display: flex; flex-direction: column; min-width: 0; }
.yt-fig strong { font-size: 1.02rem; letter-spacing: -.01em; white-space: nowrap; font-variant-numeric: tabular-nums; }
.yt-sub { font-size: .64rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.yt-arrow { font-size: 1.1rem; }
.yt-arrow.up { color: #166534; }
.yt-arrow.down { color: #be185d; }
/* two charts side by side on one shared scale */
.card.span2 { grid-column: 1 / -1; }
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
@media (max-width: 760px) { .duo { grid-template-columns: 1fr; } }
.duo-t { font-size: .72rem; font-weight: 800; color: var(--muted); text-align: center; letter-spacing: .06em; margin-bottom: 2px; }
.top-table { width: 100%; font-size: .8rem; border-collapse: collapse; }
.top-table th { text-align: left; font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; padding: 4px 6px; }
.top-table td { padding: 5px 6px; border-top: 1px solid var(--line-soft, #f0eaf4); }
.top-table .num { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }
@media (hover: hover) { .top-table tr:hover td { background: var(--brand-tint, #f5eefa); } }

table { border-collapse: collapse; width: 100%; font-size: .88rem; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
tr:last-child td { border-bottom: none; }
th { color: var(--muted); font-weight: 700; font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; }
tr.click { transition: background .1s; }
@media (hover: hover) { tr.click:hover { background: var(--brand-tint); cursor: pointer; } }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }

button, .btn {
  font: inherit; border: none; background: var(--brand); color: #fff;
  border-radius: 999px; padding: 10px 20px; cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  font-size: .9rem; font-weight: 600; min-height: 42px;
  box-shadow: 0 2px 6px rgba(71, 0, 84, .22);
  transition: background .12s, transform .06s, box-shadow .12s;
}
@media (hover: hover) { button:hover, .btn:hover { background: var(--brand-light); box-shadow: var(--shadow-lift); } }
button:active, .btn:active { transform: translateY(1px); box-shadow: none; }
button.ghost, .btn.ghost {
  background: var(--brand-tint); color: var(--brand); box-shadow: none;
}
@media (hover: hover) { button.ghost:hover, .btn.ghost:hover { background: var(--brand-tint-2); box-shadow: none; } }
/* Red is reserved for destroy/cancel/reject/void — nothing else
   (Dr Daniel, 2026-08-23), so a red button always means "this removes something". */
/* A softer red (Dr Daniel, 2026-08-23): destructive actions still read as
   "stop and think" without shouting at staff all day. The darker --bad is
   kept for text and warnings, where contrast matters more. */
/* A destructive button is OUTLINED, not filled (Dr Daniel, 2026-08-29: "dun
   fill up red for the whole button, just make the border red will do"). A
   solid red slab beside three quiet buttons pulled the eye to the one action
   nobody should reach for by accident; the red border still says "careful"
   without shouting it. Filling on hover keeps the commitment obvious at the
   moment of clicking. */
button.danger {
  background: #fff; color: #b3392f; box-shadow: none;
  border: 1.5px solid #d9584f;
}
@media (hover: hover) { button.danger:hover { background: #fbe7e5; color: #a8322a; box-shadow: none; } }
button.danger:active { background: #f6d5d1; }
/* "ghost danger" is the quiet form — outlined rather than filled, for actions
   that are destructive but routine (delete a draft, turn 2FA off). */
button.ghost.danger, .btn.ghost.danger {
  background: var(--bad-bg); color: #a8322a; box-shadow: none;
}
@media (hover: hover) { button.ghost.danger:hover, .btn.ghost.danger:hover { background: #fbd8d4; } }
button.small, .btn.small { padding: 6px 14px; font-size: .8rem; min-height: 32px; }

/* The one button that commits money. Deliberately the heaviest control on the
   page so it can never be confused with the light "add a line" actions. */
button.primary-action {
  background: #3d0047; font-size: 1rem; font-weight: 700; padding: 14px 30px;
  min-height: 52px; letter-spacing: .01em;
  box-shadow: 0 4px 14px rgba(61, 0, 71, .38);
}
@media (hover: hover) { button.primary-action:hover { background: #520060; box-shadow: 0 6px 20px rgba(61, 0, 71, .45); } }

/* Light-weight "build something" actions (product line, mattress, Mochi). */
button.soft, .btn.soft {
  background: var(--brand-tint); color: var(--brand); box-shadow: none;
  border: 1px solid var(--brand-tint-2); font-weight: 600;
}
@media (hover: hover) { button.soft:hover, .btn.soft:hover { background: var(--brand-tint-2); box-shadow: none; } }

/* Positive completion (collected, taken home) and the money-taking buttons.
   Green border, light green fill (Dr Daniel, 2026-08-29) — the same outlined
   treatment the destructive buttons got, in the opposite colour, so the two
   ends of the scale are built the same way and neither is a solid slab. Green
   already meant "this completes it" on the issue confirmation; it now reads the
   same on the button that opens it. Darkens on hover, so the commitment is
   obvious at the moment of clicking.

   There used to be a second `button.go:hover` further down this block quietly
   overriding the first — one rule now, so the hover cannot drift. */
button.go, .btn.go {
  background: var(--ok-bg); color: #14612f; box-shadow: none;
  border: 1.5px solid var(--ok);
}
@media (hover: hover) { button.go:hover, .btn.go:hover { background: #d2ecdb; color: #0f4f26; box-shadow: none; } }
button.go:active, .btn.go:active { background: #c3e5cf; }
/* The invoice page's big commit button: same treatment, a heavier border to
   match its weight. */
button.primary-action.go {
  background: var(--ok-bg); color: #14612f; box-shadow: none;
  border: 2px solid var(--ok);
}
@media (hover: hover) { button.primary-action.go:hover { background: #d2ecdb; color: #0f4f26; box-shadow: none; } }

/* Production stages, coloured so the factory board reads at a glance. */
button.stage-ack { background: var(--brand); }
button.stage-start { background: #b45309; }
@media (hover: hover) { button.stage-start:hover { background: #92400e; } }
button.stage-ready { background: var(--ok); }
@media (hover: hover) { button.stage-ready:hover { background: #14612f; } }
button.stage-delivered { background: #2f7fc7; }
/* Factory action row (Dr Daniel, 2026-09-02): even-sized buttons in calm
   tones — the stage action keeps its meaning by hue, not by shouting. */
.fo-actbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.fo-actbar button {
  flex: 0 0 auto; min-height: 42px; font-size: .9rem; white-space: nowrap;
  padding: 9px 16px; border-radius: 12px; box-shadow: none; font-weight: 700;
}
.fo-actbar button.stage-ack { background: #efeaf7; color: #5b4a86; border: 1.5px solid #b9a5d6; }
@media (hover: hover) { .fo-actbar button.stage-ack:hover { background: #e4dbf1; } }
.fo-actbar button.stage-start { background: #fdf3df; color: #92600a; border: 1.5px solid #d9b26a; }
@media (hover: hover) { .fo-actbar button.stage-start:hover { background: #fbe9c6; } }
.fo-actbar button.stage-ready { background: #e5f4e9; color: #166534; border: 1.5px solid #7fb591; }
@media (hover: hover) { .fo-actbar button.stage-ready:hover { background: #d5ecdc; } }
.fo-actbar button.stage-delivered { background: #e6f0f8; color: #2b5d85; border: 1.5px solid #8fb4d2; }
@media (hover: hover) { .fo-actbar button.stage-delivered:hover { background: #d6e6f4; } }
.fo-actbar button.ghost {
  background: #f3f0f5; color: #55475e; border: 1.5px solid #cfc4d6;
}
@media (hover: hover) { .fo-actbar button.ghost:hover { background: #eae4ee; } }
@media (hover: hover) { button.stage-delivered:hover { background: #256aa8; } }
button:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; }

input, select, textarea {
  font: inherit; padding: 10px 14px; border: 1.5px solid var(--line); border-radius: 12px;
  background: #fff; color: var(--text); width: 100%; max-width: 100%; min-height: 42px;
  transition: border-color .12s, box-shadow .12s;
}
textarea { min-height: 60px; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand-light);
  box-shadow: 0 0 0 3px var(--brand-tint-2);
}
button:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 2px; }
input[type="checkbox"] { min-height: 0; width: 20px; height: 20px; accent-color: var(--brand); cursor: pointer; }
label { display: block; font-size: .78rem; color: var(--muted); margin: 9px 0 4px; font-weight: 600; }
fieldset { border: 1.5px solid var(--line); border-radius: 12px; margin: 10px 0; }
legend { font-size: .78rem; color: var(--muted); padding: 0 6px; font-weight: 600; }
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.row > * { flex: 1; min-width: 130px; }
.row > .fix { flex: 0 0 auto; min-width: 0; }
.toolbar { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 12px; }
.toolbar input, .toolbar select { width: auto; }

/* soft filled status badges */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 11px; border-radius: 999px; font-size: .74rem;
  font-weight: 700; white-space: nowrap; border: none;
  background: var(--neutral-bg); color: var(--muted);
}
.badge::before { content: "●"; font-size: .55rem; }
.b-pending, .b-draft, .b-open { background: var(--info-bg); color: var(--info); }
.b-arrived, .b-in_progress, .b-submitted, .b-in_production { background: var(--warn-bg); color: var(--warn); }
.b-completed, .b-resolved, .b-issued, .b-ready, .b-delivered, .b-acknowledged, .b-signed { background: var(--ok-bg); color: var(--ok); }
.b-cancelled, .b-no_show, .b-replaced, .b-withdrawn, .b-superseded, .b-exception { background: var(--bad-bg); color: var(--bad); }
.b-waiting_customer, .b-waiting_factory, .b-revision_requested { background: var(--neutral-bg); color: var(--muted); }

.muted { color: var(--muted); font-size: .85rem; }
.error { color: var(--bad); font-size: .88rem; margin: 6px 0; white-space: pre-wrap; }
.notice {
  background: var(--brand-tint); border: none; border-radius: 12px;
  padding: 10px 14px; font-size: .85rem; margin: 8px 0;
}
/* A notice that is a warning, not information — used where a sync would do
   something destructive if it were run as configured (2026-08-29). */
.notice.bad { background: var(--bad-bg); color: #8f2a22; }
/* Add-to-Cart steps that were being missed as lilac footnotes (Giselle,
   2026-09-28): the pillow-exchange picker is a required step in brand
   outline; ship-from-factory is a dashed option box. */
.notice.exch-box { background: #fff; border: 1.5px solid var(--brand-light, #6a2178); border-left: 5px solid var(--brand, #470054); }
.exch-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.exch-req { font-size: .66rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: #8f2a22; background: var(--bad-bg, #fbe9e7); border-radius: 999px; padding: 2px 8px; }
.notice.fj-box { background: #fff; border: 1.5px dashed #c9b3d3; }
.fj-box .fj-tick { font-weight: 700; }
/* Ship-from-factory rides the Save row of the Add-to-Cart popup (Giselle,
   2026-09-28): a brand-tinted pill at the left of the buttons. */
/* Just the tick, sized like the buttons beside it (align-self: stretch takes
   the row's height), so the row reads as three equal pieces. */
.modal .actions > .fj-inbar { display: flex; align-items: center; margin-right: auto; align-self: stretch;
  flex: 0 0 auto; min-width: 0;
  background: var(--brand-tint, #f5ecf7); border: 1.5px solid var(--brand-light, #6a2178); border-radius: 999px;
  padding: 0 16px 0 12px; }
.fj-inbar .fj-tick { display: flex; align-items: center; gap: 8px; margin: 0; font-weight: 700; white-space: nowrap; cursor: pointer; }
.fj-inbar .fj-tick input { margin: 0; }
/* The delivery box that opens under the tick: heading, chips, level fields. */
.fj-deliver { margin-top: 10px; }
.fj-deliver-head { font-weight: 700; margin-bottom: 6px; }
.fj-deliver .fj-reasons { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 8px; }
.fj-deliver .fj-fields { align-items: flex-start; }
.fj-inbar > input:disabled { opacity: .55; }
.bad-text { color: var(--bad); }
/* A compact two-column read-out inside a panel. */
table.mini { width: 100%; border-collapse: collapse; font-size: .84rem; }
table.mini th { text-align: left; font-weight: 700; color: var(--muted); font-size: .76rem;
  text-transform: uppercase; letter-spacing: .03em; padding: 3px 0; }
table.mini td { padding: 3px 0; border-top: 1px solid var(--line); }

/* ---- modal (pop-up) ---- */
.modal-backdrop {
  position: fixed; inset: 0; background: rgba(42, 31, 46, .45);
  backdrop-filter: blur(2px);
  display: flex; align-items: flex-start; justify-content: center; padding: 4vh 12px; z-index: 50;
  overflow-y: auto;
}
.modal {
  background: var(--card); border-radius: 18px; width: 100%; max-width: 640px;
  padding: 0 22px 20px; box-shadow: 0 24px 60px rgba(0, 0, 0, .3); margin-bottom: 4vh;
  animation: modal-in .16s ease-out;
  /* THE MODAL SCROLLS, NOT THE BACKDROP (Dr Daniel, 2026-08-29: "the top bar
     for all pop up pls freeze on top properly. duwan have the gap").
     While the backdrop was the scroller, the sticky header parked about 48px
     down from the top of the screen — the backdrop's own 4vh padding, plus a
     little more — and everything in that band kept scrolling in full view
     ABOVE the header. Measured: at the top 46 pixels the painted element was
     the card's content, not the header.
     With the modal itself scrolling, the header pins to the modal's own top
     edge and there is no band left for anything to show through. */
  max-height: 92vh; overflow-y: auto;
}
@keyframes modal-in { from { opacity: 0; transform: translateY(10px) scale(.98); } }
.modal.wide { max-width: 960px; }
/* The consultation card carries a 300px history rail beside it, so it needs
   more room than an ordinary wide modal (2026-08-28). */
.modal.wide:has(.cn-split) { max-width: 1240px; }
.modal .modal-head {
  display: flex; align-items: center; gap: 10px; padding: 16px 0 10px;
  /* A line you can actually SEE under the title (Dr Daniel, 2026-09-05:
     "make it more obvious"). */
  border-bottom: 2px solid var(--brand-light, #b98cc4); margin-bottom: 12px;
  position: sticky; top: 0; background: var(--card); z-index: 2;
  /* The 12px below the rule is the head's too: with only a margin there, the
     form scrolled THROUGH it and the type buttons showed sliced in half
     (Dr Daniel, 2026-09-14). */
  box-shadow: 0 12px 0 var(--card);
}
.modal .modal-head h2 { margin: 0; color: var(--brand); flex: 1; font-size: 1.1rem; }
.modal .modal-x {
  width: 34px; height: 34px; min-height: 0; padding: 0; border-radius: 50%;
  /* No fill (Dr Daniel, 2026-08-30) - it colours on hover instead. */
  background: transparent; color: var(--brand); box-shadow: none;
  font-size: 1rem; flex: none;
}
@media (hover: hover) { .modal .modal-x:hover { background: var(--bad-bg); color: var(--bad); box-shadow: none; } }
.modal .actions {
  display: flex; gap: 9px; justify-content: flex-end; margin-top: 18px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 14px;
}
/* A footer row stretches its buttons to a common height, which quietly undid
   `small` - the type and padding shrank but the box did not (Dr Daniel,
   2026-08-30). A small action keeps its own height and centres against the
   full-size ones beside it. */
.modal .actions > button.small { align-self: center; min-height: 34px; }
/* FROZEN action bar (Dr Daniel, 2026-09-06: "freeze this tab" at the
   bottom): the PO popup's buttons stay pinned while the order scrolls;
   Edit/Lock order and SUBMIT FINALISED ride its right end. */
/* The WHOLE row is frozen, on its own solid ground (Dr Daniel, 2026-09-13:
   "The bottom tab why transparent? Pls freeze the whole bottom row") — the
   see-through strip of the day before let the mattress preview scroll through
   the buttons, which read as broken rather than as floating. The bar bleeds
   to the edges of whatever holds it, so nothing passes down either side. */
.actions.po-foot {
  position: sticky; bottom: 0; z-index: 3; align-items: center;
  background: var(--card, #fff);
  margin: 12px -22px 0; padding: 10px 22px;
  box-shadow: 0 -10px 12px -12px rgba(71, 0, 84, .3);
}
/* Each container bleeds by its own padding: the page, the popup page, the
   modal — see .content (20px 22px), body.embed .content (8px 14px) and
   .modal (0 22px 20px). */
body.embed .actions.po-foot { margin: 10px -14px 0; padding: 8px 14px; }
.modal .actions.po-foot {
  bottom: -20px; margin: 12px -22px -20px; padding: 10px 22px 16px;
  border-radius: 0 0 18px 18px;
}

/* "Customer Confirmed?" riding the same row as the submit it gates
   (Dr Daniel, 2026-09-12). Two answers, the standing one filled: amber while
   it waits, green once it is confirmed. */
.po-gate { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.po-gate-q { font-size: .8rem; font-weight: 700; color: var(--muted); }
button.po-yn {
  min-height: 28px; padding: 3px 11px; font-size: .78rem; font-weight: 700;
  background: var(--card); color: var(--muted);
  border: 1.5px solid var(--line); box-shadow: none;
}
@media (hover: hover) { button.po-yn:hover:not([disabled]) { background: var(--brand-tint); box-shadow: none; } }
button.po-yn.on { background: var(--ok-bg); color: var(--ok); border-color: var(--ok); }
button.po-yn.no.on { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); }
button.po-yn[disabled] { opacity: .6; cursor: default; }
/* The footer holds ONE row (Dr Daniel, 2026-09-12) — tightened rather than
   re-worded, so every button keeps the name staff know it by and they still
   fit side by side, Save changes included. */
.actions.po-foot { gap: 6px; }
.actions.po-foot > * { flex: none; }
.modal .actions.po-foot > button.small { min-height: 30px; padding: 5px 11px; }
.actions.po-foot > button.submit-final.head {
  font-size: .82rem; padding: 8px 14px; margin-left: 4px;
}

/* ---- toast ---- */
.toast {
  position: fixed; bottom: 20px; right: 20px; background: var(--brand-dark); color: #fff;
  padding: 12px 18px; border-radius: 14px; z-index: 99; box-shadow: var(--shadow-lift);
  max-width: 400px; font-size: .9rem; display: flex; gap: 9px; align-items: flex-start;
  animation: toast-in .2s ease-out;
}
@keyframes toast-in { from { opacity: 0; transform: translateY(12px); } }
.toast.err { background: var(--bad); }

/* ---- appointments calendar ---- */
.cal { display: grid; grid-template-columns: 52px repeat(7, 1fr); gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
.cal .hd { background: var(--brand-tint); font-size: .78rem; font-weight: 700; text-align: center; padding: 7px 2px; color: var(--brand); }
/* The header row freezes while the grid scrolls under it (Dr Daniel,
   2026-08-30). It has to be a SIBLING of the body grid, not its first row: a
   sticky grid item is confined to its own grid area - one row tall - so it can
   never travel, and `.cal`'s own `overflow: hidden` would have trapped it in a
   second way. As a block-level child of the scrolling pane it sticks properly.
   z-index clears the now-line (5) so the names are not crossed out in red as
   the clock hand passes under them. */
.cal-hdrow {
  /* Above ANYTHING a card can wear (Dr Daniel, 2026-09-06: cards were
     riding over the frozen branch names — a busy cluster's cascade z
     (2+column) passed the old 6, and a hovered card's 60 always did).
     The hover pop stays higher still at 400. */
  position: sticky; top: 0; z-index: 100;
  border-bottom: none; border-radius: var(--radius) var(--radius) 0 0;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .10);
}
/* The two halves read as one card: the seam between them carries no border and
   no corner, so only the outside of the pair is rounded. */
.cal-hdrow + .cal { border-top: none; border-radius: 0 0 var(--radius) var(--radius); }
.cal .hd.today { background: var(--brand); color: #fff; }
/* Date bar in branch colours (Dr Daniel, 2026-09-03) — keep in lockstep with
   the .cal-branches .hd palette below. */
.cal .hd.today.br-zp { background: #470054; }
.cal .hd.today.br-8tr { background: #0e7490; }
.cal .hd.today.br-ru { background: #a13d63; }
/* The one-day bar names its branch at the left edge; the date stays centred
   (Dr Daniel, 2026-09-03). */
.cal .hd .hd-daybar { position: relative; display: block; }
.cal .hd .hd-brname {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  font-size: .74rem; font-weight: 600; letter-spacing: .03em;
  white-space: nowrap; opacity: .95;
}
@media (max-width: 700px) { .cal .hd .hd-brname { display: none; } }
.cal .timecol { background: #fff; font-size: .68rem; color: var(--muted); text-align: right; padding: 2px 5px; position: relative; }
/* The hour names the row it starts, so it sits at the TOP of that row, right
   under the line (Dr Daniel, 2026-08-30). 4px rather than flush: at 2px the
   type touched the rule and read as sitting ON it, which is what prompted the
   change - this is the same position, just no longer colliding. Absolute so
   the label cannot add to the row's height. */
.cal .timecol .tc-h {
  position: absolute; right: 5px; top: 4px;
  line-height: 1; white-space: nowrap;
}
.cal .slot { background: #fff; min-height: 28px; position: relative; }
/* Today's column keeps only its side rails — the pale-purple WASH is gone
   (Dr Daniel, 2026-09-07: the wash kept reading as the hover band spilling
   past its 15 minutes; the band itself was always one quarter). */
.cal .slot.today-col { background: #fff; box-shadow: inset 1px 0 0 var(--brand-tint-2), inset -1px 0 0 var(--brand-tint-2); }
/* The HOUR boundary is the strong line, right above its hour label; the
   HALF-hour boundary is the whisper (Dr Daniel, 2026-09-07, third
   ruling: "revert back to the original line thickness and paint, then
   swap the thickness of the hour line and half-hour boundary"). BOTH
   are painted by the row itself over the grid's 1px gap — a bare gap
   pixel collapses at fractional browser zoom, which is why on Dr
   Daniel's screen the hour lines kept vanishing while the painted
   half-hour ones survived, reading as swapped weights. The hour line
   carries the ORIGINAL paint (1px var(--line)); nothing is thicker. */
.cal .slot:not(.h30), .cal .timecol:not(.h30) { box-shadow: 0 -1px 0 0 var(--line); }
.cal .slot.today-col:not(.h30) {
  box-shadow: 0 -1px 0 0 var(--line), inset 1px 0 0 var(--brand-tint-2), inset -1px 0 0 var(--brand-tint-2);
}
/* The half-hour line wears the IDENTICAL dot pattern as the 15-minute
   mid-line above — one shared recipe, so the two can never drift apart
   (Dr Daniel, 2026-09-07, after the KumoDoc example). The gap pixel
   above is blanked white and the dots paint on the cell's own top edge. */
.cal .slot.h30 {
  box-shadow: 0 -1px 0 0 #fff;
  background-image: repeating-linear-gradient(90deg, rgba(233, 226, 236, .8) 0 1px, transparent 1px 4px);
  background-repeat: no-repeat; background-size: 100% 1px; background-position: top left;
}
/* The time rail carries HOUR lines only — sub-hour lines never pass
   through the hour-label column (Dr Daniel, 2026-09-07): the rail's
   half-hour gap pixel is blanked and nothing is drawn in its place. */
.cal .timecol.h30 { box-shadow: 0 -1px 0 0 #fff; }
.cal .slot.h30.today-col {
  box-shadow: 0 -1px 0 0 #fff, inset 1px 0 0 var(--brand-tint-2), inset -1px 0 0 var(--brand-tint-2);
  background-image: repeating-linear-gradient(90deg, rgba(233, 226, 236, .8) 0 1px, transparent 1px 4px);
  background-repeat: no-repeat; background-size: 100% 1px; background-position: top left;
}
/* No whole-slot wash on hover — it merged with the quarter-hour band into
   what read as a 30-minute highlight (Dr Daniel, 2026-09-06: "i want it
   highlight in 15 min"). The hov-top/hov-bottom ::after band alone says
   which quarter the click will book. */
@media (hover: hover) { .cal .slot:hover { cursor: pointer; } }
/* drag-and-drop landing preview: dashed band on the target quarter-hour */
.cal .slot.drop-top::after, .cal .slot.drop-bottom::after {
  content: attr(data-droptime);
  position: absolute; left: 1px; right: 1px; height: calc(50% - 2px);
  border: 2px dashed var(--brand); border-radius: 6px; background: var(--brand-tint-2);
  color: var(--brand); font-size: .68rem; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
  z-index: 4; pointer-events: none;
}
.cal .slot.drop-top::after { top: 1px; }
.cal .slot.drop-bottom::after { bottom: 1px; }
/* Hovering an empty quarter FILLS it light purple with its time printed
   (Dr Daniel, 2026-09-06, after KumoDoc's blue: "please use light purple
   for us") — was a dashed outline before. */
.cal .slot.hov-top::after, .cal .slot.hov-bottom::after {
  content: attr(data-hovlabel);
  /* Fills the whole quarter — flush edges, square corners, soft vertical
     gradient (Dr Daniel, 2026-09-06). HARD PIXELS, not percentages
     (2026-09-07, after the band read as covering a full slot): a slot is
     28px by design, so the band is EXACTLY 14px and each half anchors by
     its own top offset — no container quirk can stretch it. */
  position: absolute; left: 0; right: 0; height: 14px;
  border: none; border-radius: 0;
  background: linear-gradient(180deg, #f9f4fb 0%, #ecdef4 55%, #f4ebf8 100%);
  /* Light ink, left-aligned — KumoDoc's quiet corner time (Dr Daniel,
     2026-09-06: "make it lighter color word, and align to the left"). */
  color: #b3a8bd; font-size: .62rem; font-weight: 600;
  display: flex; align-items: center; justify-content: flex-start;
  padding: 0 8px; z-index: 1; pointer-events: none;
}
.cal .slot.hov-top::after { top: 0; }
/* Top-anchored too — bottom-anchoring put the band in the wrong place
   whenever a slot box came out taller than its designed 28px. */
.cal .slot.hov-bottom::after { top: 14px; bottom: auto; }
/* KumoDoc-style cards (Dr Daniel, 2026-09-06): WHITE card, the left
   stripe wears the CLINICIAN's colour, the status shows as a small dot —
   and a completed booking greys the whole card while the stripe keeps the
   clinician. (Previously: pastel card = clinician, stripe = status.) */
.cal .appt {
  /* A LIVE booking keeps the previous look — clinician pastel fill,
     status on the left stripe (Dr Daniel, 2026-09-06: "the none complete
     one stays the previous color"); only COMPLETED goes KumoDoc-grey with
     the clinician-coloured stripe. */
  /* Less rounded (Dr Daniel, 2026-09-06). */
  position: absolute; left: 2px; right: 2px; z-index: 2; border-radius: 4px; padding: 2px 5px 2px 8px;
  background: var(--pc-bg, var(--brand-tint)); color: var(--text); font-size: .62rem; line-height: 1.3; overflow: hidden;
  cursor: grab; user-select: none; border: 1px solid var(--pc-line, var(--brand-light)); box-shadow: 0 1px 4px rgba(0,0,0,.14);
}
.cal .appt::before {
  /* Flush to the block's left edge (Dr Daniel, 2026-08-27), hugging the
     rounded corners on its side. Thinner (2026-09-06: "colour strip on
     the left, make it thinner"). */
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 3px;
  border-radius: 4px 0 0 4px; background: var(--status-c, var(--muted));
}
/* Status dot, BOTTOM right (Dr Daniel, 2026-09-07: "all icons put it on
   the right bottom corner") — the hover panel says the word. */
.cal .appt::after {
  /* z 2: the workflow pill (z 1) stretches under this corner as an opaque
     backing — without the lift it painted OVER the status figure
     (Dr Daniel, 2026-09-07: "wheres the customer sillouhette?"). */
  content: ""; position: absolute; bottom: 4px; right: 4px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--status-c, #888); z-index: 2;
}
/* The marker stays a DOT while the visit is only planned (pending,
   confirmed), but becomes a little CUSTOMER figure — filled with the same
   status colour — while a person is the story: arrived, cancelled,
   no-show. A COMPLETED visit drops the figure altogether (Dr Daniel,
   2026-09-07 third pass: "the customer sillouhette to disappear after
   the appointment is completed, so only leave the case note, billing and
   courier"). Same ::after, so the colour keeps riding --status-c; the
   silhouette is a mask, not an image, so it tints for free. */
.cal .appt.s-completed::after { display: none; }
.cal .appt.s-completed .wf-strip { padding-right: 4px; }
.cal .appt.s-arrived::after,
.cal .appt.s-cancelled::after, .cal .appt.s-no_show::after {
  /* Bigger figure (Dr Daniel, 2026-09-07: "make it bigger"). */
  bottom: 2px; right: 2px; width: 12px; height: 12px; border-radius: 0;
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* The visit's paperwork on the chip once the customer has ARRIVED (Dr
   Daniel, 2026-09-07): case note / billing $ / factory order — red means
   still to do, green means done, and the factory icon hides until an
   order exists. The strip lines up LEFT of the arrival figure (or the
   completed dot), which stays exactly where it was. */
.cal .appt .wf-strip {
  /* Bottom-right beside the status figure (Dr Daniel, 2026-09-07: "all
     icons put it on the right bottom corner, so that if it overlaps with
     the details, just hide the details and show only icon") — the pill
     inherits the card's own fill, so any text underneath is simply
     covered rather than bleeding through. */
  position: absolute; bottom: 1px; right: 1px; z-index: 1;
  display: inline-flex; gap: 3px; align-items: center;
  /* The pill runs all the way to the card's edge and reserves the status
     figure's zone inside itself (padding-right), so no text sliver can
     peek between the icons or through the silhouette's transparent
     surroundings (Dr Daniel, 2026-09-07: "Still abit overlap"). */
  background-color: inherit; padding: 2px 16px 2px 4px; border-radius: 4px 0 0 0;
}
.cal .appt .wf-ic { width: 10px; height: 10px; display: inline-block; background: #dc2626; }
.cal .appt .wf-ic.ok { background: #059669; }
.cal .appt .wf-note {
  /* A document with a folded corner and two written lines (Dr Daniel,
     2026-09-07: "do you have better icon?"). */
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M5 2h9l5 5v15H5z M13 3.2V8h4.8z M8 12h8v1.6H8z M8 15.5h8v1.6H8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M5 2h9l5 5v15H5z M13 3.2V8h4.8z M8 12h8v1.6H8z M8 15.5h8v1.6H8z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.cal .appt .wf-cash {
  background: none !important; width: auto; min-width: 7px;
  color: #dc2626; font: 900 11px/10px ui-sans-serif, system-ui, sans-serif; text-align: center;
}
.cal .appt .wf-cash.ok { color: #059669; }
.cal .appt .wf-fpo {
  /* A little LORRY (Dr Daniel, 2026-09-07: "FPO go for lorry icon") —
     cargo box, cab and two wheels, in place of the hexagon that read as
     a blob at chip size. */
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1 5h13v10H1z M14 8h4l3 3v4h-7z'/%3E%3Ccircle cx='6.5' cy='17.5' r='2.6'/%3E%3Ccircle cx='17.5' cy='17.5' r='2.6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1 5h13v10H1z M14 8h4l3 3v4h-7z'/%3E%3Ccircle cx='6.5' cy='17.5' r='2.6'/%3E%3Ccircle cx='17.5' cy='17.5' r='2.6'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* A buried card in a cascade lifts to the front under the mouse. */
@media (hover: hover) { .cal .appt:hover { z-index: 60 !important; box-shadow: 0 6px 18px rgba(0, 0, 0, .24); } }
/* The landing SHADOW (Dr Daniel, 2026-09-07: "use a grey shadow to show
   where exactly the chip will move to. The original chip remain at its
   location until we unclick") — a flat grey ghost at the chip's own
   height. It snaps INSTANTLY between the legal landing quarters: the
   .07s glide it first shipped with read as lag on a fast drag
   (2026-09-07: "not smooth"). The chip itself never moves until the
   drop commits. */
.drag-shadow {
  position: fixed; z-index: 500; pointer-events: none;
  background: rgba(110, 105, 116, .26);
  box-shadow: inset 0 0 0 1.5px rgba(110, 105, 116, .38);
}
/* While a chip is in hand the other cards are hit-transparent — no hover
   lifts or panels flickering under the pointer mid-drag. */
body.chip-dragging { cursor: grabbing; }
body.chip-dragging .cal .appt { pointer-events: none; }
/* Bottom-edge duration handle (Dr Daniel, 2026-09-06: "drag the chips
   longer or shorter") — an invisible 7px grab strip; a small grip bar
   shows on hover so the affordance is discoverable. */
.cal .appt .rz { position: absolute; left: 0; right: 0; bottom: 0; height: 7px; cursor: ns-resize; }
.cal .appt .rz::after {
  content: ""; position: absolute; left: 50%; transform: translateX(-50%); bottom: 2px;
  width: 26px; height: 3px; border-radius: 2px; background: rgba(36, 18, 40, .22); opacity: 0;
}
@media (hover: hover) { .cal .appt:hover .rz::after { opacity: 1; } }
.cal .appt.s-completed { background: #f0edf1; color: #6d6575; }
.cal .appt.s-completed::before { background: var(--pc-line, var(--brand-light)); }
.cal .appt.s-completed .t { color: #57505e; }
/* Struck through but SOLID (Dr Daniel, 2026-09-06: "Remove the
   transparent effect for cancel and no show"). */
.cal .appt.s-cancelled, .cal .appt.s-no_show { text-decoration: line-through; }
.cal .appt.blocked {
  /* LIGHT PURPLE hatch now (Dr Daniel, 2026-09-06: "colour make it light
     purple see? Remain the 条纹") — the stripes stay, the sand went.
     Full-bleed band, square corners, hatch edge to edge. */
  background: repeating-linear-gradient(45deg, #f5ecf7, #f5ecf7 7px, #e6d7ee 7px, #e6d7ee 14px);
  /* BORDERLESS (Dr Daniel, 2026-09-07: "I DUWAN GAPS") — the border drew a
     pale seam wherever two blocks met; now back-to-back bands read as one
     continuous hatch. */
  color: #5b4a68; border: none; border-radius: 0;
}
/* No side stripe and no status dot on a block (Dr Daniel, 2026-09-06:
   "Block timing has no status, why still got the pending circle") — it is
   held time, not anyone's booking. */
.cal .appt.blocked::before { display: none; }
.cal .appt.blocked::after { display: none; }
.cal .appt.blocked .blk-line { color: #5b4a68; }
.cal .appt .t {
  font-weight: 700; font-size: .66rem; padding-right: 10px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal .appt .rem {
  font-size: .58rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal .appt .ap-purpose {
  font-size: .58rem; font-style: italic; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal .appt .ap-meta {
  font-size: .58rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The floating hover panel (Dr Daniel, 2026-09-06, after KumoDoc): status
   banner on top, then the summarised booking. Pointer-events off — it
   informs, it never traps the mouse. */
.appt-pop {
  position: fixed; z-index: 400; width: 250px; background: #fff;
  border-radius: 12px; box-shadow: 0 14px 40px rgba(0, 0, 0, .3);
  overflow: hidden; pointer-events: none;
  /* A soft pop on arrival (Dr Daniel, 2026-09-06: "abit of animation"). */
  animation: appt-pop-in .16s ease-out;
}
@keyframes appt-pop-in {
  from { opacity: 0; transform: translateY(6px) scale(.96); }
}
.appt-pop .ap-banner {
  color: #fff; font-weight: 800; text-align: center; font-size: .72rem;
  letter-spacing: .08em; padding: 5px 8px;
}
.appt-pop .ap-body { padding: 10px 12px; font-size: .78rem; }
.appt-pop .ap-name { font-weight: 800; font-size: .92rem; margin-bottom: 1px; }
.appt-pop .ap-sub { color: var(--muted); font-style: italic; margin-bottom: 6px; }
.appt-pop .ap-line { margin: 2px 0; }
.appt-pop .ap-remarks { margin-top: 6px; background: #f7f4f9; border-radius: 8px; padding: 6px 8px; font-size: .74rem; }
.appt-pop .ap-muted { color: var(--muted); font-size: .7rem; margin-top: 6px; }
/* A block's one line IS its reason (Dr Daniel, 2026-09-01): sized to stay
   readable inside even a 15-minute sliver, so nobody has to hover to learn
   why the time is held. The padding squeezes with it. */
.cal .appt.blocked { padding: 0 4px 0 8px; }
.cal .appt .blk-line {
  font-size: .58rem; font-weight: 700; line-height: 10px; color: #5a5462;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The quarter-hour line (Dr Daniel, 2026-09-01: "change the time interval
   line from 30mins to 15 mins") — a dashed mid-line inside every half-hour
   slot, fainter than the solid line that starts it. Behind the cards, and
   never a click target of its own. */
.cal .slot::before {
  /* The SAME dot pattern as the half-hour line below — one shared recipe
     (Dr Daniel, 2026-09-07: "the half hour line and 15min lines got same
     dashing? U sure?"), fine dots after the KumoDoc example. */
  content: ""; position: absolute; left: 0; right: 0; top: 50%; z-index: 0; height: 1px;
  background-image: repeating-linear-gradient(90deg, rgba(233, 226, 236, .8) 0 1px, transparent 1px 4px);
  pointer-events: none;
}
/* Who is working at each branch, under its name in the ALL view. */
.cal .hd .hd-staff { display: flex; flex-wrap: wrap; gap: 3px; justify-content: center; margin-top: 3px; }
.cal .hd .hd-who {
  font-size: .62rem; font-weight: 700; padding: 1px 7px; border-radius: 999px;
  background: var(--pc-bg); border: 1px solid var(--pc-line);
  /* The branch banner writes in white; these chips are pale, so they must set
     their own dark ink or the names vanish into the pastel (Dr Daniel,
     2026-09-01: "hard to read because of the color"). */
  color: #241b30;
  /* Clickable filters now (Dr Daniel, 2026-09-06) — buttons, so the
     global button chrome is stripped here. */
  min-height: 0; box-shadow: none; cursor: pointer;
}
.cal .hd .hd-who.on { box-shadow: inset 0 0 0 1.5px #241b30; font-weight: 800; }
.cal .hd .hd-clear {
  background: none; border: none; box-shadow: none; min-height: 0;
  padding: 1px 6px; font-size: .6rem; font-weight: 700; cursor: pointer;
  color: inherit; opacity: .85;
}
@media (hover: hover) { .cal .hd .hd-clear:hover { opacity: 1; background: none; box-shadow: none; } }
/* Tight above and below (Dr Daniel, 2026-09-06). */
.cal-legend { display: flex; gap: 6px; flex-wrap: wrap; margin: 3px 0 0; align-items: center; }
.cal-legend .chip {
  display: inline-flex; align-items: center; gap: 5px; font-size: .72rem; font-weight: 700;
  padding: 2px 10px; border-radius: 999px; background: var(--pc-bg); border: 1px solid var(--pc-line);
}
.cal-legend .dot { width: 10px; height: 10px; border-radius: 3px; background: var(--status-c); }
/* The chips FILTER (Dr Daniel, 2026-09-06): picked chips fill dark; the
   matching bookings lift while every other card fades. */
/* Buttons inherit the global white button ink — these chips must say
   their words in dark text (Dr Daniel, 2026-09-06: "Where are the words"). */
.cal-legend .chip.st-filter { cursor: pointer; min-height: 0; box-shadow: none; color: var(--text, #2a1f2e); }
@media (hover: hover) { .cal-legend .chip.st-filter:hover { border-color: var(--brand); } }
.cal-legend .chip.st-filter.on { background: #372f3d; border-color: #372f3d; color: #fff; }
/* The Clear word: no fill, just quiet ink (Dr Daniel, 2026-09-06). Unscoped
   from the legend the same day — the Order Form range picker says Clear
   the same way. */
.st-clear {
  background: none; border: none; box-shadow: none; min-height: 0;
  padding: 2px 8px; font-size: .72rem; font-weight: 700; cursor: pointer;
  color: var(--muted, #79707f);
}
@media (hover: hover) { .st-clear:hover { color: var(--brand); background: none; box-shadow: none; } }
/* Today wears the brand ink, Clear stays quiet — the pair sits at the foot
   of every collapsible calendar (Dr Daniel, 2026-09-06). */
.st-clear.st-today { color: var(--brand, #470054); }

/* Every native date field opens the flat LUNOX picker instead of the
   browser's own (Dr Daniel, 2026-09-06: "why no follow the collapsible
   calendar…") — so the built-in calendar icon goes. */
input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
/* The popup's title is a BUTTON that drills up: days → years → months
   ("Pls let us choose year first, then month, then date"). */
.mc-title-btn {
  background: none; border: 0; box-shadow: none; min-height: 0;
  padding: 2px 8px; cursor: pointer; border-radius: 8px;
}
@media (hover: hover) { .mc-title-btn:hover { background: var(--brand-tint); color: var(--brand); box-shadow: none; } }
/* Year and month pages: 12 roomy cells, four to a row. */
.date-pop .mc-grid.mc-pick4 { grid-template-columns: repeat(4, 1fr); }
.date-pop .mc-pick4 .mc-day { height: 34px; border-radius: 10px; font-size: .78rem; }
.cal .appt.dim-status { opacity: .22; }
/* A dimmed ghost stacked IN FRONT of a focused card would wash it out —
   its translucent white blends over the card behind (Dr Daniel, 2026-09-06:
   "Why wong choo keong chip semi transparent?"). Ghosts drop to the back
   of the cascade; hover still lifts one for a peek. */
@media (hover: hover) { .cal .appt.dim-status:not(:hover) { z-index: 1 !important; } }
.cal .appt.focus-status { box-shadow: 0 5px 16px rgba(0, 0, 0, .3); }
.cal-legend .chip.filtered { background: #fff7e6; border-color: #e0b473; color: #8a5a00; font-weight: 700; }

/* Month grid and practitioner filter, always on screen — the date picker used
   to be a dropdown and the practitioner chips were only a legend, so neither
   could be acted on without hunting (Dr Daniel, 2026-08-24). */
/* The calendar's controls live in a rail down the left and stay put while the
   grid scrolls. Across the top they scrolled away exactly when they were needed
   and pushed the schedule off the first screen (Dr Daniel, 2026-08-24). */
.cal-layout { display: flex; gap: 16px; align-items: flex-start; }
/* The rail costs the grid 224px, so the calendar page drops the 1320px reading
   width every other screen uses — a week of columns needs every pixel. */
.content:has(.cal-layout) { max-width: none; }
.cal-rail {
  flex: 0 0 208px; width: 208px;
  /* 59px clears the sticky topbar; the rail scrolls internally if it ever
     outgrows the viewport, so a long practitioner list can never be cut off. */
  position: sticky; top: 59px; max-height: calc(100vh - 75px); overflow-y: auto;
  display: flex; flex-direction: column; gap: 10px; padding-bottom: 4px;
}
.cal-main { flex: 1 1 auto; min-width: 0; }
.rail-primary { width: 100%; }
.rail-views { display: grid; grid-template-columns: repeat(3, 1fr); gap: 5px; }
.rail-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 5px; }
.rail-duo button { width: 100%; padding: 7px 4px; font-size: .8rem; min-height: 0; }
.rail-views button { width: 100%; padding: 7px 4px; font-size: .8rem; min-height: 0; }
.rail-actions { display: flex; flex-direction: column; gap: 5px; }
.rail-actions button { width: 100%; padding: 7px 10px; font-size: .8rem; min-height: 0; text-align: left; }
.cal-rail .cal-legend { margin: 2px 0 0; gap: 4px; }
.cal-rail .cal-legend .chip { font-size: .64rem; padding: 1px 8px; }
.cal-rail .cal-legend .dot { width: 8px; height: 8px; }
.cal-rail .pract-toggles { gap: 4px; }
/* Narrow screens: the rail becomes a normal block above the grid. A sticky
   sidebar on a phone would eat the screen it is meant to save. */
@media (max-width: 900px) {
  .cal-layout { display: block; }
  .cal-rail { position: static; width: auto; max-height: none; flex-direction: row;
    flex-wrap: wrap; align-items: center; margin-bottom: 10px; }
  .rail-primary, .rail-views, .rail-actions { width: auto; }
  .rail-views { display: flex; }
  .rail-actions { flex-direction: row; }
}
.mini-cal {
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); padding: 5px 7px 6px; box-shadow: var(--shadow);
}
.mc-head { display: flex; align-items: center; justify-content: space-between; gap: 4px; margin-bottom: 2px; }
/* The second month carries no arrows — one window, moved by the pair above. */
.mc-head.mc-next { justify-content: center; margin-top: 7px; padding-top: 6px; border-top: 1px solid var(--line); }
/* GNOME-flat mini calendar (Dr Daniel, 2026-09-06: "I want to look exactly
   the same. Except the chosen colour is lunox's purple.") — plain dark
   title, quiet grey arrows, bare numbers, and ONE filled purple circle on
   the chosen day. The global button box-shadow was what made every cell a
   floating white pill, so each piece flattens it explicitly. */
.mc-title { font-size: .72rem; font-weight: 700; color: var(--text); }
/* These are all <button>, so they inherit the 42px touch-target minimum meant
   for real buttons — which is what was padding the whole block out. The
   calendar is a front-desk screen, so a 22px day cell is the right size here. */
.mc-nav {
  background: none; border: 0; box-shadow: none; color: var(--muted);
  font-size: .95rem; line-height: 1;
  padding: 1px 6px; border-radius: 999px; cursor: pointer; min-height: 0;
}
@media (hover: hover) { .mc-nav:hover { background: var(--brand-tint); color: var(--brand); box-shadow: none; } }
.mc-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.mc-wd { font-size: .58rem; color: var(--muted); text-align: center; font-weight: 600; }
.mc-day {
  height: 22px; min-height: 0; border: 0; background: none; box-shadow: none;
  border-radius: 999px; cursor: pointer;
  font-size: .68rem; font-weight: 500; color: var(--text); padding: 0;
}
@media (hover: hover) { .mc-day:hover { background: var(--brand-tint); box-shadow: none; } }
.mc-day.pad { visibility: hidden; }
/* The days currently on the schedule — the one filled circle. */
.mc-day.sel { background: var(--brand); color: #fff; font-weight: 700; }
@media (hover: hover) { .mc-day.sel:hover { background: var(--brand); } }
/* Today is quiet when not chosen: purple digits, no ring. */
.mc-day.today { color: var(--brand); font-weight: 800; box-shadow: none; }
.mc-day.today.sel { color: #fff; box-shadow: none; }

/* Working list (2026-08-27): a tidy aligned column — one full-width row per
   clinician, ticks in a fixed gutter, the head row carrying the label and the
   Hide/Show-all pill. */
.pract-toggles { display: flex; gap: 3px; flex-direction: column; align-items: stretch; }
.pt-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 2px; }
.pt-label { font-size: .6rem; font-weight: 800; color: var(--muted); letter-spacing: .08em; text-transform: uppercase; }
.pt-chip {
  display: flex; align-items: center; gap: 6px; font-size: .72rem; font-weight: 700;
  padding: 4px 10px; border-radius: 8px; cursor: pointer; line-height: 1.4; min-height: 0;
  width: 100%; justify-content: flex-start; text-align: left;
  background: var(--pc-bg); border: 1px solid var(--pc-line); color: var(--text); box-shadow: none;
}
@media (hover: hover) { .pt-chip:hover { box-shadow: none; filter: brightness(.97); } }
.pt-chip .pt-tick { font-size: .62rem; width: 1em; display: inline-block; flex: none; }
/* Hidden reads as switched off, not as a different clinician. */
.pt-chip.off { background: #fff; border-color: var(--line); color: var(--muted); text-decoration: line-through; }
.pt-all {
  background: var(--brand-tint); border: 0; color: var(--brand); font-size: .64rem;
  font-weight: 800; border-radius: 999px; cursor: pointer; padding: 2px 10px;
  min-height: 0; box-shadow: none; letter-spacing: .02em;
}
@media (hover: hover) { .pt-all:hover { background: var(--brand-tint-2); box-shadow: none; } }

/* ---- mattress illustration (side view) ---- */
.stack { max-width: 520px; margin: 10px 0; }
.stack .cap {
  font-size: .74rem; color: var(--muted); display: flex; justify-content: space-between;
  padding: 2px 10px; font-weight: 600; letter-spacing: .03em;
}
.stack .mattress {
  position: relative; border: 3px solid #7a6a80; border-radius: 26px; overflow: hidden;
  background: #fff; box-shadow: 0 14px 24px -12px rgba(71, 0, 84, .35);
}
/* The sewn-on side tag sits on the RIGHT of the base band (Dr Daniel,
   2026-09-06) — a real element now, so a split build carries exactly one. */
.stack .label-tag {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  background: #fff; border: 2px solid #7a6a80;
  border-radius: 6px; font-size: .58rem; font-weight: 700; color: #7a6a80;
  padding: 3px 8px; letter-spacing: .1em; pointer-events: none;
}
.stack .layer {
  display: flex; min-height: 40px; font-size: .8rem; text-align: center;
  align-items: stretch; border-top: 2px solid rgba(122, 106, 128, .55);
}
.stack .layer:first-child { border-top: none; }
.stack .layer.fabric {
  min-height: 44px; font-weight: 600;
  background:
    repeating-linear-gradient(45deg, rgba(122, 106, 128, .16) 0 1.5px, transparent 1.5px 22px),
    repeating-linear-gradient(-45deg, rgba(122, 106, 128, .16) 0 1.5px, transparent 1.5px 22px),
    linear-gradient(#ffffff, var(--fab, #f2ecf5));
  border-bottom: 2px dashed rgba(122, 106, 128, .7);
}
/* Each fabric quilts in its own tint, matching its button icon (Dr Daniel,
   2026-09-02): Nanofrost ❄ ice blue, Tencel 🍃 leaf green, Proneem 🛡 olive;
   the hexa topper skins follow the same families. */
.stack .layer.fabric.fab-nanofrost { --fab: #d9e9f7; }
/* Tencel greyish-white, ProNeem green (Dr Daniel, 2026-09-03). */
.stack .layer.fabric.fab-tencel { --fab: #eceeeb; }
.stack .layer.fabric.fab-proneem { --fab: #cfe6cc; }
.stack .layer.fabric.fab-nanohexa { --fab: #d9e9f7; }
.stack .layer.fabric.fab-halfnanohexa { --fab: #e5eff8; }
.stack .layer.fabric.fab-tencelhexa { --fab: #eceeeb; }
/* Topper cover: a thin skin, not a layer (2026-09-02). */
.stack .layer.fabric.thin { min-height: 20px; font-size: .68rem; }
/* A topper is a thin slab, not a capsule (Dr Daniel, 2026-09-02: the wide
   pill looked "out of proportion"): narrower drawing, square-ish corners,
   lighter shell, and a slimmer band per layer. */
.stack.topper { max-width: 380px; }
.stack.topper .layer { min-height: 34px; }
.stack.topper .mattress {
  border-radius: 10px; border-width: 2px;
  box-shadow: 0 8px 14px -10px rgba(71, 0, 84, .3);
}
.stack.topper .label-tag { padding: 2px 6px; font-size: .54rem; right: 8px; top: 42%; transform: none; }
.stack .layer.base {
  min-height: 74px; font-weight: 600; border-top: 2px solid #7a6a80;
}
/* The base as it is actually upholstered: LUNOX purple, logo on the right,
   the customer's embroidery on the left (Dr Daniel, 2026-08-23). */
.stack .layer.base.brandbase {
  position: relative; border-top: 2px solid #2e0036; color: #fff;
}
/* Keep the centre label clear of the embroidery (left) and logo (right). */
.stack .layer.base.brandbase .side { color: #fff; box-shadow: none; padding: 6px 76px; }
.stack .layer.base.brandbase .side small { color: rgba(255, 255, 255, .72); }
.stack .layer.base.brandbase .baselogo,
.stack .layer.base.brandbase .baseemb {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: .62rem; font-weight: 800; letter-spacing: .18em; color: #fff;
  text-transform: uppercase; opacity: .92; pointer-events: none;
}
.stack .layer.base.brandbase .baselogo { right: 12px; }
/* Embroidery is stitched, so it reads as thread rather than print. */
.stack .layer.base.brandbase .baseemb {
  left: 12px; letter-spacing: .1em; font-style: italic; opacity: 1;
  text-shadow: 0 1px 0 rgba(0, 0, 0, .45);
}
.stack .layer.base.brandbase.spring {
  background:
    repeating-linear-gradient(90deg, transparent 0 5px, rgba(255, 255, 255, .16) 5px 7px, transparent 7px 22px),
    repeating-linear-gradient(180deg, transparent 0 12px, rgba(255, 255, 255, .08) 12px 14px),
    linear-gradient(#5b1668, var(--brand));
}
.stack .layer.base.brandbase.foam {
  background:
    radial-gradient(rgba(255, 255, 255, .13) 1.5px, transparent 1.5px) 0 0 / 14px 14px,
    linear-gradient(#5b1668, var(--brand));
}
/* Thickness gauge beside the stack: a tick per layer plus the total. */
.stack-row { display: flex; align-items: stretch; gap: 8px; }
.stack-row .mattress { flex: 1; }
.gauge {
  position: relative; width: 34px; display: flex; flex-direction: column;
  justify-content: space-around; padding: 6px 0;
  border-left: 2px solid var(--line);
}
.gauge .tick { display: block; height: 1px; background: var(--line); margin-left: -2px; width: 9px; }
.gauge .gauge-total {
  position: absolute; right: -2px; top: 50%;
  transform: translateY(-50%) rotate(90deg); transform-origin: center;
  font-size: .62rem; font-weight: 700; color: var(--muted); white-space: nowrap;
}
.stack .side {
  flex: 1; display: flex; align-items: center; justify-content: center; padding: 6px 8px;
  box-shadow: inset 0 -8px 10px -8px rgba(0, 0, 0, .18), inset 0 6px 8px -6px rgba(255, 255, 255, .8);
}
.stack .side .matname { font-weight: 600; }
.stack .side small { display: block; font-weight: 400; color: rgba(34, 24, 38, .6); font-size: .68rem; }
.stack .layer .side + .side { border-left: 3px dashed rgba(122, 106, 128, .8); }
.stack .m-latex_topper, .stack .m-latex { background-color: var(--mat-latex); }
.stack .m-memory_foam { background-color: var(--mat-memory); }
.stack .m-soft_foam { background-color: var(--mat-soft); }
.stack .m-medium_foam { background-color: var(--mat-medium); }
.stack .m-hard_foam { background-color: var(--mat-hard); }
.stack .dims { text-align: center; font-size: .76rem; color: var(--muted); margin-top: 6px; }
.stack .dims .rule {
  height: 8px; border-left: 2px solid var(--muted); border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted); margin: 2px 24px 3px; border-radius: 0 0 4px 4px;
}
.superseded-banner {
  background: var(--bad); color: #fff; font-weight: 700; text-align: center;
  padding: 7px; border-radius: 10px; letter-spacing: .08em; margin: 8px 0;
}

/* ---- production status stepper ---- */
/* One row, always (Dr Daniel, 2026-08-29). Six stages wrapped "Delivered" onto
   a line of its own, which reads as two separate journeys rather than one.
   The connectors give up their width first, then the labels ellipsis (their
   full name is on hover), and the strip scrolls only as a last resort. */
.steps {
  display: flex; flex-wrap: nowrap; gap: 0; margin: 10px 0 14px;
  overflow-x: auto; scrollbar-width: thin;
}
.step { display: flex; align-items: center; gap: 7px; padding: 4px 0; min-width: 0; flex: 0 1 auto; }
.step .dot {
  width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center;
  justify-content: center; font-size: .78rem; font-weight: 700; flex: none;
  background: #fff; border: 2px solid var(--line); color: var(--muted);
}
/* Nothing that carries meaning shrinks: the dot holds the number and the label
   holds the stage name. Clipping "Draft" to "Dra…" to save four pixels looks
   broken, so ALL the slack is taken out of the connectors instead, and when
   even that is not enough the strip scrolls rather than truncating. */
.step .lbl { font-size: .78rem; color: var(--muted); white-space: nowrap; font-weight: 600; flex: 0 0 auto; }
.step .dot { flex: none; }
.step::after {
  content: ""; height: 2px; background: var(--line);
  margin: 0 7px; flex: 0 1 26px; min-width: 6px;
}
.step:last-child::after { display: none; }
.step.done .dot { background: var(--brand); border-color: var(--brand); color: #fff; }
.step.done .lbl { color: var(--text); }
.step.now .dot { border-color: var(--brand); color: var(--brand); box-shadow: 0 0 0 4px var(--brand-tint-2); }
.step.now .lbl { color: var(--brand); }

/* ---- definition rows ---- */
.kv { display: grid; grid-template-columns: 130px 1fr; gap: 5px 12px; font-size: .9rem; margin: 8px 0; }
.kv dt { color: var(--muted); font-size: .78rem; padding-top: 2px; font-weight: 600; }
.kv dd { margin: 0; }

/* ---- Custom Mochi assessment ---- */
.mochi-wrap { position: relative; }
.mochi-bg { position: absolute; inset: 0; color: var(--brand-tint); pointer-events: none; }
.mochi-bg svg { width: 100%; height: 100%; display: block; }
.mochi-pa {
  position: relative;
  display: grid; grid-template-columns: minmax(120px, 1fr) minmax(180px, 2fr) minmax(120px, 1fr);
  grid-template-rows: auto auto auto; gap: 8px;
  padding: 26px 34px 40px;
}
.pocket {
  background: rgba(255, 255, 255, .7); border: 2px dashed var(--brand-tint-2);
  border-radius: 16px; padding: 10px 8px; text-align: center;
}
.pocket.d { grid-row: span 3; display: flex; flex-direction: column; justify-content: center; }
/* outer pockets follow the pillow contour */
.pocket.p-d1 { border-radius: 60px 14px 14px 60px; padding-left: 14px; }
.pocket.p-d2 { border-radius: 14px 60px 60px 14px; padding-right: 14px; }
.pocket.p-b { border-radius: 30px 30px 12px 12px; }
.pocket.p-c { border-radius: 12px 12px 38px 38px; }
.pocket h4 { margin: 0 0 2px; font-size: .74rem; letter-spacing: .07em; text-transform: uppercase; color: var(--brand); }
.pocket .sub { font-size: .68rem; color: var(--muted); margin-bottom: 6px; }
.pocket .gram { width: 84px; text-align: center; margin: 0 auto; }
/* Phones (2026-10-07): the diagram keeps its shape but gives up the desktop
   minimums, the side pockets get the room (their pad bars carry names), and a
   pad name wraps inside its bar instead of pushing the ✕ out. */
@media (max-width: 600px) {
  .mochi-pa { grid-template-columns: minmax(0, 1fr) minmax(0, .72fr) minmax(0, 1fr); gap: 4px; padding: 18px 2px 28px; }
  .pocket { padding: 8px 3px; border-radius: 12px; }
  .pocket.p-d1 { padding-left: 5px; border-radius: 40px 10px 10px 40px; }
  .pocket.p-d2 { padding-right: 5px; border-radius: 10px 40px 40px 10px; }
  .pocket h4 { font-size: .66rem; letter-spacing: .03em; }
  .pocket .sub { font-size: .62rem; margin-bottom: 4px; }
  .pocket .gram { width: 100%; max-width: 84px; }
}
/* The NECK SUPPORT side — which face of the pillow goes up under the neck —
   is the one thing on this diagram that changes from build to build, and a
   solid border was all it had to say so (Dr Daniel, 2026-09-23: "I meant neck
   support side ... highlight it with a colour"). It is now filled in the brand
   tint and says out loud which way up it goes. */
.pocket.hot {
  /* Teal, not the brand purple everything else on this page is set in
     (Dr Daniel, 2026-09-23: "Can give it another calm colour"). */
  border-color: var(--accent-teal); border-style: solid;
  background: var(--accent-teal-bg);
  box-shadow: 0 0 0 3px rgba(14, 116, 144, .10);
}
.pocket.hot h4 { color: var(--accent-teal); }
.pocket.hot::after {
  content: 'this side up';
  display: inline-block; margin-top: 8px;
  padding: 2px 10px; border-radius: 999px;
  background: var(--accent-teal); color: #fff;
  font-size: .6rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
}
.layerstack { display: flex; flex-direction: column; gap: 3px; margin: 4px 0; }
.layerstack .lay {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  background: #fff; border: 1.5px solid var(--brand-tint-2); border-radius: 9px;
  padding: 4px 7px 4px 11px; font-size: .76rem; font-weight: 700; color: var(--brand);
  min-height: 32px; text-align: left;
}
.layerstack .lay.thin { min-height: 21px; padding-top: 1px; padding-bottom: 1px; }
.layerstack .lay.mf { background: var(--brand-tint); }
.layerstack .lay button {
  min-height: 0; padding: 0; width: 20px; height: 20px; border-radius: 50%; flex: none;
  background: transparent; color: var(--bad); box-shadow: none; font-size: .68rem;
}
.stacksum { font-size: .72rem; color: var(--muted); font-weight: 700; margin: 3px 0 5px; }
.lay[draggable="true"] { cursor: grab; }
.lay .grip { color: var(--brand-tint-2); font-size: .8rem; flex: none; letter-spacing: -2px; touch-action: none; }
/* A finger needs something to hold (2026-10-07): the grip is the handle for
   reordering pads on a phone or iPad, so it gets a thumb-sized hit area. */
@media (pointer: coarse) {
  .lay .grip { font-size: 1.05rem; padding: 6px 10px 6px 4px; margin: -6px 0 -6px -6px; color: var(--brand); }
}
@media (max-width: 600px) and (pointer: coarse) {
  .lay .grip { padding: 6px 4px 6px 2px; margin: -6px -2px -6px -3px; }
}
/* After the bar rules above, or they win (same specificity, later line). */
@media (max-width: 600px) {
  .layerstack .lay { gap: 3px; padding: 3px 3px 3px 5px; font-size: .68rem; line-height: 1.15; }
  .layerstack .lay > span:not(.grip) { min-width: 0; overflow-wrap: anywhere; }
  .layerstack .lay button { width: 18px; height: 18px; }
}
.lay.dragging { opacity: .45; }
.lay.dragtarget { outline: 2px dashed var(--brand); outline-offset: 2px; }
select.addpad {
  min-height: 32px; padding: 4px 28px 4px 12px; font-size: .78rem; width: auto;
  border-radius: 10px; background: var(--card); border: 1.5px solid var(--line);
  color: var(--text); font-weight: 600; cursor: pointer; margin-top: 2px;
}
@media (hover: hover) { select.addpad:hover { background: var(--brand-tint); } }
/* Aroma chips: each scent its own calm tone; picked = its tone, ink and a
   touch of weight (2026-09-02). */
/* Same state language as the layering rows above (2026-09-02): resting =
   neutral grey, chosen = a DEEP fill of the scent's own colour. */
button.aroma-chip {
  background: var(--neutral-bg); color: var(--muted);
  border: none; border-radius: 10px;
  padding: 6px 10px; font-size: .8rem; font-weight: 600; box-shadow: none;
  min-height: 32px; flex: 1; min-width: 110px;
}
@media (hover: hover) { button.aroma-chip:hover { background: var(--tone-bg, #eceef2); color: var(--tone-fg, #55606f); } }
button.aroma-chip.picked {
  background: var(--tone-deep, #d9dde3); color: var(--tone-fg, #55606f); font-weight: 800;
}
button.aroma-chip:disabled { opacity: .45; cursor: not-allowed; }
button.aroma-chip.aroma-none {
  display: block; width: 100%; margin-top: 6px;
  min-height: 24px; padding: 3px 10px; font-size: .74rem; flex: none;
}
.optrow { display: flex; gap: 8px; flex-wrap: wrap; }
.optrow button { flex: 1; min-width: 110px; }
/* Chosen = calm lavender with brand ink, not the solid block (2026-09-02). */
/* :not(.aroma-chip): the aroma chips share the row container but carry
   their own per-scent colours — this rule was painting them all lavender
   and eating their picked state (found 2026-09-02). */
.optrow button:not(.off):not(.aroma-chip) {
  background: #e9d5f0; color: var(--brand);
  box-shadow: none; font-weight: 700;
}
@media (hover: hover) { .optrow button:not(.off):not(.aroma-chip):hover { background: #e0c6e9; } }
.optrow button.off { background: var(--neutral-bg); color: var(--muted); box-shadow: none; }
/* Which kind of consultation this is, on the case note card: four pills that
   used to stretch the full width because .optrow buttons are flex:1, which
   made a setting look like the point of the page (Dr Daniel, 2026-09-14:
   "it's messy"). They now take the room their own words need. */
.cn-typebar { gap: 6px; margin: 0 0 14px; }
.cn-typebar button { flex: 0 0 auto; min-width: 0; min-height: 30px; padding: 4px 15px; font-size: .82rem; }
/* Beside the customer's name there is less room than there was across the
   body, so the pills tighten rather than wrap the title row onto two lines. */
.cn-headtools .cn-typebar button { padding: 4px 11px; font-size: .78rem; white-space: nowrap; }
@media (hover: hover) { .optrow button.off:hover { background: var(--brand-tint); color: var(--brand); } }
.hline { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 7px 0; }
.hline .v { font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ok); }
.hline .v.over { color: var(--bad); }
.guide-panel { background: var(--brand-tint); border-radius: 14px; padding: 12px 14px; font-size: .86rem; }
.guide-panel dt { color: var(--brand); font-size: .72rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; margin-top: 8px; }
.guide-panel dd { margin: 1px 0 0; font-weight: 600; }

/* ---- phone view (mobile.html) ---- */
.mob { max-width: 680px; margin: 0 auto; padding: 0 12px 30px; }
.mob-head {
  position: sticky; top: 0; z-index: 20; background: var(--bg);
  padding: 8px 0 6px; border-bottom: 1px solid var(--line); margin-bottom: 8px;
}
/* Every millimetre the header keeps is a half-hour the grid loses, so the
   brand line is as small as it can be and still be itself (2026-09-14). */
.mob-head .brand { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.mob-head .brand img { height: 22px; width: auto; }
.mob-head .brand button { min-height: 32px; padding: 4px 12px; }
.mob-head .brand .who { margin-left: auto; font-size: .78rem; color: var(--muted); font-weight: 700; }
/* The phone's branch toggle (Dr Daniel, 2026-09-01) — same chips as the
   desktop sidebar, a thumb-height row of their own under the logo. */
.mob-branch { display: flex; gap: 6px; margin: 2px 0 6px; }
.mob-branch .br-chip { min-height: 32px; padding: 3px 12px; font-size: .82rem; }
.mob-daynav { display: flex; gap: 6px; align-items: center; }
.mob-daynav button { min-height: 36px; padding: 5px 12px; }
.mob-daynav input[type="date"] { flex: 1; min-height: 40px; }
.mob-appt {
  display: block; width: 100%; text-align: left; background: var(--pc-bg, var(--card));
  color: var(--text); border: 1px solid var(--pc-line, var(--line)); border-radius: 14px;
  padding: 10px 12px 10px 14px; margin-bottom: 8px; position: relative; overflow: hidden;
  box-shadow: var(--shadow); min-height: 0; font-weight: 500; font-size: .9rem;
}
.mob-appt::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 5px;
  background: var(--status-c, var(--muted));
}
.mob-appt .l1 { display: flex; gap: 8px; align-items: baseline; font-weight: 800; }
.mob-appt .l1 .time { font-variant-numeric: tabular-nums; }
.mob-appt .l2 { font-size: .78rem; color: var(--muted); margin-top: 1px; }
.mob-appt.blocked { background: repeating-linear-gradient(45deg, #eceaee, #eceaee 7px, #e0dde4 7px, #e0dde4 14px); }
.mob-fabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 25;
  display: flex; gap: 8px; padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
  background: var(--bg); border-top: 1px solid var(--line);
}
.mob-fabbar button { flex: 1; }
.mob .card { padding: 12px 14px; }
.mob .statusrow { display: flex; gap: 6px; flex-wrap: wrap; }
.mob .statusrow button { flex: 1 1 30%; min-height: 44px; box-shadow: none; }
.mob-photos img { max-height: 120px; max-width: 46vw; border-radius: 10px; display: block; }
.mob-week { display: flex; gap: 2px; }
.mob-week .tcol { position: relative; width: 20px; flex: none; }
.mob-week .tcol span {
  position: absolute; right: 2px; font-size: .58rem; color: var(--muted); font-weight: 700;
}
.mob-wcol { flex: 1; min-width: 0; }
.mob-whd {
  display: block; width: 100%; min-height: 0; padding: 3px 0 4px; border-radius: 8px;
  background: var(--brand-tint); color: var(--brand); font-size: .64rem; font-weight: 800;
  box-shadow: none; line-height: 1.1; text-align: center;
}
.mob-whd span { display: block; font-size: .72rem; }
.mob-whd.today { background: var(--brand); color: #fff; }
.mob-wbody {
  position: relative; margin-top: 2px; background: #fff; border-radius: 8px; cursor: pointer;
  background-image: repeating-linear-gradient(to bottom, transparent 0 47px, var(--line) 47px 48px);
}
.mob-wapp {
  position: absolute; left: 1px; right: 1px; min-height: 0; padding: 0 2px; overflow: hidden;
  border-radius: 4px; background: var(--pc-bg); color: var(--text); font-size: .56rem;
  font-weight: 800; text-align: left; box-shadow: none;
  border: 1px solid var(--pc-line); border-left: 3px solid var(--status-c);
}
.mob-wapp.s-cancelled, .mob-wapp.s-no_show { opacity: .5; text-decoration: line-through; }
.mob-wapp.blocked { background: repeating-linear-gradient(45deg, #eceaee, #eceaee 5px, #e0dde4 5px, #e0dde4 10px); color: var(--muted); }

/* ---- phone: five destinations on a thumb bar (Dr Daniel, 2026-09-14) ----
   Dashboard, Calendar, Notes, Mochi, Mattress. The bar is the last thing on
   the screen, so everything above it keeps clear of both it and the action
   row that floats over it. */
.mob-tabs {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  display: flex; background: var(--card); border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -8px 20px -14px rgba(71, 0, 84, .4);
}
.mob-tab {
  flex: 1; min-height: 54px; border-radius: 0; background: none; color: var(--muted);
  box-shadow: none; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 1px; padding: 6px 2px; font-weight: 700;
}
@media (hover: hover) { .mob-tab:hover { background: var(--brand-tint); box-shadow: none; } }
.mob-tab .ic { font-size: 1.15rem; line-height: 1; }
.mob-tab .lb { font-size: .62rem; letter-spacing: .01em; }
.mob-tab.on { color: var(--brand); background: var(--brand-tint); }
/* The action row sits ON TOP of the tab bar, not over the page's last line. */
.mob-fabbar { bottom: calc(54px + env(safe-area-inset-bottom)); }
.mob-fabbar:empty { display: none; }
.mob-body { padding-bottom: 124px; }
.mob-h { margin: 2px 0 8px; font-size: 1.15rem; }
.mob-h3 { margin: 0 0 6px; font-size: .95rem; }
.mob-line { display: flex; justify-content: space-between; gap: 10px; padding: 4px 0; font-size: .9rem; }
.mob-line + .mob-line { border-top: 1px solid var(--line); }
.mob-kpis { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 10px; }
.mob-kpi { padding: 10px 12px; }
.mob-seg { display: flex; gap: 6px; }
.mob-seg button { flex: 1; min-height: 38px; font-size: .8rem; }
/* A row in a phone list: a line, a quieter line under it, the whole thing a
   target big enough for a thumb. */
.mob-row {
  display: block; width: 100%; text-align: left; background: var(--card); color: var(--text);
  border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 7px;
  box-shadow: none; min-height: 0; font-weight: 500;
}
.mob-row .l1 { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: .92rem; }
.mob-row .l2 { font-size: .76rem; color: var(--muted); margin-top: 2px; }
/* Seven days as bars — figures above, day letters below, today picked out. */
.mob-bars { display: flex; align-items: flex-end; gap: 3px; height: 110px; }
/* min-width: 0 or a long figure widens its own column and pushes the week
   out of the card (Dr Daniel, 2026-09-14). */
.mob-bar { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 2px; }
.mob-bar .bv {
  font-size: .55rem; color: var(--muted); font-weight: 700;
  max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mob-bar .bfill { width: 100%; border-radius: 5px 5px 0 0; background: var(--brand-tint-2); }
.mob-bar .bfill.now { background: var(--brand); }
.mob-bar .bl { font-size: .62rem; color: var(--muted); font-weight: 700; }

/* ---- phone calendar: the desktop grid, narrowed ------------------------
   One column per clinician on duty, time down the side, a booking drawn
   where it actually sits. Whitespace in a column IS availability — the flat
   list this replaced could not show that at all. */
.mcal-title { display: flex; align-items: baseline; gap: 8px; margin: 2px 0 6px; flex-wrap: wrap; }
.mcal-title h2 { margin: 0; font-size: 1.1rem; }
.mcal-title .muted { font-size: .78rem; }
.mcal-scroll {
  overflow: auto; max-height: calc(100vh - 250px); border: 1px solid var(--line);
  border-radius: 12px; background: var(--card); -webkit-overflow-scrolling: touch;
}
.mcal { display: flex; align-items: flex-start; min-width: min-content; }
.mcal-gutwrap { position: sticky; left: 0; z-index: 3; background: var(--card); flex: none; }
.mcal-colwrap { flex: 1 1 0; min-width: 104px; }
.mcal-name {
  position: sticky; top: 0; z-index: 4; min-height: 30px; display: flex; align-items: center;
  justify-content: center; font-size: .72rem; font-weight: 800; color: var(--text);
  background: var(--pc-bg, var(--card)); border-bottom: 1px solid var(--line);
  border-left: 1px solid var(--line); padding: 4px 6px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.mcal-name.gut { background: var(--card); border-left: none; }
.mcal-gutter { position: relative; width: 46px; }
.mcal-gutter span {
  position: absolute; left: 0; right: 4px; text-align: right; transform: translateY(-6px);
  font-size: .62rem; font-weight: 700; color: var(--muted); font-variant-numeric: tabular-nums;
}
.mcal-col { position: relative; border-left: 1px solid var(--line); }
.mcal-slot { position: absolute; left: 0; right: 0; height: 30px; border-top: 1px solid var(--line); }
.mcal-slot.half { border-top-style: dotted; opacity: .6; }
.mcal-appt {
  position: absolute; left: 2px; right: 2px; z-index: 2; display: block; overflow: hidden;
  text-align: left; padding: 2px 4px 2px 6px; border-radius: 7px; min-height: 0;
  background: var(--pc-bg, var(--brand-tint)); color: var(--text); box-shadow: none;
  border: 1px solid var(--pc-line, var(--line)); border-left: 4px solid var(--status-c, var(--muted));
  font-weight: 600; line-height: 1.15;
}
.mcal-appt .tm { display: block; font-size: .6rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.mcal-appt .nm { display: block; font-size: .68rem; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mcal-appt .pu { display: block; font-size: .58rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mcal-appt.s-cancelled, .mcal-appt.s-no_show { opacity: .55; text-decoration: line-through; }
.mcal-appt.blocked {
  background: repeating-linear-gradient(45deg, #eceaee, #eceaee 6px, #e0dde4 6px, #e0dde4 12px);
  color: var(--muted); border-left-color: var(--muted);
}
.mcal-hint { font-size: .74rem; margin: 6px 0 0; }

/* ---- login ---- */
/* The rows open (Dr Daniel, 2026-09-15: "make able to click in and check which
   appt"), so they say so on hover. */
tr.ml-row { cursor: pointer; }
@media (hover: hover) { tr.ml-row:hover > td { background: var(--brand-tint); } }
/* In the drill-down: counted toward nothing, but shown, because a low number
   is exactly when someone wants to see what fell out of it. */
tr.ml-dropped > td { color: var(--muted); text-decoration: line-through; }

/* Whose turn is the next mattress consultation (Dr Daniel, 2026-09-15). The
   row with the fewest is marked, because finding the smallest number in a
   column is work the page can do for you. */
.ml-table { width: 100%; }
.ml-table td.num, .ml-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* An edge, not a fill: five clinicians can sit level on zero, and five filled
   rows is a shout where a marked edge will do. */
tr.ml-next > td:first-child { box-shadow: inset 3px 0 0 var(--brand); }
.ml-flag {
  margin-left: 8px; font-size: .68rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--brand);
}

/* The eye on a password box (Dr Daniel, 2026-09-15). The button sits INSIDE
   the field, so the field makes room for it rather than the button covering
   what you typed. Quiet until you reach for it. */
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 46px; width: 100%; }
button.pw-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; min-height: 0; padding: 0; border: 0;
  background: transparent; box-shadow: none; color: var(--muted);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
button.pw-eye svg { width: 20px; height: 20px; display: block; }
@media (hover: hover) { button.pw-eye:hover { background: transparent; color: var(--brand); } }
button.pw-eye:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 8px; }

.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(160deg, var(--brand) 0%, var(--brand-dark) 100%); padding: 16px; }
.login-card { background: #fff; border-radius: 20px; padding: 30px 32px; width: 100%; max-width: 400px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, .35); }
.login-card .login-logo { display: block; width: 230px; max-width: 80%; height: auto; margin: 0 auto 6px; }

/* ---- print (FPO, customer invoice copy) ---- */
@media print {
  .sidebar, .topbar, .no-print, .toast { display: none !important; }
  .content { padding: 0; max-width: none; }
  body { background: #fff; }
  .card { box-shadow: none; }
  body:has(.modal-backdrop) .shell { display: none !important; }
  .modal-backdrop { position: static; padding: 0; background: #fff; overflow: visible; display: block; backdrop-filter: none; }
  .modal { box-shadow: none; max-width: 100%; margin: 0; padding: 0; animation: none; }
  .modal .actions, .modal .modal-x { display: none !important; }
  /* On paper the calendar has no fold to scroll: unbound the pane, or only the
     hours that happened to be on screen would print (Dr Daniel, 2026-08-30 -
     the pane took its own scrollbar so the header could freeze). The header
     goes back into the flow for the same reason: a sticky row has nothing to
     stick to once the page is not scrolling. */
  .cal-scroll { max-height: none !important; overflow: visible !important; }
  .cal-hdrow { position: static; box-shadow: none; }
  /* Customer invoice copy: one tidy A4 page (Dr Daniel, 2026-09-02). The
     on-screen modal chrome (title bar) duplicates the letterhead, so it goes;
     everything else tightens so a long bill still lands on a single sheet. */
  @page { margin: 12mm; }
  body:has(.inv-print) .modal-head { display: none !important; }
  .modal-backdrop.print-only { visibility: visible; }
  .inv-print { font-size: 11.5px; line-height: 1.35; }
  .inv-print h3 { font-size: 12px; margin: 7px 0 2px; }
  .inv-print p { margin: 3px 0; }
  .inv-print table { margin-top: 4px !important; }
  .inv-print table th, .inv-print table td { padding: 3px 6px; font-size: 11.5px; }
  .inv-print img { width: 120px !important; }
  .inv-print .inv-print-head { margin-bottom: 6px; padding-bottom: 6px; }
  /* Production Detail Confirmation (Dr Daniel, 2026-09-06): the popup body
     scrolls on screen, and printing an element mid-scroll CLIPPED whatever
     stood above the fold ("the details are eaten up on to top") — on paper
     the popup unbinds completely and the document always starts from its
     own letterhead. The popup's title bar would duplicate that letterhead,
     so it goes; brand marks are forced to print even with background
     graphics off. One tidy A4. */
  .modal { max-height: none !important; overflow: visible !important; height: auto !important; }
  body:has(.po-doc) .modal-head { display: none !important; }
  /* The po-doc print compressions live in their own @media print block at
     the very END of the sheet — they must come AFTER the screen .po-doc
     rules, or same-specificity screen rules written later in the file win
     and the print sizes never apply (the 2026-09-06 two-page bug). */
}

/* The print copy never shows on screen — it goes straight to the print
   dialog (Dr Daniel, 2026-09-03). visibility (not display) keeps it in the
   render tree so the printer still sees it. */
.modal-backdrop.print-only { visibility: hidden; }

/* ---- customer invoice copy letterhead (screen + paper) ---- */
.inv-print-head {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 16px; border-bottom: 3px solid var(--brand);
  padding-bottom: 10px; margin-bottom: 12px;
}
.inv-print-co { text-align: right; font-size: .78rem; color: var(--muted); line-height: 1.5; }
.inv-print-co strong { display: block; color: var(--text); font-size: .84rem; }

/* ---- responsive ---- */
/* The menu button belongs to the phone only; on a desk the menu is the
   sidebar and needs no opening (Dr Daniel, 2026-09-14). */
.nav-burger { display: none; }
@media (max-width: 900px) {
  .shell { flex-direction: column; }
  .sidebar { width: 100%; flex: none; height: auto; position: sticky; top: 0; z-index: 30; }
  /* Folded away by default: a page opened on a phone starts with the page,
     not with a screen and a half of navigation. */
  .sidebar nav, .sidebar .foot { display: none; }
  .shell.navopen .sidebar nav { display: flex; flex-wrap: wrap; }
  .shell.navopen .sidebar .foot { display: block; }
  .nav-burger {
    display: block; width: calc(100% - 24px); margin: 2px 12px 8px;
    min-height: 40px; background: var(--brand-tint); color: var(--brand);
    box-shadow: none; font-weight: 800;
  }
  .sidebar nav a { padding: 7px 10px; }
  .cal { grid-template-columns: 40px repeat(7, minmax(80px, 1fr)); }
  .cal-scroll { overflow-x: auto; }
  .content { padding: 14px; }
}

/* ---- phone / tablet header (2026-09-26) ----
   One row: logo · branch pills · ☰ Menu. The rail-reset selectors above
   carry .shell.rail, so the same specificity is repeated here to win. */
@media (max-width: 900px) {
  .sidebar {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center;
    column-gap: 8px; padding: 0 10px 0 0; overflow: visible;
    border-right: none; border-bottom: 1px solid var(--line);
  }
  .sidebar .logo, .shell.rail .sidebar .logo {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px;
    padding: 8px 12px; min-width: 0;
  }
  .sidebar .logo img, .shell.rail .sidebar .logo img { width: 112px; max-width: 112px; flex: none; }
  .sidebar > .sub, .shell.rail .sidebar > .sub { margin: 0; padding: 0; min-width: 0; }
  .nav-burger {
    width: auto; margin: 0; min-height: 36px; padding: 0 14px; font-size: .85rem;
    white-space: nowrap;
  }
  .sidebar nav, .sidebar .foot { grid-column: 1 / -1; }
  .sidebar .foot { padding: 0 12px 10px; }
  .topbar { padding: 6px 12px; min-height: 0; }
  .topbar .pagetitle { font-size: 1rem; }
}
/* On a phone the three branch columns are ~100px each: the booking count in
   the corner ran into the branch name, so it steps aside there. */
@media (max-width: 560px) { .cal-branches .hd .hd-count { display: none; } }

/* ---- motion -------------------------------------------------------------
   Movement here is functional: screens settle in, controls answer the moment
   they are pressed, and anything that changed draws the eye for a moment.
   Nothing loops, nothing delays the work, and the whole layer switches off
   for anyone whose device asks for reduced motion (guard at the end).      */

/* A page settles in section by section. */
.content > * { animation: rise .3s cubic-bezier(.22, .9, .3, 1) backwards; }
.content > *:nth-child(1) { animation-delay: .02s; }
.content > *:nth-child(2) { animation-delay: .06s; }
.content > *:nth-child(3) { animation-delay: .1s; }
.content > *:nth-child(n+4) { animation-delay: .14s; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* Controls answer the press. */
button:active:not(:disabled), .btn:active:not(:disabled) { transform: scale(.976); }
.sidebar nav a { transition: background .12s, color .12s, padding-left .14s ease; }
@media (hover: hover) { .sidebar nav a:hover { padding-left: 16px; } }
.topbar .iconbtn { transition: background .12s, transform .12s; }
/* The topbar's controls grow a little under the cursor (Dr Daniel,
   2026-08-30). They are small targets in a quiet strip, so a size change reads
   faster than a colour change alone. The jump shortcuts are <a>, not <button>,
   so the global button hover never reached them - they need their own rule.
   The reduced-motion block further up flattens the transition for anyone who
   asks for that. */
@media (hover: hover) { .topbar .iconbtn:hover { transform: scale(1.1); } }
.topbar .iconbtn:active { transform: scale(.94); }
.topbar-jump .jump { transition: background .12s, color .12s, transform .12s; }
@media (hover: hover) { .topbar-jump .jump:hover { transform: scale(1.12); } }
.topbar-jump .jump:active { transform: scale(.96); }
/* The blanket reduced-motion rule below only flattens the TRANSITION, which
   would leave the size still jumping - and a size change under the cursor is
   exactly the movement that rule exists to spare people. So drop the scale
   itself for them; the colour change alone still answers the hover. */
@media (prefers-reduced-motion: reduce) {
  .topbar .iconbtn:active, .topbar-jump .jump:active { transform: none; }
@media (hover: hover) { .topbar .iconbtn:hover, .topbar-jump .jump:hover { transform: none; } }
}
.card { transition: box-shadow .18s ease, border-color .18s ease; }
@media (hover: hover) { tr.click:hover { transition: background .12s; } }

/* Numbers that just refreshed deserve a beat of attention. */
.kpi .v { animation: rise .42s .06s cubic-bezier(.22, .9, .3, 1) backwards; }

/* The backdrop fades so the modal does not arrive on a hard black flash. */
.modal-backdrop { animation: fade .14s ease-out; }
@keyframes fade { from { opacity: 0; } }

/* Calendar: blocks fade in when the day (re)loads — the confirmation that a
   drag, a status change or a new booking actually landed. Deliberately no
   movement, so nothing fights the drag-and-drop preview band. */
.cal .appt { animation: fade .16s ease-out; }
.cal .slot { transition: background .1s; }
.cal .slot.drop-top::after, .cal .slot.drop-bottom::after { animation: fade .1s ease-out; }

/* Builder stacks (Mochi pads, mattress layers) settle as they are edited. */
.stack .layer, .stack .side { animation: fade .14s ease-out; }

/* Status pills ease between colours instead of snapping. */
.badge { transition: background .18s ease, color .18s ease; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
}

/* ---- Mattress station and configurator ------------------------------------
   Chips instead of dropdowns, and the price panel sticks.

   These were built at iPad-touch size — 50px chips, 1rem type, 48px inputs —
   which made the configurator far taller than anything else in the system
   (Dr Daniel, 2026-08-29: "the components and buttons dont need to be so big.
   Make it compact and dun use up so many spaces"). They now sit just under the
   42px of an ordinary button: still a comfortable target, a third less height.
   Each state re-asserts BOTH background and colour (cascade lesson learnt). */
.st-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(300px, .9fr); gap: 12px; align-items: start; }
@media (max-width: 920px) { .st-grid { grid-template-columns: 1fr; } }
/* Compact enough that a form of six chip groups still reads at a glance
   (Dr Daniel, 2026-08-29: "make these buttons smaller and more compact, use
   lesser space"). Still a comfortable touch target on the shop-floor station,
   which shares this class. */
.st-chips {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
  gap: 6px; margin: 3px 0 2px;
}
.st-chip {
  background: var(--card); color: var(--text); border: 1.5px solid var(--line);
  border-radius: 10px; padding: 6px 10px; font-size: .8rem; font-weight: 600;
  min-height: 32px; line-height: 1.25; cursor: pointer;
  min-width: 0; overflow-wrap: break-word;
}
@media (hover: hover) { .st-chip:hover { background: var(--brand-tint); color: var(--text); } }
/* Chosen = calm lavender tint with brand ink, outlined in its own colour
   (Dr Daniel, 2026-09-02: "when choose the option, give it a calmer colour")
   — the solid purple block was shouting. */
.st-chip.on {
  background: #e9d5f0; border-color: transparent;
  color: var(--brand); font-weight: 700;
}
@media (hover: hover) { .st-chip.on:hover { background: #eee0f2; color: var(--brand); } }
.st-chip:active { transform: scale(.97); }
/* ---- KumoDoc-style chart cards (2026-09-04) ---- */
.kcard { display: flex; flex-direction: column; padding-bottom: 14px; }
.kcard .kbody { margin-top: 6px; }
.ktitle { text-align: center; font-weight: 800; font-size: 1.02rem; color: #374151; margin: 2px 0; }
.ksub { text-align: center; color: #9ca3af; font-size: .8rem; margin-bottom: 8px; }
.kbody { display: flex; gap: 2px; }
.kytitle {
  writing-mode: vertical-rl; transform: rotate(180deg);
  color: #9ca3af; font-size: .7rem; text-align: center; flex: none;
}
.kmain { flex: 1; min-width: 0; }
.kplotrow { display: flex; }
.kyaxis { position: relative; width: 36px; height: 170px; flex: none; }
.kyaxis span {
  position: absolute; right: 8px; transform: translateY(50%);
  font-size: .66rem; color: #6b7280;
}
.kplot { position: relative; flex: 1; height: 170px; min-width: 0; }
.kgrid, .kline { position: absolute; inset: 0; width: 100%; height: 100%; }
.kcols { position: absolute; inset: 0; display: grid; }
.kcell { position: relative; }
.kbar {
  position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  border-radius: 3px 3px 0 0; max-width: 44px;
}
.kval {
  position: absolute; left: 50%; transform: translateX(-50%);
  font-size: .64rem; font-weight: 700; color: #374151; white-space: nowrap;
}
/* A month of days gives each bar about 30px: the figure goes short and small
   rather than overlapping its neighbours. */
.kval.tight { font-size: .55rem; letter-spacing: -.02em; }
.khit { position: absolute; width: 18px; height: 18px; transform: translate(-50%, -50%); z-index: 2; }
.kdot { position: absolute; inset: 3px; border-radius: 50%; background: #fff; border: 2px solid; }
.kxaxis { display: grid; margin-left: 36px; margin-top: 4px; }
.kxaxis span { text-align: center; font-size: .7rem; color: #374151; font-weight: 600; overflow: hidden; }
.klegend {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  margin-top: 10px; font-size: .76rem; color: #4b5563;
}
.kleg { display: inline-flex; align-items: center; gap: 6px; }
.kleg-ring { width: 11px; height: 11px; border-radius: 50%; border: 2px solid; background: #fff; flex: none; }
.kleg-sq { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* KPI comparison line — the Kumo habit (2026-09-04). */
.kpi-cmp { font-size: .68rem; font-weight: 800; margin-top: 2px; letter-spacing: .01em; }
/* Payment-method chips: each method its own calm tone (2026-09-03) —
   coloured at rest too (Dr Daniel: "when havednt choose, i want it to have
   colours"); picking deepens the fill and bolds the ink. */
/* The CHOSEN method must be unmistakable and the rest greyish (Dr Daniel,
   2026-09-05: "make the chosen button more obvious, other not chosen have
   to be greyish less obvious") — unchosen chips drop their tone for a
   quiet grey; hover previews the tone; chosen wears the deep tone, bold,
   with a ring in its own ink. */
button.pm-chip {
  background: #f2f1f4; color: #9a93a1;
  border-color: transparent;
}
@media (hover: hover) { button.pm-chip:hover { background: var(--tone-bg, #eceef2); color: var(--tone-fg, #55606f); } }
button.pm-chip.on {
  background: var(--tone-deep, #d4d9e0); color: var(--tone-fg, #55606f);
  border-color: transparent; font-weight: 800;
  box-shadow: inset 0 0 0 2px var(--tone-fg, #55606f);
}
@media (hover: hover) { button.pm-chip.on:hover { background: var(--tone-deep, #d4d9e0); color: var(--tone-fg, #55606f); } }

/* ---- Tap-to-answer rows on the consultation card --------------------------
   (Dr Daniel, 2026-08-29: "pls show the common options in buttons so they can
   click rightaway".) Smaller again than the configurator chips: a card carries
   twelve of these groups.

   Each group is a form control in its own right, so it gets the same border as
   the inputs beside it — without that boundary the eye could not tell where one
   question's answers ended and the next began, which was most of what "messy"
   meant. A grid rather than flex: sized by content, a group asks for the width
   of its answers UNWRAPPED (around 860px for the pillow questions) and each one
   took a whole line to itself. Equal columns that wrap their own chips are
   tidier and predictable, and auto-fit collapses the spare track so a
   two-question row still fills the width. */
.row.row-opts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px 16px; align-items: start;
}
.row.row-opts > * { min-width: 0; }
/* Each group wears its COMPONENT's tone (Dr Daniel, 2026-08-29). Shape,
   material and duration are three questions about one pillow, so they share a
   tone; the mattress questions share another. Colouring them separately said
   they were unrelated, which is the opposite of true.
   The tone arrives as --tone-bg / --tone-fg on the group, so a field added
   later needs no new CSS; a group with no tone falls back to a plain card. */
/* The tone marks the group without filling it (Dr Daniel, 2026-09-14: five
   solid colours down one form read as a quilt). The colour stays where it
   carries meaning — the hairline and the chosen chip. */
.cn-opts {
  border: 1px solid var(--tone-fg, var(--line)); border-radius: 12px;
  padding: 7px 9px 8px; background: transparent;
}
/* A group's own border is quiet until you are working in it. */
.cn-opts { border-color: color-mix(in srgb, var(--tone-fg, var(--line)) 28%, #fff); }
.cn-opts:focus-within { border-color: var(--tone-fg, var(--brand)); }
.cn-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* Sleeping habit as percentages (Dr Daniel, 2026-09-01):
   Supine _%  Side _%  Back _% on one line, in the habit tone. */
.pct-trio .cn-chips { align-items: center; gap: 4px; }
.pct-trio .pct-name { font-size: .78rem; font-weight: 700; color: var(--tone-fg, var(--text)); margin-left: 6px; }
.pct-trio .pct-name:first-child { margin-left: 0; }
.pct-trio .pct-box { width: 64px; min-height: 34px; text-align: right; }
.pct-trio .pct-legacy { font-size: .72rem; margin-left: 8px; }
.cn-chip {
  background: var(--card); color: var(--text); border: 1.5px solid var(--line);
  border-radius: 14px; padding: 6px 11px; font-size: .8rem; font-weight: 600;
  min-height: 0; line-height: 1.25; box-shadow: none;
}
@media (hover: hover) { .cn-chip:hover:not(:disabled) { background: #fff; border-color: var(--tone-fg, var(--brand)); } }
.cn-chip.on {
  background: var(--tone-fg, var(--brand)); border-color: var(--tone-fg, var(--brand)); color: #fff;
}
@media (hover: hover) { .cn-chip.on:hover { background: var(--tone-fg, var(--brand-dark)); color: #fff; } }
/* The add-on cover chip: borderless wash, a step below the chosen item
   (Dr Daniel, 2026-09-05: "remove the outer line. leave a gap"). */
.cn-chip.cover-addon { border: none; background: var(--neutral-bg, #f1eef3); padding: 7px 12px; }
/* Pending marker (Dr Daniel, 2026-09-05, third pass: "revert the button
   design to just bold words like before, but now only write pending, and
   put it on bright red"). */
.pending-badge {
  color: #e5231b; font-weight: 800; letter-spacing: .03em;
  white-space: nowrap; vertical-align: middle;
}

/* Greyish-white action buttons (Dr Daniel, 2026-09-05: the factory
   card's Edit / Edit-details buttons) — quiet until hovered. */
.soft-btn { background: #f4f3f5; color: #57505e; border: 1px solid #e6e3e9; box-shadow: none; font-weight: 600; }
@media (hover: hover) { .soft-btn:hover:not(:disabled) { background: #eae7ee; color: #3f3946; } }
/* ✏️ Edit order pops a touch more than the soft greys around it (Dr
   Daniel, 2026-09-06: "Edit button colour not obvious enough") — a light
   brand wash with purple ink, still calm. */
.edit-btn {
  background: var(--brand-tint, #f5ecf7); color: var(--brand, #470054);
  border: 1px solid #d9c3e0; box-shadow: none; font-weight: 700;
}
@media (hover: hover) { .edit-btn:hover:not(:disabled) { background: var(--brand-tint-2, #ecdcf1); box-shadow: none; } }

/* Factory accept/decline on clinic changes (Dr Daniel, 2026-09-05):
   calm washes, riding the same row as the request. Scoped under the
   banner because .amended-banner button paints everything amber. */
.amend-accept, .amended-banner button.amend-accept {
  background: #e3f0e6; color: #1d5a33; border: none; box-shadow: none;
  font-weight: 700; padding: 6px 12px;
}
@media (hover: hover) { .amend-accept:hover:not(:disabled), .amended-banner button.amend-accept:hover { background: #d3e6d8; } }
.amend-decline, .amended-banner button.amend-decline {
  background: #fbe4e2; color: #99291f; border: none; box-shadow: none;
  font-weight: 700; padding: 6px 12px;
}
@media (hover: hover) { .amend-decline:hover:not(:disabled), .amended-banner button.amend-decline:hover { background: #f5d4d1; } }

/* The construction must-choose block glows softly until answered
   (Dr Daniel, 2026-09-05: "more prominent so that it is a must choose"). */
.notice.altera-ask { background: #fdf6e7; border: 1px solid #e5c878; }

/* ---- Production Detail Confirmation (approved design 2026-09-06;
   reply-to-confirm revision 2026-09-07) ---------------------------------
   Letterhead with the caps title block on the right, purple hero ("Made
   for you. Checked by you."), numbered sections, rounded soft panels,
   the mattress facts beside the illustration with colour-dot side
   labels, the name-and-sleeping-side review panel (neutral grey-white
   since the revision), then the lavender "Ready to confirm?" panel and
   the prose terms line. Customers confirm by REPLYING to the
   accompanying message — the document carries no checkboxes and no
   signature rules, and the closing panel is an instruction, not a
   button. Colours are lifted from the approved PDF itself. */
.po-doc {
  --pod-purple: #511a65; --pod-ink: #2e2733; --pod-grey: #756d7b;
  --pod-panel: #f7f4f8; --pod-lav: #f2eaf4; --pod-line: #9a81a5;
  color: var(--pod-ink); font-size: .88rem; line-height: 1.45;
}
.po-doc .po-hd { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 12px; }
.po-doc .po-logo { width: 146px; height: auto; }
.po-doc .po-hd-meta { text-align: right; display: grid; gap: 3px; font-size: .78rem; color: var(--pod-grey); }
.po-doc .po-hd-title {
  font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--pod-purple); font-size: .74rem;
}
.po-doc .po-hero {
  background: var(--pod-purple); border-radius: 14px;
  padding: 16px 20px 14px; margin: 0 0 14px; color: #fff;
}
.po-doc .po-hero h1 { margin: 0 0 6px; font-size: 1.72rem; letter-spacing: -.01em; color: #fff; }
.po-doc .po-hero-hi { margin: 0 0 8px; font-size: 1rem; }
.po-doc .po-hero-sub { margin: 0; font-size: .8rem; color: #eee4f1; }
.po-doc .po-sec-head { display: flex; align-items: center; gap: 11px; margin: 18px 0 8px; }
.po-doc .po-num {
  width: 27px; height: 27px; flex: none; border-radius: 9px;
  background: var(--pod-purple); color: #fff; font-weight: 800; font-size: .74rem;
  display: inline-flex; align-items: center; justify-content: center;
}
.po-doc .po-sec-head h2 { margin: 0; font-size: 1.08rem; color: var(--pod-purple); flex: 1; }
/* Panels sit flat on the page (Dr Daniel, 2026-09-07: "Remove the grey
   background") — only the review callout and the confirm panel keep a
   fill of their own. */
.po-doc .po-panel { border-radius: 10px; padding: 12px 15px; }
.po-doc .po-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 20px; }
.po-doc .po-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.po-doc .po-lab {
  font-size: .62rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .08em; color: var(--pod-grey);
}
.po-doc .po-val { font-weight: 600; overflow-wrap: anywhere; }
.po-doc .po-matt { display: grid; grid-template-columns: 1.05fr .95fr; gap: 16px; align-items: start; }
.po-doc .po-matt-left { display: flex; flex-direction: column; gap: 12px; }
.po-doc .po-size { display: flex; flex-direction: column; gap: 3px; }
.po-doc .po-size-val { font-size: 1.2rem; font-weight: 800; color: var(--pod-purple); }
.po-doc .po-loose { padding: 0 3px; }
/* The review panel holds plain ROWS (2026-09-07 revision): neutral
   grey-white ground, purple row headings, a subtle grey divider — no
   checkboxes, customers confirm by replying to the message. */
.po-doc .po-callout { background: #f5f5f4; padding: 2px 0; }
.po-doc .po-crow { padding: 10px 15px; margin: 0; }
.po-doc .po-crow + .po-crow { border-top: 1px solid #e4e4e1; }
.po-doc .po-crow .po-lab { color: var(--pod-purple); }
.po-doc .po-crow-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.po-doc .po-callout-name { font-size: 1.16rem; font-weight: 800; color: var(--pod-purple); }
.po-doc .po-callout-sub { font-size: .78rem; }
.po-doc .po-matt-right { text-align: center; }
.po-doc .po-matt-right img { width: 100%; max-width: 300px; height: auto; background: none; }
.po-doc .po-fig-labs { display: flex; justify-content: space-around; margin-top: 2px; gap: 12px; }
.po-doc .po-fig-labs > div { display: flex; flex-direction: column; align-items: center; gap: 1px; font-size: .8rem; font-weight: 700; }
.po-doc .po-fig-labs strong {
  font-size: .6rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .07em; color: var(--pod-grey);
}
.po-doc .fl-dot { width: 8px; height: 8px; border-radius: 50%; margin-bottom: 2px; }
.po-doc .fl-dot.blue { background: #3454c8; }
.po-doc .fl-dot.red { background: #de2828; }
/* One row, however many panels the order carries (Dr Daniel, 2026-09-07:
   "make these 3 component in one row") — auto-flow columns split the
   width equally between one, two or three panels. */
.po-doc .po-items-row { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 12px; margin-bottom: 10px; }
.po-doc .po-items { border-radius: 10px; padding: 11px 14px; }
.po-doc .po-items h4 {
  margin: 0 0 7px; font-size: .64rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .08em; color: var(--pod-purple);
}
/* Qty hugs the item name (Dr Daniel, 2026-09-07: "The qty column make it
   nearer to the item description") instead of flushing to the far edge. */
.po-doc .po-item { display: flex; gap: 10px; padding: 2.5px 0; font-size: .8rem; }
.po-doc .po-item strong { flex: none; }
.po-doc .po-none { margin: 0; font-size: .78rem; color: var(--pod-grey); }
.po-doc .po-cta { background: var(--pod-lav); border-radius: 10px; padding: 12px 15px; margin-top: 16px; }
.po-doc .po-cta h3 { margin: 0 0 4px; font-size: .92rem; color: var(--pod-purple); }
.po-doc .po-cta p { margin: 0; font-size: .78rem; }
.po-doc .po-cta p + p { margin-top: 3px; }
.po-doc .po-cta strong { color: var(--pod-purple); }
.po-doc .po-before { margin-top: 12px; }
.po-doc .po-terms-p { margin: 0; font-size: .76rem; }

/* Separate construction (Dr Daniel, 2026-09-06, "split the base vs
   topper/altera into 2 components"): the comfort piece and the base piece
   are TWO boxes stacked with a small labelled gap — not one box with a
   dotted band inside. */
.stack-row .stack-col { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.stack-col .mattress { flex: none; border-radius: 16px; }
/* The two split kinds wear different shells (Dr Daniel, 2026-09-06): a
   Topper piece outlines in WHITE, an Altera piece in LUNOX purple — and
   the Altera's line is DOUBLE (a second ring just outside the border). */
.stack-col .mattress.split-topper { border-color: #fff; }
/* The Altera piece reverted to the ORIGINAL grey shell (Dr Daniel,
   2026-09-06: "revert back to your ori design. this one nicer") — no rule
   needed; it falls back to the standard .mattress border. */
/* Construction chips carry tiny drawings in the preview's own language
   (Dr Daniel, 2026-09-06): one piece / white-outline piece over the base /
   double-outline piece over the base. A tinted plate keeps the white
   outline readable on the white button. */
.cons-ico {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 3px;
  width: 40px; padding: 5px; border-radius: 8px; background: #e9def0; flex: none;
  vertical-align: middle;
}
.cons-ico .ci-one {
  height: 19px; border-radius: 5px; border: 2px solid #7a6a80;
  background: linear-gradient(#fff 0 55%, var(--brand, #470054) 55%);
}
.cons-ico .ci-piece { height: 9px; border-radius: 4px; background: #f7f2fa; border: 2px solid #fff; }
/* The altera piece wears the standard grey shell, matching the reverted
   preview (Dr Daniel, 2026-09-06: "remove the double outline"). */
.cons-ico.altera .ci-piece { border-color: #7a6a80; }
.cons-ico .ci-base {
  height: 10px; border-radius: 4px; border: 2px solid #2e0036;
  background: var(--brand, #470054);
}
.pickbtn .cons-ico { margin-right: 4px; }
/* Flip sides wears the same greyish white as the soft buttons (Dr Daniel,
   2026-09-06, superseding the sky tone from earlier the same day) — one
   family with Edit order / Edit Configuration. */
.flip-btn {
  background: #f4f3f5; color: #57505e; border: 1px solid #e6e3e9;
  box-shadow: none; font-weight: 600;
}
@media (hover: hover) { .flip-btn:hover { background: #eae7ee; color: #3f3946; box-shadow: none; } }

/* Compact construction chips (Dr Daniel, 2026-09-06: "Construction button
   no need so big") — icon and label side by side on one tight row. */
.cons-grid .pickbtn {
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  padding: 7px 10px; min-height: 0; font-size: .8rem; line-height: 1.2;
  text-align: left; border-radius: 10px;
  /* A grid column will not shrink past its content unless it is told it may;
     without this the three construction chips pushed themselves off the side
     of a phone (Dr Daniel, 2026-09-14). */
  min-width: 0;
}
.cons-grid .pickbtn span { min-width: 0; overflow-wrap: anywhere; }
/* Three across is a desk's luxury; a phone takes them in one column, at full
   width, where the label can be read. */
@media (max-width: 620px) {
  .pick-grid.cons-grid { grid-template-columns: minmax(0, 1fr) !important; }
}
.cons-grid .cons-ico { width: 30px; padding: 3px 4px; gap: 2px; margin: 0; }
.cons-grid .cons-ico .ci-one { height: 13px; border-width: 1.5px; }
.cons-grid .cons-ico .ci-piece { height: 6px; border-width: 1.5px; }
.cons-grid .cons-ico .ci-base { height: 7px; border-width: 1.5px; }

/* The standalone Topper / Altera Topper products wear the same shells
   (Dr Daniel, 2026-09-06: "make topper and altera configuration same
   design"). */
.stack.topper .mattress.kind-topper { border-color: #fff; }
/* kind-altera keeps the ORIGINAL grey shell (reverted 2026-09-06). */
.stack-col .split-note {
  padding: 6px 10px; text-align: center;
  font-size: .66rem; font-weight: 800; letter-spacing: .06em; color: var(--brand);
}
.stack-col .split-base .layer.base { border-top: none; }
@media (hover: hover) { .cn-chip.cover-addon:hover:not(:disabled) { border: none; background: var(--brand-tint); } }
.cn-chip.cover-addon.on { background: var(--brand); color: #fff; }
/* "Other…" reads as the escape hatch it is, not as one more answer. */
.cn-chip.other { color: var(--muted); font-style: italic; padding: 6px 9px; font-weight: 500; }
.cn-chip.other.on { color: #fff; font-style: normal; }
.cn-chip.clear { padding: 6px 9px; color: var(--muted); }
.cn-chip:disabled { opacity: .55; cursor: default; box-shadow: none; }
@media (hover: hover) { .cn-chip:disabled:hover { background: var(--card); border-color: var(--line); } }
@media (hover: hover) { .cn-chip.on:disabled:hover { background: var(--brand); color: #fff; } }
.cn-opts input { margin-top: 7px; max-width: 340px; }

/* An icon-only action: square, so it reads as a control rather than as a
   button whose label failed to load. Its name is on hover and on aria-label.
   The icon is drawn larger than it would be beside text — a 16px mark in a 42px
   button is doing all the work and has room to spare (2026-08-29). */
.modal-actions .iconlabel, .iconlabel { padding: 10px; width: 42px; flex: none; }
.iconlabel .ic, .iconlabel .ic svg { width: 20px; height: 20px; }

/* The two catalogue recommendations: a dashed field reads as "this opens a
   chooser", and fills in solid once something is chosen. */
.cn-pick {
  flex: 1; min-width: 0; justify-content: flex-start; text-align: left;
  background: var(--card); color: var(--muted); border: 1.5px dashed var(--line);
  border-radius: 12px; padding: 9px 12px; font-size: .82rem; font-weight: 600;
  min-height: 0; line-height: 1.25; box-shadow: none;
}
@media (hover: hover) { .cn-pick:hover:not(:disabled) {
  background: #fff; border-color: var(--tone-fg, var(--brand));
  color: var(--tone-fg, var(--brand)); box-shadow: none;
} }
.cn-pick.on { border-style: solid; border-color: var(--tone-fg, var(--brand)); color: var(--text); }
.cn-pick:disabled { opacity: .55; cursor: default; }
@media (hover: hover) { .cn-pick:disabled:hover { background: var(--card); border-color: var(--line); color: var(--muted); } }

.st-step { display: inline-flex; align-items: center; gap: 7px; }
.st-step button {
  width: 40px; height: 40px; min-height: 0; border-radius: 11px; font-size: 1.2rem; padding: 0;
  background: var(--brand-tint-2); color: var(--brand); font-weight: 800;
}
@media (hover: hover) { .st-step button:hover { background: var(--brand-tint); color: var(--brand); } }
.st-step .v { min-width: 66px; text-align: center; font-size: 1rem; font-weight: 800; }
.st-price { position: sticky; top: 12px; }
/* The running total is the one thing here that stays big — it is what the
   customer is looking at. */
.st-total { font-size: 1.8rem; font-weight: 800; color: var(--brand); letter-spacing: -.02em; }
.st-select { font-size: .92rem; min-height: 40px; width: 100%; }
.station-page h2 { font-size: 1rem; margin-top: .85rem; }
.station-page input[type="text"], .station-page input[type="number"] { font-size: .95rem; min-height: 40px; }
.st-level { border: 1.5px solid var(--line); border-radius: 12px; padding: 9px 11px; margin: 6px 0; background: var(--card); }
.st-level .lvhead { display: flex; align-items: center; gap: 8px; margin-bottom: 3px; }
.st-level .lvhead h3 { margin: 0; flex: 1; }
.st-split { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; }
.st-split > div + div { border-left: 1.5px solid var(--line); padding-left: 11px; }

/* ---- calendar "now" line: red, moving with the clock ---- */
.cal .now-line {
  position: absolute; height: 0; border-top: 2px solid #dc2626;
  z-index: 5; pointer-events: none;
}
.cal .now-line::before {
  content: ""; position: absolute; left: -5px; top: -5px;
  width: 8px; height: 8px; border-radius: 50%; background: #dc2626;
}
.cal .now-line .now-time {
  /* In the slot BELOW the red rule, not straddling it (Dr Daniel,
     2026-09-07: "the display time … shall be in the slot below the
     thicker horizontal line"). */
  position: absolute; left: -50px; top: 3px; font-size: .68rem;
  font-weight: 800; color: #fff; background: #dc2626;
  padding: 1px 5px; border-radius: 7px;
}

/* Invoice actions: the "add something" row sits above the row that decides the
   bill's fate, so a mis-tap on ➕ can never hit Cancel or Issue
   (Dr Daniel, 2026-08-23). */
.build-row { margin-bottom: 6px; }
/* The two "add something" buttons sit above the cart and are reached often but
   are not the point of the page, so they are deliberately smaller than an
   ordinary button (Dr Daniel, 2026-08-29). */
.build-row button {
  min-height: 34px; padding: 5px 13px; font-size: .8rem; line-height: 1.25;
}

/* The two ends of a draft — cancel it, or issue it — sit side by side, so they
   are the same height (Dr Daniel, 2026-08-29). They cannot be left to work it
   out from a shared min-height: Issue carries 1rem text and Cancel only an
   icon, so the same rule rendered them 4px apart. Both are pinned to one value
   here, and the border widths match, or they would still differ by a pixel. */
/* Outline, not fill (Dr Daniel, 2026-08-30): the colour is in the border and
   the mark, the middle stays empty. For actions that are available without
   being what you came to do. */
button.outline-act {
  background: transparent; border: 1.5px solid var(--brand); color: var(--brand);
  box-shadow: none;
}
@media (hover: hover) { button.outline-act:hover { background: var(--brand-tint); box-shadow: none; } }

/* The opposite: keep the fill, drop the border (Dr Daniel, 2026-08-30). Some
   buttons carry a border from their colour class - .go and .danger both do -
   and in a row where only ONE thing is outlined, the rest should not be. */
button.no-outline { border: none; }

/* No fill at all: the label and the mark carry the colour (Dr Daniel,
   2026-08-30). For the quiet actions beside a decisive one. */
button.no-fill { background: transparent; box-shadow: none; }
@media (hover: hover) { button.no-fill:hover { background: var(--brand-tint); box-shadow: none; } }
/* `.ghost.danger` carries its own fill at a higher specificity than `.no-fill`,
   so the two destructive marks - remove this line, delete this draft - need the
   combination spelled out or they keep their pink. The red stays in the mark
   itself, and the hover answers in red rather than borrowing the purple tint. */
button.ghost.danger.no-fill, button.danger.no-fill {
  background: transparent; box-shadow: none;
}
@media (hover: hover) { button.ghost.danger.no-fill:hover, button.danger.no-fill:hover {
  background: #fbe7e5; box-shadow: none;
} }

/* Sleepy Points spend in the gold of their own mark (Dr Daniel, 2026-08-30):
   redeeming points is not the same act as taking money, so the button does not
   borrow the green that issues the bill. Taken from the moon in the icon
   (#f2cd72 on #9a7118) rather than picked to sit near it. */
button.tone-sleepy {
  background: #fbf1d5; color: #7f5d10; border: none; box-shadow: none;
}
@media (hover: hover) { button.tone-sleepy:hover { background: #f6e7bd; color: #6b4e0b; box-shadow: none; } }
button.tone-sleepy:active { background: #efdca6; }

/* Fill AND outline: the light purple stays, the border marks it out
   (Dr Daniel, 2026-08-30). */
button.tint-outline {
  background: var(--brand-tint); color: var(--brand);
  border: 1.5px solid var(--brand); box-shadow: none;
}
@media (hover: hover) { button.tint-outline:hover { background: var(--brand-tint-2); box-shadow: none; } }

/* Calm fills that are nobody else's colour on these screens: sky for the
   factory shortcut, teal for rescheduling. Neither collides with the five
   solid status chips above them, nor with the green that takes money. */
button.tone-lav { background: #efeaf7; color: #5b4a86; border: none; box-shadow: none; }
@media (hover: hover) { button.tone-lav:hover { background: #e3daf2; color: #4b3c72; box-shadow: none; } }
button.tone-sky { background: #e6f0f8; color: #3f6b8e; border: none; box-shadow: none; }
@media (hover: hover) { button.tone-sky:hover { background: #d7e6f3; color: #35597a; box-shadow: none; } }

/* Signing is irreversible, so it neither matches the ordinary purple actions
   nor shouts like the green one: a calm yellow with its own outline. */
button.sign-act {
  background: #fdf4e3; color: #7a5410; border: 1.5px solid #e0a33a; box-shadow: none;
}
@media (hover: hover) { button.sign-act:hover { background: #f9e9cd; color: #6a4809; box-shadow: none; } }
button.sign-act:active { background: #f4dfb8; }

/* Reschedule sits with the status chips but is not one of them: outlined, and
   pushed away from the five so the eye reads it as a different kind of answer
   (Dr Daniel, 2026-08-30). */
.resched-btn {
  margin-left: 10px; border: none; background: #e4f1f0; color: #3d726e;
  box-shadow: none; min-height: 38px; padding: 6px 14px; font-size: .84rem;
}
@media (hover: hover) { .resched-btn:hover { background: #d3e8e6; color: #2f5d5a; box-shadow: none; } }

/* An amended invoice keeps its original number, so the tag is what says which
   copy you are holding (Dr Daniel, 2026-08-30). */
.amend-tag {
  display: inline-flex; align-items: center; align-self: center;
  background: #fdf0d8; color: #8a5a00; border: 1px solid #e0a33a;
  border-radius: 999px; padding: 2px 10px;
  font-size: .68rem; font-weight: 800; letter-spacing: .05em; white-space: nowrap;
}
.amend-tag.withdrawn { background: #fdeaea; color: #97321f; border-color: #d98274; }
.inv-no .amend-tag { font-size: .6rem; padding: 1px 7px; margin-right: 5px; }

/* Room above the pair that ends the draft (Dr Daniel, 2026-08-30) - the last
   cart line should not sit right on top of Delete / Issue. */
.cart-actions { margin-top: 18px; }

/* 56px -> 46px (Dr Daniel, 2026-08-30): still the largest pair on the page,
   without owning a whole band of it. */
button.primary-action.go, button.cancel-draft { min-height: 46px; }
button.primary-action.go { padding: 9px 20px; font-size: .92rem; }
/* The bin alone now, so it is padded to a square rather than left the width
   of the word it no longer carries (Dr Daniel, 2026-08-30). */
button.cancel-draft { padding: 9px 14px; border-width: 2px; font-size: .92rem; }

/* ---- lorry board ---- */
.day-card .run + .run { margin-top: 14px; padding-top: 10px; border-top: 1px dashed var(--line); }
.day-card .run h3 { color: var(--brand); margin: 0 0 6px; }
/* Disposal is easy to miss and expensive to miss: the lorry turns up without
   room for the old mattress. It is called out wherever the order appears. */
.warn-flag { display: inline-block; font-size: .72rem; font-weight: 800; color: #8a5a00;
  background: #fff7e6; border: 1px solid #e0b473; border-radius: 999px; padding: 0 7px; margin-top: 3px; }
.ok-flag { font-size: .74rem; font-weight: 700; color: var(--good, #059669); }
/* The delivery board's address column: readable, but not allowed to push
   the editable columns off the edge (Dr Daniel, 2026-09-12). */
.rfd-addr { font-size: .8rem; color: var(--muted); max-width: 230px; }
/* Wide enough that the promised date and time always share one line. */
.rfd-slot { min-width: 216px; }
/* CS remarks read as a line to write on, not another box in a row full of
   them (Dr Daniel, 2026-09-12). */
input.rfd-remark {
  border: none; border-bottom: 1px solid var(--line); border-radius: 0;
  background: transparent; padding-left: 2px; padding-right: 2px;
}
input.rfd-remark:focus {
  outline: none; border-bottom-color: var(--brand); background: transparent;
}
/* Saved, the remark is just words — the underline belongs to the box you are
   typing in, not to the note you already wrote (Dr Daniel, 2026-09-12: "click
   tick, then it'll show it there without the underline"). A pencil opens it
   again; the tick that saves it is green, the ✕ beside it changes nothing. */
.rfd-remarkrow { display: flex; align-items: center; gap: 2px; }
.rfd-remarktext { font-size: .8rem; white-space: pre-wrap; word-break: break-word; }
.rfd-remarkrow > button.rfd-tick { color: var(--ok); }
/* The board scrolls INSIDE a box no taller than the screen, so the
   left-right bar is always within reach instead of hiding at the foot of a
   long list (Dr Daniel, 2026-09-12: "Freeze the drag left and right bar"),
   and the headings stay put above the rows. */
.rfd-scroll { overflow: auto; max-height: calc(100vh - 250px); min-height: 180px; }
.rfd-scroll table { margin: 0; }
.rfd-scroll th {
  position: sticky; top: 0; z-index: 2;
  background: var(--card); box-shadow: 0 1px 0 var(--line);
}
/* The status IS the control (Dr Daniel, 2026-09-12: "Ready for Delivery
   status label make it a dropdown box instead. To change the status to
   Delivered"). It keeps the badge's shape and the badge's colour — which
   comes from the same .b-<status> classes the pill used — so the board still
   reads as a column of status pills, each one openable. */
select.rfd-statussel {
  width: auto; min-height: 0; padding: 3px 22px 3px 11px;
  border: none; border-radius: 999px;
  font-size: .74rem; font-weight: 700; white-space: nowrap;
  cursor: pointer; appearance: none;
  /* The caret is drawn in, since removing the native appearance takes the
     browser's own away with it. */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 10px center, right 6px center;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
select.rfd-statussel:focus { box-shadow: 0 0 0 3px var(--brand-tint-2); }
@media (hover: hover) { select.rfd-statussel:hover { filter: brightness(.96); } }
select.rfd-statussel[disabled] { opacity: .6; cursor: progress; }
/* The options themselves render in the system menu, which does not inherit
   the pill's colours — keep them plainly readable. */
select.rfd-statussel option { background: var(--card); color: var(--text); font-weight: 600; }
/* The disposal flag rides beside the product rather than wrapping under it. */
.rfd-disposal { font-size: .66rem; padding: 0 6px; white-space: nowrap; margin-top: 2px; }

/* Money still owed on an order that is about to leave the building
   (Dr Daniel, 2026-09-12) — red, under the status, impossible to skim past. */
.due-flag {
  font-size: .76rem; font-weight: 800; color: var(--bad);
  margin-top: 3px; white-space: nowrap;
}
tr.row-ready td { background: #f2fbf6; }
/* Day close: a variance should be impossible to skim past. */
tr.row-variance td { background: #fff7e6; }
tr.total-row td { border-top: 2px solid var(--line); }

/* ---- Director Dashboard ----
   One page across four systems. Two ideas carry the layout: a source strip
   that says which systems are actually answering, and an attention list that
   sits ABOVE every number — the director should meet his to-do list before he
   meets his revenue. Brand primaries only; there is no traffic-light palette
   here beyond the semantic tokens the rest of the app already uses. */
.panel-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: .5rem;
}
.src-strip { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }
.src {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px; border-radius: 999px; font-size: .78rem; font-weight: 600;
  background: var(--card); box-shadow: var(--shadow);
}
.src-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.src.on .src-dot { background: var(--ok); }
.src.off .src-dot { background: var(--bad); }
.src.off { color: var(--muted); }
.src-age { color: var(--muted); font-weight: 500; }

/* An item waiting on the director. The severity rail is the only colour. */
.att {
  display: grid; grid-template-columns: 84px 1fr auto; gap: 12px;
  align-items: start; padding: 11px 0; border-bottom: 1px solid var(--line);
}
.att:last-child { border-bottom: none; }
.att-sev {
  font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  padding: 3px 8px; border-radius: 6px; text-align: center;
}
.att-bad .att-sev { background: var(--bad-bg); color: var(--bad); }
.att-warn .att-sev { background: var(--warn-bg); color: var(--warn); }
.att-info .att-sev { background: var(--info-bg); color: var(--info); }
.att-where { color: var(--muted); font-size: .78rem; white-space: nowrap; padding-top: 3px; }

/* A system that is dark says why, and what would light it. */
.dark-panel { background: var(--brand-tint); border-radius: 12px; padding: 14px 16px; }
.dark-title { font-weight: 800; color: var(--brand); font-size: .82rem; text-transform: uppercase; letter-spacing: .05em; }

/* One bar row, used by BOTH the dashboard and the Director page. The track
   is the only flexible part, so a long figure shrinks the bar instead of
   pushing itself out of the card — which is what the dashboard's own
   hand-rolled version did once the values ran to six figures. */
.dbar { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
.dbar-l {
  flex: none; color: var(--muted); font-size: .8rem; font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dbar-t { flex: 1 1 auto; min-width: 0; height: 14px; background: var(--brand-tint); border-radius: 4px; overflow: hidden; }
.dbar-f { height: 100%; background: var(--brand); border-radius: 4px; min-width: 2px; }
.dbar-v {
  flex: none; color: var(--muted); font-size: .8rem; font-variant-numeric: tabular-nums;
  min-width: 74px; text-align: right; white-space: nowrap;
}

.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; }
@media (max-width: 720px) {
  .att { grid-template-columns: 1fr; gap: 4px; }
  .att-sev { justify-self: start; }
  .att-where { white-space: normal; }
}

/* The branch switcher looks like the control it is (2026-08-26). Every login
   can now switch, so the switcher has to be findable by someone who has never
   been told it exists. */
.sidebar .logo select.branch-switch {
  display: block; width: 100%; margin-top: 8px; padding: 7px 30px 7px 10px;
  font: 600 .72rem/1.2 inherit; letter-spacing: .08em; color: var(--ink, #2b0f33);
  background: var(--panel, #fff); border: 1px solid var(--line, #ebe0e8);
  border-radius: 9px; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%236d5974' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
  min-height: 0;
}
@media (hover: hover) { .sidebar .logo select.branch-switch:hover { border-color: var(--brand, #470054); } }

/* FPO priority + post-edit flag (2026-08-26). ASAP is loud, later is calm,
   and NEWLY EDITED is impossible to scroll past on the factory floor. */
.prio-asap {
  display: inline-block; background: #fde8e6; color: #a3352c; font-weight: 700;
  padding: 3px 10px; border-radius: 999px; font-size: .74rem; letter-spacing: .04em;
  border: 1px solid rgba(163, 53, 44, .35); white-space: nowrap;
}
.prio-later {
  display: inline-block; background: #e8eef8; color: #23508f; font-weight: 700;
  padding: 3px 10px; border-radius: 999px; font-size: .74rem; letter-spacing: .04em;
  border: 1px solid rgba(35, 80, 143, .3); white-space: nowrap;
}
.amended-banner {
  background: #fdf3df; border: none; border-radius: 12px;
  padding: 12px 14px; margin: 8px 0;
}
/* The Noted button: calm amber tint but unmissable on the pale banner
   (Dr Daniel, 2026-09-02) — its ink matches the banner's warning hue. */
.amended-banner button {
  background: #f4d78a; color: #6b4708; border: none; box-shadow: none;
  font-weight: 800; padding: 9px 16px;
}
@media (hover: hover) { .amended-banner button:hover { background: #eec96b; } }
.amended-banner s { text-decoration-color: rgba(163, 53, 44, .6); }

/* Factory portal accordion (2026-08-26): a clean list of order + customer,
   tap to open. The flags that must never hide ride on the closed row. */
.fo-card { padding: 0; overflow: hidden; }
.fo-card.open { padding-bottom: 14px; }
.fo-card.open .fo-head { border-bottom: 1px solid var(--line, #ebe0e8); }
.fo-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 13px 16px; cursor: pointer; user-select: none; min-height: 52px;
}
@media (hover: hover) { .fo-head:hover { background: var(--wash, rgba(71,0,84,.05)); } }
.fo-chev { color: var(--muted, #6d5974); transition: transform .14s ease; flex: 0 0 auto; }
.fo-chev.open { transform: rotate(90deg); }
.fo-id { display: flex; flex-direction: column; min-width: 150px; }
/* The quiet type word beside the FPO number (Dr Daniel, 2026-09-06): the
   left-border colour alone let a courier card pass for a mattress PO. */
.fo-type {
  font-size: .58rem; font-weight: 800; letter-spacing: .08em;
  color: var(--muted); text-transform: uppercase; white-space: nowrap;
}
.fo-id .muted { font-size: .74rem; }
.fo-cust { flex: 1 1 auto; font-weight: 600; min-width: 120px; }
.fo-flags { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.fo-card.open > :not(.fo-head) { margin-left: 16px; margin-right: 16px; }
.edited-chip {
  display: inline-block; background: #fff7e0; color: #7a5a00; font-weight: 700;
  border: 1px solid #d9a92b; padding: 3px 9px; border-radius: 999px;
  font-size: .72rem; letter-spacing: .03em; white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) { .fo-chev { transition: none; } }

/* rail-hold (2026-08-26): set the moment « is clicked, cleared when the
   pointer leaves the sidebar once. Without it the pointer that clicked the
   toggle is still hovering the sidebar, the flyout re-opens instantly, and
   the collapse appears to do nothing. Overrides every hover-flyout rule. */
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover { width: var(--rail-w, 64px); box-shadow: none; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover .logo { padding: 14px 0 10px; text-align: center; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover .logo img { display: none; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover .logo .logo-mark { display: inline-block; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover > .sub, .shell.rail.rail-hold .sidebar:hover .foot { opacity: 0; pointer-events: none; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover nav { padding: 6px 8px; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover nav a { justify-content: center; padding: 10px 0; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover nav a .lbl { opacity: 0; width: 0; } }
@media (hover: hover) { .shell.rail.rail-hold .sidebar:hover .rail-btn { align-self: center; margin: 0 0 12px; } }

/* By-staff calendar view (2026-08-26): the header chip carries the
   practitioner's colour so the column is identifiable at a glance. */
.cal-staff .staff-hd {
  background: var(--pc-bg, #fff); border-bottom: 2px solid var(--pc-line, var(--line));
  display: flex; align-items: center; justify-content: center; gap: 7px;
  font-weight: 700;
}
.cal-staff .staff-hd .staff-dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--pc-line, #888);
  flex: 0 0 auto;
}
/* Empty slots invite the click — a soft hint on hover says "book here". */
/* No whole-slot wash here either (Dr Daniel, 2026-09-07: the by-staff view
   still showed a second pale band under the quarter highlight — THIS rule
   was the culprit, missed when the generic slot:hover wash was removed). */
@media (hover: hover) { .cal-staff .slot:hover { cursor: pointer; } }

/* The by-staff grid gets one column per rostered practitioner — twelve of
   them cannot fit the viewport, so the CALENDAR PANE scrolls sideways while
   the page itself never does (2026-08-26). The week/day views still fit and
   are unaffected. */
/* The PANE scrolls, not the page (Dr Daniel, 2026-08-30). Both axes belong on
   .cal-scroll: it is the box the sticky header measures itself against, and
   two nested scroll containers would leave the header stuck to a box that
   never moves. The max-height here is only a fallback - sizeCal() measures the
   real one - but it has to exist, or the pane is unbounded until JS runs and
   the header cannot stick at all. */
.cal-main { overflow: visible; }
.cal-scroll { overflow: auto; max-height: calc(100vh - 220px); overscroll-behavior: contain; }
/* The by-staff view fits the pane, however many people are on it (2026-08-29).
   It used to be `min-width: max-content` with a 130px floor per column, so a
   full roster ran off the side and had to be dragged — the one view whose
   whole purpose is seeing everybody at once. */
.cal-staff { min-width: 0; }
.cal-staff .slot, .cal-staff .hd { min-width: 0; }
/* Narrow columns need smaller type, not clipped names. */
.cal-staff.dense .staff-hd { font-size: .74rem; padding: 5px 2px; }
.cal-staff.dense .appt .t { font-size: .68rem; }
.cal-staff.dense .appt .rem { display: none; }
.cal-staff.denser .staff-hd { font-size: .66rem; padding: 4px 1px; letter-spacing: -.01em; }
.cal-staff.denser .staff-hd .staff-dot { display: none; }
.cal-staff.denser .staff-offroster { display: none; }
.cal-staff.denser .appt { padding: 2px 3px; }
.cal-staff.denser .appt .t { font-size: .62rem; line-height: 1.15; }
.cal-staff.denser .appt .rem { display: none; }
/* A name that still cannot fit is cut with an ellipsis rather than pushing
   the column wider and bringing the scrollbar back. */
.cal-staff .staff-hd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Branch-roster grid (2026-08-26): each cell is a single branch choice, so
   one person cannot hold two branches on one day by construction. */
.roster-grid select.roster-cell {
  min-height: 0; padding: 5px 8px; font-size: .8rem; border-radius: 8px;
  border: 1px solid var(--line, #ebe0e8); background: #fff; cursor: pointer;
}
.roster-grid select.rb-ZP  { background: #ecfdf1; border-color: #5f7a44; }
.roster-grid select.rb-8TR { background: #f3ecfd; border-color: #6d47a3; }
.roster-grid select.rb-RU  { background: #ecf4fd; border-color: #23508f; }

/* ---- roster: one month calendar per branch (Dr Daniel, 2026-08-30) --------
   Shaped after the team's own Google Sheet work schedule - a month grid per
   outlet with the names written into the days - because that is the layout
   everyone here already reads a rota in. */
.rc-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.rc-month { font-size: 1rem; min-width: 9.5em; text-align: center; }
/* Side by side while there is room; stacked once there is not. Three months
   abreast is the whole point - it is how you see that Thursday is thin at one
   branch and crowded at another. */
.rc-wrap {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* One branch, the whole width. A day square then has room for four or five
   names instead of clipping at two (Dr Daniel, 2026-08-30). */
.rc-wrap.one { grid-template-columns: 1fr; }
.rc-wrap.one .rc-day { min-height: 86px; padding: 5px 5px 6px; }
.rc-wrap.one .rc-n { font-size: .72rem; }
.rc-wrap.one .rc-who { font-size: .72rem; padding: 2px 6px; }
.rc-wrap.one .rc-head strong { font-size: 1rem; }
.rc-wrap.one .rc-wd { font-size: .68rem; padding-bottom: 4px; }
/* ALL, three months abreast, readable (Dr Daniel, 2026-09-21: "why so
   messy?"). At the 960px every wide modal gets, a day square was 37px and
   216 of 232 names clipped to two letters; the roster takes the screen's
   width instead, and names get a tighter tag that may wrap at a space
   ("Daniel / Chia") rather than clip. The lead's crown sits on the tag's
   corner here, so it costs the name no width. */
.modal.wide:has(.rc-wrap) { max-width: min(1500px, 100%); }
.rc-wrap:not(.one) { gap: 10px; }
.rc-wrap:not(.one) .rc-cal { padding: 8px 7px 9px; }
.rc-wrap:not(.one) .rc-day { padding: 2px 2px 3px; }
.rc-wrap:not(.one) .rc-who {
  font-size: .58rem; line-height: 1.2; padding: 1px 2px 1px 3px; border-left-width: 2px;
  white-space: normal; overflow: visible;
}
.rc-wrap:not(.one) .rc-who.lead { position: relative; box-shadow: inset 0 0 0 1px #d9a520; background: #fff3c9; }
.rc-wrap:not(.one) .rc-who.lead .lotd { position: absolute; top: -5px; right: -3px; margin: 0; }
.rc-wrap:not(.one) .rc-who.lead .lotd .ic,
.rc-wrap:not(.one) .rc-who.lead .lotd .ic svg { width: 11px; height: 11px; }
/* The branch strip, in the toolbar beside the month. */
.rc-picks { display: flex; align-items: center; gap: 5px; margin-left: 10px; }
.rc-cal {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); padding: 9px 10px 11px; min-width: 0;
}
.rc-head { display: flex; align-items: center; gap: 7px; margin-bottom: 7px; }
.rc-head strong { font-size: .86rem; font-weight: 600; }
/* rc-days, not rc-grid: the Report Centre's card grid (2026-09) took the name
   .rc-grid further down this file, and being later it won — every roster
   month fell into one or five 240px columns (Dr Daniel, 2026-09-21: "why so
   messy?"). */
.rc-days { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; }
.rc-wd {
  font-size: .58rem; color: var(--muted); text-align: center;
  font-weight: 700; padding-bottom: 2px;
}
/* A day is a small block, not a single line: the names live inside it, which
   is what makes the branch's week legible at a glance. */
.rc-day {
  display: flex; flex-direction: column; align-items: stretch; gap: 1px;
  min-height: 52px; min-width: 0; padding: 3px 3px 4px;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--card); text-align: left; font: inherit; color: inherit;
  box-shadow: none; cursor: default;
}
button.rc-day { cursor: pointer; }
@media (hover: hover) { button.rc-day:hover { border-color: var(--brand); background: var(--brand-tint); box-shadow: none; } }
.rc-day.pad { visibility: hidden; border: none; min-height: 0; }
/* An empty day is left plainly empty rather than marked - a rota is mostly
   empty squares and a symbol in every one of them is just noise. */
.rc-day.empty { background: var(--paper, #fbf9fc); }
.rc-day.today { box-shadow: inset 0 0 0 2px var(--brand); }
.rc-n { font-size: .6rem; color: var(--muted); font-weight: 700; line-height: 1; }
/* Each name is a little card in that practitioner's OWN colour - the same
   stable hue their appointments carry on the calendar, so a person reads as
   the same colour wherever they appear (Dr Daniel, 2026-08-30). The branch no
   longer tints the names: with one branch on screen at a time the outlet is
   already named above, and colouring by branch would have wasted the one
   signal that tells two clinicians apart at a glance. */
.rc-who {
  font-size: .62rem; line-height: 1.3; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--pc-bg, var(--brand-tint));
  color: var(--text);
  border-left: 3px solid var(--pc-line, var(--brand-light));
  border-radius: 4px; padding: 1px 4px;
}

/* The day picker: who works this day at this branch. */
.rd-list { display: flex; flex-direction: column; gap: 1px; max-height: 46vh; overflow: auto; }
.rd-row {
  display: flex; align-items: center; gap: 9px; padding: 5px 7px;
  border-radius: 8px; cursor: pointer; margin: 0;
}
@media (hover: hover) { .rd-row:hover { background: var(--brand-tint); } }
.rd-row .inv-tick { margin: 0; }
/* Placed at another branch that day. Shown rather than hidden: the reason a
   name cannot be ticked belongs on screen, not in an error afterwards. */
.rd-row.busy { opacity: .55; cursor: not-allowed; }
@media (hover: hover) { .rd-row.busy:hover { background: none; } }
.rd-at { margin-left: auto; font-size: .68rem; color: var(--muted); font-weight: 700; }
/* Lead of the day (Dr Daniel, 2026-09-21). The crown sits in front of the
   lead's name wherever the day's people are listed; in the day picker it is a
   button — tap to crown, tap the same crown again to take it back. */
.lotd { display: inline-flex; flex: none; margin-right: 2px; vertical-align: -0.14em; }
.lotd .ic, .lotd .ic svg { width: 1.05em; height: 1.05em; }
.rc-who.lead, .hd-who.lead { font-weight: 800; }
.rd-lead {
  margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card); color: var(--muted); font-size: .72rem; font-weight: 700;
  box-shadow: none; cursor: pointer; min-height: 0;
}
.rd-at + .rd-lead { margin-left: 8px; }
/* Not lead: the crown is a pale outline of itself, so the button still says
   what it does without shouting on every row. */
.rd-lead .ic { opacity: .35; filter: grayscale(1); }
@media (hover: hover) { .rd-lead:hover { border-color: #d9a520; color: var(--text); background: #fff8e1; } }
.rd-lead.on { border-color: #d9a520; background: #fff3c9; color: #6b4a00; }
.rd-lead.on .ic { opacity: 1; filter: none; }
.rd-lead:disabled { opacity: .4; cursor: not-allowed; }
.staff-offroster {
  font-size: .64rem; font-weight: 700; color: #a3352c; background: #fde8e6;
  border-radius: 999px; padding: 2px 7px; margin-left: 6px; letter-spacing: .03em;
}

/* KumoDoc case notes mirror (2026-08-26): read-only, collapsed by default. */
.kumo-note { border: 1px solid var(--line, #ebe0e8); border-radius: 10px; padding: 8px 12px; margin: 6px 0; }
.kumo-note summary { cursor: pointer; font-weight: 600; font-size: .88rem; }
.kumo-note-body { white-space: pre-wrap; font-size: .85rem; color: var(--muted, #6d5974); padding: 8px 2px 2px; }

/* Configurator pickers (2026-08-26): the complimentary set and item add-ons
   start as two buttons; everything else lives in popups. */
.comp-choose { display: flex; gap: 8px; align-items: center; }
/* Was 64px wide and 1.2rem tall for what is one glyph (Dr Daniel, 2026-08-29). */
.comp-choose > button { padding: 6px 12px; min-height: 32px; font-size: .82rem; }
.comp-plus { font-size: 1rem; min-width: 42px; }
.pick-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 9px; }
.pickbtn {
  /* The global button rule paints text white for purple fills; on this white
     card that made the label invisible (only the emoji showed). */
  padding: 16px 10px; font-size: .95rem; font-weight: 600; border-radius: 12px;
  border: 1px solid var(--line, #ebe0e8); background: #fff; cursor: pointer;
  min-height: 56px; color: var(--text, #2a1f2e); box-shadow: none;
}
@media (hover: hover) { .pickbtn:hover { border-color: var(--brand, #470054); background: var(--wash, rgba(71,0,84,.05)); } }

/* Purpose chips on the booking form (Dr Daniel, 2026-08-29). Each purpose
   carries its own tone, supplied per chip as --tone-bg / --tone-fg, so adding a
   purpose needs no new CSS. Chosen fills with the tone's own dark shade, which
   is unmistakable without being loud. */
.purp-chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 2px 0 8px; }
.purp-chip {
  background: var(--tone-bg); color: var(--tone-fg);
  border: 1.5px solid transparent; border-radius: 13px;
  padding: 7px 12px; font-size: .82rem; font-weight: 600;
  min-height: 0; line-height: 1.25; box-shadow: none;
}
@media (hover: hover) { .purp-chip:hover { background: var(--tone-bg); border-color: var(--tone-fg); box-shadow: none; } }
.purp-chip.on { background: var(--tone-fg); color: #fff; border-color: var(--tone-fg); }
@media (hover: hover) { .purp-chip.on:hover { background: var(--tone-fg); color: #fff; } }

/* Choosing a customer (2026-08-29): smaller type than an ordinary pick button,
   because a name and a phone number is more text than "Pillows", and a round
   initial to give the eye something to land on while scanning a long list. */
.pickbtn.cust-pick {
  justify-content: flex-start; text-align: left; gap: 9px;
  padding: 9px 11px; min-height: 0; font-size: .82rem; font-weight: 500;
}
.cust-face {
  width: 28px; height: 28px; border-radius: 50%; flex: none;
  background: var(--brand); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: .8rem;
}
/* min-width:0 is what lets a long name ellipsis instead of stretching the
   button past its grid cell. */
.cust-who { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.cust-nm { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cust-ph { font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }

/* Searching for a customer on the booking form itself (Dr Daniel, 2026-08-29).
   The box takes the width; New sits beside it and never wraps under. Results
   are capped in height so a broad search cannot push the rest of the form off
   the screen. */
.cust-find-row { display: flex; gap: 8px; align-items: center; }
.cust-find-row input { flex: 1; min-width: 0; }
.cust-find-row button { flex: none; }
.cust-find { display: flex; flex-direction: column; gap: 5px; margin-top: 6px; max-height: 268px; overflow-y: auto; }
.cust-find:empty { margin-top: 0; }
/* A long result list scrolls; its rows never shrink to fit the box — eight
   partner clinics squeezed into it overlapped line on line (Dr Daniel,
   2026-09-18: "Fix the cluster mess"). */
.cust-find > * { flex: none; }
/* The partner clinic search (customer-form.js partnerPicker): one line of
   detail, cut short rather than wrapped; the badge keeps its size. Chosen,
   the clinic sits on one line and its two buttons on the next. */
.partner-find .cust-who, .pp-chosen-main .cust-who { flex: 1 1 auto; }
.partner-find .cust-ph { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.partner-find .badge, .pp-chosen-main .badge { flex: none; }
.pp-chosen { display: flex; flex-direction: column; gap: 8px; }
.pp-chosen-main { display: flex; align-items: center; gap: 9px; min-width: 0; }
.pp-chosen-main .cust-nm { white-space: normal; }
.pp-chosen-actions { display: flex; gap: 6px; flex-wrap: wrap; padding-left: 37px; }

/* A section of a form as a card (Dr Daniel, 2026-09-18: "make this a card so
   that we know its a section"): the partner clinic and the referring customer
   on the customer form and on the calendar's and the till's quick forms. */
.form-card {
  background: var(--card); border: 1px solid var(--line, #ebe0e8); border-radius: 12px;
  padding: 10px 14px 12px; margin: 12px 0; box-shadow: 0 1px 4px rgba(71, 0, 84, .07);
}
.form-card-head {
  display: flex; align-items: center; gap: 8px; margin: 0 0 10px; padding-bottom: 8px;
  border-bottom: 1px solid var(--line, #ebe0e8);
  font-weight: 700; font-size: .92rem; color: var(--brand, #470054);
}
.form-card-intro { margin: 0 0 8px; font-size: .8rem; }

/* Add-to-Cart item search (Dr Daniel, 2026-09-05: "this is so messy"): the
   category chips step aside while results show, and each hit is ONE compact
   line — icon, name, SKU, price at the right edge. */
.item-find { gap: 4px; margin-top: 8px; max-height: 320px; }
.item-find .item-hit { display: flex; align-items: center; gap: 10px; padding: 7px 12px; border-radius: 10px; }
.item-find .item-hit .ic { flex: none; }
.item-find .find-nm { font-weight: 600; font-size: .84rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item-find .find-sku { flex: none; font-size: .72rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.item-find .find-price { flex: none; margin-left: auto; font-weight: 700; font-size: .84rem; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* One bar that takes a scan OR a typed name (Dr Daniel, 2026-08-30). The
   magnifier sits inside it in plain grey - a coloured mark on a field you type
   into competes with the text. */
.scanfind { position: relative; display: flex; align-items: center; }
.scanfind > .ic {
  position: absolute; left: 11px; color: var(--muted);
  pointer-events: none; display: flex;
}
.scanfind > .ic svg { width: 16px; height: 16px; }
.scanfind > input { width: 100%; padding-left: 34px; padding-right: 36px; }
/* Bare ✕ inside the bar (Dr Daniel, 2026-09-05): no outline, no fill —
   only shown while there is something to clear. */
.scanfind > .scan-clear {
  position: absolute; right: 4px; top: 0; bottom: 0;
  display: flex; align-items: center;
  background: none !important; border: none; box-shadow: none !important;
  color: var(--muted); font-size: .95rem; padding: 0 10px; min-height: 0; cursor: pointer;
}
@media (hover: hover) { .scanfind > .scan-clear:hover { color: var(--text); } }

/* The practitioner row: name, percent, bin and the add button all on one line
   (Dr Daniel, 2026-08-30). */
.alloc-row { align-items: center; gap: 8px; margin-bottom: 8px; }
.alloc-row > .fix > button { min-height: 38px; }
.alloc-add { font-size: .9rem; }

/* Buttons inside a table cell: inline, never flex - a flex <td> is no longer a
   table cell and its column stops lining up (Dr Daniel, 2026-08-30). */
.cell-acts { white-space: nowrap; }
.cell-acts > button { display: inline-flex; vertical-align: middle; margin-right: 4px; }

/* Allocation: the name and its pencil share a line, the name shrinking rather
   than wrapping (Dr Daniel, 2026-08-30). */
/* NOT display:flex - these are <td>s, and a flex td stops being a table cell,
   which drops it out of the column model and lets the columns wander row to
   row (Dr Daniel, 2026-08-30: "why out of alignment"). Inline-block children
   in a nowrap cell give the same one-line layout and keep the table a table. */
.alloc-cell { white-space: nowrap; }
.alloc-name {
  display: inline-block; vertical-align: middle;
  font-size: .78rem; line-height: 1.25; max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Smaller than a normal small button: it sits inside a table cell beside a
   name, so it should read as a mark on that name rather than a control of its
   own (Dr Daniel, 2026-08-30). */
/* button.alloc-pen, not .alloc-pen: `button.small` sets min-height 32px at the
   same specificity, so the bare class lost. */
button.alloc-pen {
  min-height: 0; height: 24px; width: 24px; padding: 0; margin-left: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle;
}
button.alloc-pen .ic svg { width: 13px; height: 13px; }

/* The line's own number (Dr Daniel, 2026-08-30): a handle for talking about
   the cart out loud, not information about the sale, so it stays quiet.
   Tabular figures keep the column straight once the cart passes nine lines. */
.line-no {
  width: 34px; color: var(--muted); font-variant-numeric: tabular-nums;
  text-align: right; padding-right: 10px;
}
/* Qty sits beside the price it multiplies, and is typed straight into on a
   draft. The box is narrower than its cell so the header sits over its left
   edge rather than floating away from it. */
.qty-cell { width: 76px; }
.qty-cell .qty-in {
  width: 58px; min-height: 30px; padding: 3px 6px; text-align: center;
  font-variant-numeric: tabular-nums; margin: 0;
}
/* A locked qty is a fact, not a control: no box, no affordance to press. */
.qty-cell > .muted { display: inline-block; padding-left: 6px; }

/* Discount / configure / remove: one row, always. */
/* Same reason as .alloc-cell: a table cell, not a flex box. */
.line-acts { white-space: nowrap; text-align: right; }
/* Closer together (Dr Daniel, 2026-08-30). With the fills gone the buttons
   have no visible edge, so their padding read as empty space and the three
   marks looked like three unrelated controls rather than one line's actions:
   23px apart (9 + 5 + 9) became 14px (6 + 2 + 6). The 28px min-height keeps
   the tap target honest even though the box no longer shows. */
.line-acts > button, .line-acts > .mochi-slot > button {
  display: inline-flex; vertical-align: middle; margin-left: 2px;
  padding: 3px 6px; min-height: 28px; font-size: .8rem;
}
.line-acts > .mochi-slot { display: inline; }

/* The heading, with the add controls tucked in at its right once the cart has
   something in it. */
.cart-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cart-head > h2 { margin: 0 0 6px; }
.cart-head > .build-row { margin: 0 0 6px auto; }
/* Tucked into the heading they are a group, not a spread row - the spacer that
   pushes them apart in the empty state has no job up here. */
.cart-head > .build-row .build-gap { display: none; }
.cart-head > .build-row input { max-width: 190px; }

/* The empty cart: the add controls sit where the first row would be, and a
   clear gap separates them from Cancel / Issue below. */
.cart-empty-slot:not(:empty) { margin: 4px 0 22px; }
.cart-empty-slot > .build-row { margin-bottom: 0; }

/* The draft's build row: configurators left, the plus and the scanner right.
   The Mochi button is appended after its own API call and would otherwise land
   last, so both configurators are ordered ahead of the spacer explicitly. */
/* Mattress / Topper sits with the plus and the scanner, not away from them
   (Dr Daniel, 2026-08-30) - all three are ways of putting something in the
   cart. Only the Mochi configurator stays out on the left. The spacer is
   ordered between them, so the group lands hard right. */
.build-row { align-items: center; }
/* Mochi sits WITH the mattress configurator, not away from it (Dr Daniel,
   2026-08-30). Explicit order because the Mochi button is appended after its
   own API call and would otherwise land last: spacer, Mochi, Mattress/Topper,
   plus, scanner. */
.build-row .build-gap { order: -4; flex: 1 1 auto; }
.build-row .mochi-act { order: -3; flex: 0 0 auto; }
.build-row .cfg-act { order: -2; flex: 0 0 auto; }
.build-row .add-act { order: -1; flex: 0 0 auto; }
.build-row input { max-width: 230px; }

/* The cart's footer: Mochi at the left end, everything else pushed right.
   `order` and `margin-right:auto` do it without caring what order the
   buttons were appended in - the Mochi button arrives after its own API
   call, so it would otherwise always land last. */
.cart-actions .mochi-act { order: -1; margin-right: auto; }

/* The three document actions on an issued bill, held at the right end of the
   row (Dr Daniel, 2026-08-30). `flex: 1` rather than `margin-left: auto`: the
   Mochi button already claims a `margin-right: auto`, and two auto margins
   split the free space between them, which would leave this group stranded
   mid-row. Flex-grow is resolved before auto margins, so growing this box
   takes the space outright and its children sit at its right edge whatever
   else is on the row. `order` keeps it last however late a sibling arrives. */
.cart-actions .doc-actions {
  display: flex; align-items: center; gap: 6px;
  order: 2; flex: 1; justify-content: flex-end;
}
/* On a draft nothing goes in it, and an empty flex child would still eat the
   row's free space. */
.cart-actions .doc-actions:empty { display: none; }

/* The item and its three controls share a line. While the category chips are
   showing they take the whole row and the controls drop beneath; once an item
   is chosen the name and the controls sit side by side. */
.item-line { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.item-line > .cn-chips { flex: 1 1 100%; }
.item-line > .comp-summary { flex: 1 1 auto; min-width: 0; margin: 0; }
/* The chosen item, its price, quantity, pencil and FOC on ONE row, with the
   tint on the ITEM alone (Dr Daniel, 2026-08-30) - the fill marks what was
   picked, not the controls that act on it. */
.item-line > .comp-summary {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.item-chip {
  background: var(--brand-tint); border: 1px solid var(--brand-tint-2, #e0d3e6);
  border-radius: 999px; padding: 5px 13px;
  font-weight: 700; color: var(--brand); font-size: .9rem;
}
/* It is also the way back to the picker, so it behaves like a button without
   looking like another control on a row that already has two. */
button.item-chip {
  min-height: 0; box-shadow: none; cursor: pointer; font-family: inherit;
}
@media (hover: hover) { button.item-chip:hover { background: var(--brand); color: #fff; box-shadow: none; } }
.item-price { font-weight: 800; font-size: .95rem; white-space: nowrap; }
.item-src { font-size: .78rem; color: var(--muted); white-space: nowrap; }
.item-ctl { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.iq-qty { display: flex; align-items: center; gap: 6px; margin: 0; font-weight: 600; }
.iq-qty > span { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.iq-qty input { width: 62px; min-height: 34px; padding: 4px 8px; text-align: center; }

/* A remark is an aside, so it gets a rule to write on rather than a slab. */
.remark-line { margin: 8px 0 2px; }
.remark-line > label { display: block; margin-bottom: 0; }
.remark-line > input {
  border: none; border-bottom: 1.5px solid var(--line); border-radius: 0;
  background: transparent; padding: 3px 2px; min-height: 30px; box-shadow: none;
}
.remark-line > input:focus {
  border-bottom-color: var(--brand); outline: none; box-shadow: none;
}

/* FOC belongs beside the quantity: how many, and whether they are paying. */
.foc-tick {
  display: flex; align-items: center; gap: 7px; margin: 0 0 9px;
  font-weight: 600; white-space: nowrap; flex: 0 0 auto;
}
.foc-tick input[type="checkbox"] { width: 18px; height: 18px; flex: none; }

/* Ship from factory, on one line: the tick, the reason, the note. */
/* No fill (Dr Daniel, 2026-08-30): it is one line among several, and a tinted
   slab gave it more weight than the item it belongs to. */
.fj-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 6px 0 0; margin: 6px 0 0; background: none; border: none;
  border-top: 1px solid var(--line); border-radius: 0;
}
.fj-row > label {
  display: flex; align-items: center; gap: 7px; margin: 0;
  font-weight: 600; white-space: nowrap; flex: 0 0 auto;
}
.fj-row > label input[type="checkbox"] { width: 18px; height: 18px; flex: none; }
.fj-row > .fj-reasons { display: flex; gap: 6px; flex: 0 0 auto; }
.fj-row > .fj-reasons .cn-chip { padding: 4px 11px; font-size: .78rem; min-height: 30px; }
.fj-row > select { flex: 1 1 190px; min-width: 150px; min-height: 34px; padding: 5px 10px; font-size: .84rem; }
/* Narrow enough that label + both reason chips + the note still share one
   line once the tick is on (measured: 144 + 237 + gaps leaves ~183px). */
.fj-row > input { flex: 1 1 130px; min-width: 110px; min-height: 34px; padding: 5px 10px; font-size: .84rem; }

/* The Item label and its search box share a line: browse by chip, or type. */
.item-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 2px; }
.item-head label { margin: 0; }
/* The search sits BESIDE the label, so it takes only the room it needs rather
   than stretching across the popup (Dr Daniel, 2026-08-29). Smaller type and
   padding than a full form field: it is a shortcut, not the main input. */
.item-head input {
  flex: 0 1 190px; min-width: 130px;
  font-size: .8rem; padding: 5px 10px; min-height: 0;
}

/* Clinician chips (Dr Daniel, 2026-08-29). Same shape as the purpose chips but
   in the brand colour, because a clinician is a person rather than a category
   and giving thirteen of them thirteen colours would be noise. Anyone off the
   roster stays on screen, dimmed and dashed, so it is clear they exist and why
   they cannot be picked. */
.clin-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 8px; }
.clin-chip {
  background: var(--card); color: var(--text); border: 1.5px solid var(--line);
  border-radius: 13px; padding: 7px 12px; font-size: .82rem; font-weight: 600;
  min-height: 0; line-height: 1.25; box-shadow: none;
}
@media (hover: hover) { .clin-chip:hover:not(:disabled) { background: var(--brand-tint); border-color: var(--brand); } }
.clin-chip.on { background: var(--brand); border-color: var(--brand); color: #fff; }
@media (hover: hover) { .clin-chip.on:hover { background: var(--brand-dark); color: #fff; } }
.clin-chip:disabled { opacity: .45; border-style: dashed; cursor: default; }
@media (hover: hover) { .clin-chip:disabled:hover { background: var(--card); border-color: var(--line); } }
/* Not rostered today, but choosable as a special case (2026-09-25): dimmed and
   dashed like before, and amber once chosen so the exception shows. */
.clin-chip.off { opacity: .55; border-style: dashed; }
@media (hover: hover) { .clin-chip.off:hover { opacity: .85; background: var(--warn-bg); border-color: var(--warn); } }
.clin-chip.off.on { opacity: 1; border-style: solid; background: var(--warn); border-color: var(--warn); color: #fff; }
@media (hover: hover) { .clin-chip.off.on:hover { background: #7a4800; color: #fff; } }
/* A pickbtn can also act as a persistent choice (e.g. cover yes/no): .on is
   the selected one. */
.pickbtn.on { border-color: var(--brand, #470054); background: var(--brand-tint, #f5ecf7); box-shadow: inset 0 0 0 1px var(--brand, #470054); }
/* Date presets: calm per-window tones, the chosen one outlined in its own ink. */
.pickbtn.preset-tone { background: var(--tone-bg, #eceef2); color: var(--tone-fg, #55606f); border-color: transparent; font-weight: 600; }
@media (hover: hover) { .pickbtn.preset-tone:hover { border-color: var(--tone-fg, #55606f); background: var(--tone-bg, #eceef2); } }
.pickbtn.preset-tone.on { border-color: var(--tone-fg, #55606f); box-shadow: inset 0 0 0 1px var(--tone-fg, #55606f); background: var(--tone-bg, #eceef2); }
.comp-summary { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 6px; }
.comp-pick-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line, #ebe0e8); }
.comp-pick-label { font-weight: 700; min-width: 64px; }
.comp-pick-val { flex: 1 1 auto; }
.addon-row { border-left: 3px solid var(--brand-tint-2, #d9c2e0); padding-left: 10px; margin-bottom: 10px; }
.st-price { font-size: 2.2rem; font-weight: 800; color: var(--brand, #470054); margin: 6px 0 2px; }

/* ---- grouped nav (2026-08-26) ---- */
.nav-group { margin-bottom: 4px; }
.nav-group + .nav-group { border-top: 1px solid var(--line); margin-top: 4px; }
.nav-group-h {
  display: flex; align-items: center; width: 100%; gap: 8px;
  background: none; border: none; box-shadow: none; cursor: pointer;
  color: var(--brand); font-weight: 800; font-size: .8rem; line-height: 1;
  letter-spacing: .07em;
  text-transform: uppercase; padding: 12px 12px 6px; min-height: 0; border-radius: 0;
}
.nav-group-h .ic { flex: none; display: inline-flex; }
.nav-group-h .ic span { display: inline-flex; }
.nav-group-h svg { width: 16px; height: 16px; }
@media (hover: hover) { .nav-group-h:hover { background: none; color: var(--brand-light); box-shadow: none; transform: none; } }
.nav-group-h:active { transform: none; box-shadow: none; }
.nav-group-h .lbl { flex: 1; text-align: left; }
.nav-chev { transition: transform .15s; font-size: .7rem; }
.nav-group.folded .nav-chev { transform: rotate(-90deg); }
.nav-group.folded > a { display: none; }
/* The icon rail has no room for headers: they disappear and every icon stays
   reachable regardless of fold — a fold must never hide pages from the rail.
   rail-hold keeps the rail closed under the cursor, so it needs the same. */
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav .nav-group-h, .shell.rail.rail-hold .sidebar:hover nav .nav-group-h { display: none; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav .nav-group.folded > a, .shell.rail.rail-hold .sidebar:hover nav .nav-group.folded > a { display: flex; } }

/* Back / Today / forward read as ONE control, not three loose buttons: the
   arrows are square, Today keeps its words, and they share a single outline. */
.cal-step { display: inline-flex; align-items: center; }
/* The viewed day, spelt out between the arrows (2026-09-04). No fill on
   the controls, and arrows big enough to hit (Dr Daniel, same day). */
.cal-step-date { font-weight: 700; padding: 0 10px; white-space: nowrap; font-size: .92rem; }
.cal-step > button { background: none !important; border: none; box-shadow: none; }
@media (hover: hover) { .cal-step > button:hover { background: var(--brand-tint) !important; } }
.cal-step > button:first-child, .cal-step > button:last-child {
  font-size: 1.45rem; line-height: 1; padding: 2px 12px;
}
.cal-step > button { border-radius: 0; margin: 0; box-shadow: none; }
.cal-step > button:first-child { border-radius: 10px 0 0 10px; }
.cal-step > button:last-child { border-radius: 0 10px 10px 0; }
.cal-step > button:not(:first-child) { margin-left: -1px; }
.cal-step > button:first-child, .cal-step > button:last-child {
  padding: 0 12px; font-size: 1.1rem; line-height: 1;
}
@media (hover: hover) { .cal-step > button:hover { position: relative; z-index: 1; } }

/* The disposal question, in the one colour the rest of the form never uses.
   :has() lets the block answer for itself: amber while undecided, green the
   moment the box is ticked — so "did we ask?" is answered from across the
   room without reading a word (Dr Daniel, 2026-08-30). */
.disposal-ask {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  border: none; background: none;
  border-radius: 12px; padding: 9px 0; margin: 10px 0;
}
.disposal-ask > label {
  display: flex; gap: 10px; align-items: center; margin: 0;
  font-size: 1rem; font-weight: 700; color: #7a5410;
}
.disposal-ask input[type="checkbox"] { width: 20px; height: 20px; accent-color: #b57614; }
.disposal-ask:has(input[type="checkbox"]:checked) > label { color: #1f5c41; }
.disposal-ask:has(input[type="checkbox"]:checked) input[type="checkbox"] { accent-color: #2f7d5b; }

/* The target date is the one field on the fulfilment block that only WE can
   answer, and the factory schedules against it (Dr Daniel, 2026-08-29). It is
   marked out rather than sitting as one of four equal boxes. */
.target-block {
  border: none; border-radius: 12px;
  background: var(--brand-tint); padding: 9px 12px 10px; margin: 8px 0 10px;
}
.target-label { font-weight: 700; color: var(--brand); margin-bottom: 4px; display: block; }
.target-input {
  background: none; font-size: .92rem;
  border: none; border-bottom: 1.5px solid var(--line, #cfc4d6);
  border-radius: 0; box-shadow: none; padding-left: 2px;
}
.target-input:focus { outline: none; border-bottom-color: var(--brand); }
/* Labels that run from "ASAP" to "Within 2 weeks" (or from "SE" to "Deluxe"):
   equal columns would size every one of them to the longest, so these take the
   room the words need and wrap. */
.chips-tight .st-chips,
.target-block .st-chips { display: flex; flex-wrap: wrap; margin-top: 7px; }
/* :not(.on) matters. Without it this rule wins over `.st-chip.on` on source
   order alone — same specificity — and repaints the SELECTED chip white while
   `.on` keeps the white text, so the label disappears (Dr Daniel, 2026-08-29:
   "why after click, the word in the button missing?"). */
.target-block .st-chip:not(.on) { background: var(--card); }

/* ---- invoice header tiles (2026-08-27) ---- */
/* Four facts about the bill, in the least room that still reads (Dr Daniel,
   2026-08-29). The strip is a header, not a section: its card loses most of
   its padding so the tiles are what takes the space, not the frame. */
.hdr-card { padding: 9px 10px; margin-bottom: 10px; }
.hdr-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(186px, 1fr)); gap: 8px; }
.hdr-tile {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  text-align: left; padding: 6px 10px; min-height: 46px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: var(--text); box-shadow: none;
}
/* A tile with its own small action in the corner. The tile still fills the
   grid cell and still opens its own popup; the action sits over it and stops
   the click from reaching it. */
.hdr-tile-wrap { position: relative; display: flex; }
.hdr-tile-wrap > .hdr-tile { flex: 1; min-width: 0; padding-right: 36px; }
.hdr-edit {
  position: absolute; top: 6px; right: 6px;
  width: 24px; height: 24px; min-height: 0; padding: 0; border-radius: 7px;
  background: var(--card); border: 1px solid var(--line); color: var(--muted);
  box-shadow: none;
}
@media (hover: hover) { .hdr-edit:hover { background: var(--brand-tint); border-color: var(--brand); color: var(--brand); box-shadow: none; } }
button.hdr-tile { cursor: pointer; }
@media (hover: hover) { button.hdr-tile:hover { border-color: var(--brand); background: var(--brand-tint); box-shadow: none; } }
.hdr-tile .t-label {
  font-size: .6rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); line-height: 1.25;
}
.hdr-tile .t-value { font-weight: 700; font-size: .85rem; line-height: 1.3; }
.hdr-tile.empty .t-value { color: var(--brand); }

/* ---- invoice register rows (2026-08-27) ---- */
/* A grid with one shared column template, so every row's columns line up
   like a table (Dr Daniel, 2026-08-27): date | number | mode | paid |
   customer | (draft actions). */
/* A 34px tick column leads every row (2026-08-28), so a batch can be printed
   or cleared in one go; the header's tick takes whatever the filters left. */
.inv-row {
  display: grid;
  grid-template-columns: 30px 112px 138px minmax(118px, .5fr) 132px minmax(180px, 1.6fr) 132px;
}
/* Finalised rows have no action cell — same template minus that column, so
   the frozen header and every row align to the pixel in both views. */
.inv-row.no-acts {
  grid-template-columns: 30px 112px 138px minmax(118px, .5fr) 132px minmax(180px, 1.6fr);
  align-items: stretch; gap: 0;
  padding: 0; margin-bottom: 4px; cursor: pointer; overflow: hidden;
  border: 1px solid var(--line); border-radius: 12px; background: var(--card);
}
/* Every cell centres its own content and carries a full-height divider on its
   left (Dr Daniel, 2026-08-27) — the columns read like a proper table. */
.inv-row > * {
  padding: 5px 10px; display: flex; flex-direction: column; justify-content: center;
  line-height: 1.25;
}
.inv-row > * + * { border-left: 1px solid var(--line); }
@media (hover: hover) { .inv-row:hover { border-color: var(--brand); background: var(--brand-tint); } }
/* The frozen header: same template, stuck beneath the glassy topbar. */
.inv-head-row {
  position: sticky; top: 58px; z-index: 8; cursor: default;
  font-size: .68rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .06em; color: var(--brand-light);
  background: rgba(255, 255, 255, .92);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  margin-bottom: 6px;
}
.inv-head-row > * { padding: 5px 10px; }
@media (hover: hover) { .inv-head-row:hover { border-color: var(--line); background: rgba(255, 255, 255, .92); } }
.inv-tick {
  width: 16px; height: 16px; min-height: 0; margin: 0 auto; padding: 0;
  accent-color: var(--brand); cursor: pointer; align-self: center;
}
/* The tick cell is the one cell with no left divider and no padding. */
.inv-row > .inv-tick { padding: 0; border-left: none; }
.inv-row.picked { border-color: var(--brand); background: var(--brand-tint); }

/* ---- draft bubbles (Dr Daniel, 2026-08-30) --------------------------------
   The Drafts tab shows tiles, not rows. A draft is a job in progress: you want
   who it is for and what it has come to, not a grid read across seven columns.
   auto-fill rather than auto-fit, so two open drafts stay two card-width tiles
   instead of stretching to half the screen each. */
.inv-bubbles {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
}
.inv-bub {
  border: 1px solid var(--line); border-radius: 18px; background: var(--card);
  padding: 11px 14px 9px; cursor: pointer; display: flex; flex-direction: column;
  gap: 3px; min-width: 0; transition: border-color .12s, background .12s, box-shadow .12s;
}
@media (hover: hover) { .inv-bub:hover {
  border-color: var(--brand); background: var(--brand-tint);
  box-shadow: 0 2px 10px rgba(71, 0, 84, .10);
} }
.inv-bub.picked { border-color: var(--brand); background: var(--brand-tint); }
.bub-top { display: flex; align-items: center; gap: 7px; min-width: 0; }
.bub-top .inv-tick { margin: 0; flex: 0 0 auto; }
/* The draft's own number is the quiet part - it is a handle, not the headline. */
.bub-no {
  font-size: .68rem; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--brand-light);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bub-top .br-tag { margin-left: auto; flex: 0 0 auto; }
/* The customer leads, and a long name wraps rather than pushing the tile wide. */
.bub-name {
  font-size: .95rem; font-weight: 600; line-height: 1.3;
  overflow-wrap: anywhere; margin-top: 1px;
}
.bub-total { font-size: 1.24rem; font-weight: 800; letter-spacing: -.01em; }
.bub-sub {
  font-size: .72rem; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The date and the two actions share the last line: the actions sit at the
   right edge where the thumb expects them, and the tile keeps its height
   whether or not there is a consultant to name. */
.bub-foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; margin-top: 5px; min-height: 30px;
}
.bub-when { font-size: .7rem; color: var(--muted); white-space: nowrap; }
.bub-acts { display: flex; gap: 4px; flex: 0 0 auto; }
.bub-acts button { min-height: 28px; padding: 3px 9px; }
/* Select-everything, standing in for the header row the bubbles do not have. */
.bub-all {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: .72rem; font-weight: 700; color: var(--brand-light);
  text-transform: uppercase; letter-spacing: .05em; margin: 0 2px 10px;
}
.bub-all .inv-tick { margin: 0; }

/* The bar only exists while something is selected, so it never takes space
   from the list it acts on. */
.bulk-bar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 12px; margin-bottom: 10px;
  border: 1px solid var(--brand); border-radius: 12px; background: var(--brand-tint);
}
.bulk-count { font-weight: 800; color: var(--brand); font-size: .86rem; }

.inv-who { min-width: 0; }
.inv-dt { font-variant-numeric: tabular-nums; font-weight: 600; font-size: .82rem; }
.inv-dt .muted { font-weight: 400; font-size: .72rem; }
/* "Bank Transfer" was wrapping to two lines and setting the height of every
   row in the register (Dr Daniel, 2026-08-30). It gets the width it needs and
   smaller type, and the whole list halves. */
.inv-mode { font-size: .8rem; font-weight: 700; white-space: nowrap; }
.inv-row .inv-no { font-size: .84rem; }
.inv-row .badge { font-size: .62rem; padding: 1px 7px; }
.inv-row .inv-who strong, .inv-row .inv-who b { font-size: .86rem; }
.inv-row .muted { font-size: .72rem; }
.inv-row .num, .inv-row .inv-amt { font-size: .86rem; }
/* Branch chip beside the number, not under it. */
.inv-row .inv-no { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
@media (max-width: 860px) {
  .inv-row, .inv-row.no-acts { grid-template-columns: 34px 92px 1fr auto; }
  .inv-mode, .inv-no { display: none; }
}
.flt-chip { width: auto; min-height: 38px; padding: 6px 12px; border-radius: 999px; font-size: .84rem; }
.br-tag {
  display: inline-block; font-size: .62rem; font-weight: 800; letter-spacing: .04em;
  border-radius: 6px; padding: 1px 7px; color: #fff; margin-top: 2px;
}
.br-tag.br-zp { background: #470054; } .br-tag.br-8tr { background: #0e7490; }
.br-tag.br-ru { background: #a13d63; } .br-tag.br-cs { background: #0b5fa5; } .br-tag.br-wsd { background: #c2410c; }
.br-tag.br-pt { background: #166534; }
/* The customer name carries the row on SIZE, not weight (Dr Daniel,
   2026-08-28: "too many bolds in the list, too heavy") — with the invoice
   number and the total already bold, a third bold column made every row shout. */
.inv-name { font-weight: 500; font-size: 1.02rem; }

/* The customer's name is a control - it opens their record - but it has to
   read as the name it is, not as a button parked in the middle of a row
   (Dr Daniel, 2026-08-30). Everything the base button gives a button is taken
   back off: the pill, the fill, the shadow, the 42px floor. What is left is the
   underline on hover, which is how a name that can be opened has always looked. */
button.name-link {
  background: none; border: none; box-shadow: none; padding: 0; margin: 0;
  font: inherit; color: inherit; min-height: 0; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: flex-start;
  gap: 5px; text-align: left; cursor: pointer;
}
@media (hover: hover) { button.name-link:hover {
  background: none; box-shadow: none;
  color: var(--brand); text-decoration: underline;
} }
/* The base rules press every button down on click; a line of text that sinks
   under the cursor just looks like a rendering fault. */
button.name-link:active { transform: none; box-shadow: none; }
button.name-link:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* The profile's name line, with its pencil alongside (Dr Daniel, 2026-08-30).
   The name keeps its own weight; the pencil is a mark beside it, not a control
   competing with it. */
.prof-name { display: flex; align-items: center; gap: 8px; }
button.prof-edit { min-height: 26px; padding: 2px 6px; }
button.prof-edit .ic svg { width: 15px; height: 15px; }
.inv-amts { text-align: right; align-items: flex-end; }
.inv-total { font-weight: 800; }
.inv-due { color: var(--bad); font-weight: 700; font-size: .82rem; }
.inv-settled { color: var(--ok); font-size: .82rem; }
.inv-acts { flex-direction: row; align-items: center; gap: 6px; }
.inv-pop-who { margin-bottom: 10px; }

/* Appointments / Invoices / Production Orders, one tap from any screen. They
   sit just left of the profile, and the one you are on is filled in. */
.topbar-jump { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.topbar-jump .jump {
  width: 34px; height: 34px; min-height: 0; padding: 0; border-radius: 10px;
  background: transparent; color: var(--muted); box-shadow: none;
  display: inline-flex; align-items: center; justify-content: center;
}
@media (hover: hover) { .topbar-jump .jump:hover { background: var(--brand-tint); color: var(--brand); box-shadow: none; } }
.topbar-jump .jump.on { background: var(--brand-tint); color: var(--brand); }
/* Both the .ic box AND the svg: .ic is 1.15em with flex:none, so sizing only
   the svg leaves it squeezed back to the container's width. */
.topbar-jump .jump .ic, .topbar-jump .jump .ic svg { width: 20px; height: 20px; }
/* The profile block already had margin-left:auto to push itself right; with the
   jumps taking that job, it just follows them. */
.topbar .topbar-jump ~ .who { margin-left: 10px; }
@media (max-width: 620px) { .topbar-jump { display: none; } }

/* The register's filter row, on one line (Dr Daniel, 2026-08-30). One height
   for everything in it, so the row reads as a single control strip; the search
   takes the leftover width instead of a fixed slab. */
.reg-toolbar { gap: 6px; align-items: center; }
.reg-toolbar > * { flex: 0 0 auto; }
.reg-toolbar .seg-row { border-radius: 999px; }
.reg-toolbar .pickbtn.seg-toggle {
  min-height: 34px; padding: 5px 11px; font-size: .8rem;
}
.reg-toolbar .flt-chip { min-height: 34px; padding: 5px 10px; font-size: .8rem; }
.reg-toolbar .branch-seg { align-self: center; }
.reg-toolbar .br-chip { min-height: 26px; padding: 3px 8px; font-size: .72rem; }
.reg-find { flex: 1 1 110px; min-width: 90px; }
.reg-find > input { min-height: 34px; padding: 5px 10px 5px 32px; font-size: .82rem; }
.reg-find > .ic { left: 9px; }
.reg-new {
  min-height: 34px; padding: 5px 14px; font-size: .82rem; white-space: nowrap;
}
/* Drafts / Finalised toggle on the register (2026-08-27): the active side is
   a solid brand fill so the two states cannot be confused at a glance. */
.seg-row { display: flex; gap: 0; border: 1px solid var(--brand); border-radius: 999px; overflow: hidden; }
.pickbtn.seg-toggle {
  min-height: 46px; padding: 8px 22px; font-size: .92rem; font-weight: 700;
  border: none; border-radius: 0; background: var(--card); color: var(--brand);
}
@media (hover: hover) { .pickbtn.seg-toggle:hover { background: var(--brand-tint); box-shadow: none; } }
.pickbtn.seg-toggle.on {
  background: var(--brand); color: #fff; box-shadow: none;
}
@media (hover: hover) { .pickbtn.seg-toggle.on:hover { background: var(--brand); } }

/* The PV & Orders tabs (partners.js, 2026-09-18: "this is ugly, redesign
   this toggle") — the partner system's own tab style, since this screen is
   framed inside it: a compact tinted group sized to its tabs, the open one
   raised in white. */
.pv-tabs {
  display: flex; flex-wrap: wrap; gap: 4px; width: fit-content; max-width: 100%; margin: 0 0 16px;
  padding: 4px; border-radius: 14px; background: var(--brand-tint, #f5ecf7); border: 1px solid var(--line, #ebe0e8);
}
.pv-tab {
  font: inherit; font-size: .9rem; font-weight: 700; padding: 8px 18px; min-height: 0; border: none; border-radius: 10px;
  background: transparent; color: var(--muted, #7a6c80); cursor: pointer; box-shadow: none;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
@media (hover: hover) { .pv-tab:hover { color: var(--brand, #470054); background: rgba(255, 255, 255, .55); } }
.pv-tab.on { background: #fff; color: var(--brand, #470054); box-shadow: 0 1px 4px rgba(71, 0, 84, .14); }
@media (hover: hover) { .pv-tab.on:hover { background: #fff; } }

/* ---- branch toggle, one colour per branch (2026-08-27) ---- */
.branch-seg { display: flex; gap: 4px; flex-wrap: wrap; }
.br-chip {
  /* Also used as a plain tag on a <span> (a note's branch, a booking being
     read from another branch), so it must not rely on being a button. */
  display: inline-block;
  min-height: 0; padding: 4px 10px; font-size: .72rem; font-weight: 800;
  border-radius: 8px; background: transparent; box-shadow: none;
  border: 1px solid var(--line); color: var(--muted); letter-spacing: .03em;
}
@media (hover: hover) { .br-chip:hover { box-shadow: none; background: var(--neutral-bg); color: var(--text); } }
/* branch colours: ZP brand purple, 8TR teal, RU amber, CS blue */
.br-chip.on { color: #fff; }
.br-chip.on.br-zp { background: #470054; border-color: #470054; }
.br-chip.on.br-8tr { background: #0e7490; border-color: #0e7490; }
.br-chip.on.br-ru { background: #a13d63; border-color: #a13d63; }
.br-chip.on.br-cs { background: #0b5fa5; border-color: #0b5fa5; }
.br-chip.on.br-wsd { background: #c2410c; border-color: #c2410c; }
.br-chip.on.br-pt { background: #166534; border-color: #166534; }
/* The link to the CS Hub, beside the branch chips (2026-11-11). */
.br-chip.br-hub { text-decoration: none; display: inline-flex; align-items: center; color: #0b5fa5; border-color: #0b5fa5; background: #eef5fc; white-space: nowrap; }
@media (hover: hover) { .br-chip.on:hover { opacity: .92; } }
/* ALL is not a branch, so it takes the neutral ink rather than a branch hue */
.br-chip.on.br-all { background: var(--text); border-color: var(--text); }
/* Order Form list (Dr Daniel, 2026-09-06: "remove the label's filling
   colour"): branch and status read as plain coloured TEXT — the branch
   keeps its hue as ink, the status badge keeps its ink and dot only. */
.br-plain { font-weight: 800; letter-spacing: .03em; font-size: .78rem; }
.br-plain.br-zp { color: #470054; }
.br-plain.br-8tr { color: #0e7490; }
.br-plain.br-ru { color: #a13d63; }
.br-plain.br-cs { color: #0b5fa5; }
.br-plain.br-wsd { color: #c2410c; }
.br-plain.br-pt { color: #166534; }
.badge.badge-plain { background: transparent !important; border: none !important; padding: 0; }
/* Draft orders wear a light-YELLOW wash across the row, and the word
   Draft itself goes orange (Dr Daniel, 2026-09-06: "Draft column pls
   make it yellow. The colour of the word 'Draft' orange colour." —
   superseding the light-blue wash from earlier the same day). */
tr.row-draft td { background: #fdf6df; }
@media (hover: hover) { tr.row-draft.click:hover td { background: #f9edc7; } }
tr.row-draft .badge.badge-plain { color: #d97706; }
/* A DELIVERED order recedes — the whole row slightly transparent but
   still clickable; pointing at it brings it back for reading
   (Dr Daniel, 2026-09-06). */
/* One step fainter than the first cut (Dr Daniel, 2026-09-06: "slightly
   more transparent. Probably about semi transparent"). */
tr.row-done td { opacity: .45; }
@media (hover: hover) { tr.row-done.click:hover td { opacity: .95; } }

/* ---- configurator: start over (2026-08-28) ---------------------------- */
.cfg-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cfg-head h1 { margin: 0; flex: 1; }
.cfg-reset { white-space: nowrap; }
/* The configurator: build form beside the preview on a desk, stacked on a
   phone (Dr Daniel, 2026-09-14). The preview only follows the scroll where
   there is a second column for it to follow in — pinned on a phone it would
   sit on top of the very form it is previewing. */
.cfg-grid { grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 900px) {
  .cfg-grid { grid-template-columns: minmax(320px, 1fr) minmax(320px, 1fr); }
  .cfg-preview { position: sticky; top: 12px; max-height: calc(100vh - 24px); overflow-y: auto; }
}

/* ---- the case-note editor: card right, history left (2026-08-28) ------- */
/* The clinic reads a consultation with the previous ones beside it, so the
   editor puts them there rather than behind a tab. Below 1000px the grid
   collapses and the rail falls under the card, which is the stacked order the
   phone had before. */
.cn-split { display: grid; grid-template-columns: 300px minmax(0, 1fr); gap: 16px; align-items: start; }
/* The rail stays put while the card scrolls (Dr Daniel, 2026-08-29: "the side
   tab pls freeze it"). It was already sticky, but at top:0 — which is where the
   modal's own sticky header sits, so the rail parked BEHIND it and its first
   lines were hidden the moment anything scrolled. It now stops just below the
   header, and is tall enough to fill the rest of the screen. */
.cn-history {
  position: sticky; top: 68px; max-height: calc(92vh - 96px); overflow-y: auto;
  padding-right: 10px; border-right: 1px solid var(--line);
}
.cn-editor { min-width: 0; }
.cn-h {
  margin: 4px 0 6px; font-size: .72rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--brand);
}
.cn-h + .cn-h, .cn-list + .cn-h, table + .cn-h, p + .cn-h { margin-top: 16px; }
.cn-list { display: flex; flex-direction: column; gap: 6px; }
/* Each past note is a button: it opens read-only, where it can be amended or
   withdrawn. Nothing in this rail is edited in place. */
.cn-item {
  width: 100%; text-align: left; display: block; min-height: 0;
  padding: 8px 10px; border-radius: 10px; box-shadow: none;
  background: var(--card); border: 1px solid var(--line); color: var(--text);
  font-weight: 400; font-size: .84rem;
}
@media (hover: hover) { .cn-item:hover { background: var(--brand-tint); border-color: var(--brand); } }
.cn-item.on { border-color: var(--brand); background: var(--brand-tint); }
.cn-item-head { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.cn-item-sub {
  margin-top: 2px; font-size: .78rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* A rail row that carries its own action: the row still takes the width and
   opens the record, the action sits beside it and never inside it (a button
   cannot contain a button). */
/* The row IS the card now, so the delete sits inside the same frame rather
   than floating beside it (Dr Daniel, 2026-08-30). */
.cn-item-row {
  display: flex; align-items: center; gap: 4px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); padding-right: 6px; margin-bottom: 6px;
}
@media (hover: hover) { .cn-item-row:hover { border-color: var(--brand); background: var(--brand-tint); } }
.cn-item-row .cn-item {
  flex: 1; min-width: 0; border: none; background: transparent; border-radius: 9px;
}
@media (hover: hover) { .cn-item-row .cn-item:hover { background: transparent; border-color: transparent; } }
.cn-item-row .cn-item-act { flex: 0 0 auto; }
/* Bare inside the card (Dr Daniel, 2026-08-30): no fill and no border - the
   row already draws the box, so a second frame inside it was a box in a box.
   Colour arrives on hover, which is when it matters. */
.cn-item-act {
  flex: none; width: 34px; min-height: 0; padding: 0; border-radius: 9px;
  background: transparent; border: none; color: var(--muted); box-shadow: none;
}
@media (hover: hover) { .cn-item-act:hover:not(:disabled) { background: var(--brand-tint); color: var(--brand); box-shadow: none; } }
.cn-item-act.danger { color: #b3392f; border-color: transparent; background: transparent; }
@media (hover: hover) { .cn-item-act.danger:hover { background: #fbe7e5; border-color: #d9584f; color: #a8322a; } }
.cn-item-act:disabled { opacity: .35; cursor: not-allowed; }
@media (hover: hover) { .cn-item-act:disabled:hover { background: transparent; border-color: transparent; } }
.cn-buys { width: 100%; font-size: .8rem; }
/* Four things the rail can show, one at a time (2026-08-28): notes,
   purchases, the mattress that was built, the Mochi that was built. Icon and
   count only — the heading underneath says which one is open, so the strip
   stays narrow enough for a 300px rail. */
/* The strip stays put while the rail's own list scrolls beneath it (Dr Daniel,
   2026-08-29). .cn-history is itself a scroll box, so without this the four
   buttons scrolled out of their own panel and you had to scroll back up just to
   change what the rail was showing. Opaque, or the list shows through it. */
.cn-seg {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
  position: sticky; top: 0; z-index: 1;
  background: var(--card); padding-bottom: 8px; margin-bottom: 2px;
}
.cn-tab {
  min-height: 0; padding: 6px 2px; border-radius: 9px; box-shadow: none;
  background: var(--neutral-bg); color: var(--muted);
  border: 1px solid transparent; display: flex; flex-direction: column;
  align-items: center; gap: 1px; line-height: 1;
}
@media (hover: hover) { .cn-tab:hover { background: var(--brand-tint); color: var(--brand); } }
.cn-tab.on { background: var(--brand); color: #fff; border-color: var(--brand); }
.cn-tab-ic { font-size: 1rem; }
.cn-tab-n { font-size: .66rem; font-weight: 800; }
/* A purchase row puts its amount at the far end so the column of figures
   lines up down the rail. */
.cn-amt { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
/* The Mochi build in a popup uses the portal's own markup and classes, so it
   needs no overrides here — squashing the pockets is exactly what stopped it
   looking like the portal. */
.cn-buys td { padding: 3px 4px; border-bottom: 1px solid var(--line); }
/* On a phone the consultation card IS the screen — it replaced a full-screen
   sheet, so it should not sit in a floating box with the day behind it. */
@media (max-width: 700px) {
  .modal-backdrop:has(.cn-split) { padding: 0; }
  .modal:has(.cn-split) {
    border-radius: 0; margin-bottom: 0; min-height: 100vh; max-height: 100vh;
    padding: 0 14px 20px;
  }
}
@media (max-width: 1000px) {
  .cn-split { grid-template-columns: 1fr; }
  /* Card first on a phone, history under it — the order it has always been. */
  .cn-history {
    position: static; top: auto; max-height: none; order: 2;
    border-right: none; border-top: 1px solid var(--line);
    padding-right: 0; padding-top: 12px; margin-top: 12px;
  }
  .cn-editor { order: 1; }
}

/* ---- clinical notes on a customer profile (2026-08-28) ---------------- */
/* Closed, a note is one scannable header line; opened, it is the consultation
   card in the card's own order. */
.note-card {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 9px 12px; margin-bottom: 8px; background: var(--card);
  /* The label/value grid below collapses on the CARD's width, not the
     viewport's: this sits inside a modal, so the two are not the same. */
  container-type: inline-size;
}
.note-card[open] { box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.06)); }
.note-head { cursor: pointer; list-style: none; display: flex; align-items: center; flex-wrap: wrap; gap: 2px; }
.note-head::-webkit-details-marker { display: none; }
.note-head::before {
  content: '\25b8'; color: var(--muted); margin-right: 6px;
  transition: transform .15s ease; display: inline-block;
}
.note-card[open] > .note-head::before { transform: rotate(90deg); }
/* Which branch recorded it: a customer's history follows the person, so a
   note from another branch is normal and must be labelled, not hidden. */
.note-br { padding: 1px 7px; font-size: .64rem; margin: 0 2px; }
.note-sec {
  display: flex; align-items: center; gap: 6px;
  margin: 16px 0 6px; padding-bottom: 4px; border-bottom: 1px solid var(--line);
  font-size: .74rem; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--brand);
}
.note-sec:first-child { margin-top: 0; }
/* A category stands above the section headings: same family, one step louder,
   and a rule over it rather than under (Dr Daniel, 2026-09-14). */
.note-cat {
  margin: 22px 0 2px; padding-top: 10px; border-top: 2px solid var(--brand-light, #b98cc4);
  font-size: .82rem; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text);
}
.note-cat + .note-sec { margin-top: 8px; }
.cn-cat { margin-top: 26px; }
.cn-sec { margin: 18px 0 8px; }
/* Current bedding: the pillow on the left, the mattress on the right, each
   question under the last (Dr Daniel, 2026-09-14). One column on a narrow
   screen, where side by side would squeeze six chips into nothing. */
/* Not an even split (Dr Daniel, 2026-09-17): "current pillow answer line
   no need so long, can cut short by 20%, then give the space to current
   mattress" — the mattress side holds the sinking pairs and their remarks,
   the pillow side three short words. */
.cn-bedding { display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); gap: 12px 18px; }
@media (max-width: 760px) { .cn-bedding { grid-template-columns: minmax(0, 1fr); } }
.cn-bedcol > * + * { margin-top: 8px; }
/* The bedding columns take the SAME 105px track as every other panel
   (2026-09-17: "Label track pls uniform throughout the case notes"). */
/* Patient history as one sheet (Dr Daniel, 2026-09-14: "make it in one typing
   box … leave the titles there and let us to type the answers"). One border
   round all three sections and a rule under each answer instead of a box round
   it — the page reads as something written on, not something filled in. */
.cn-sheet {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 2px 16px 16px; background: var(--card);
}
.cn-sheet > .cn-sec:first-child { margin-top: 14px; }
/* Four panels on one card: the gap between them does the separating, so the
   heads inside them can be quieter and closer than they were when each one
   had to hold its own section apart. */
.cn-sheet + .cn-cat, .cn-sheet + .cn-sheet { margin-top: 14px; }
.cn-sheet .cn-sec { margin: 16px 0 7px; }
.cn-sheetgrid {
  /* ONE label track for the whole card (Dr Daniel, 2026-09-17: "Label
     track pls uniform throughout the case notes. I saw some gap wider and
     some narrower"). 105px is the longest label that should not wrap —
     "Is the bed sinking?" — measured at the .8rem the labels wear, plus
     rounding headroom; only "Material / Firmness / Duration" wraps, as it
     always has. */
  display: grid; grid-template-columns: 108px minmax(0, 1fr);
  gap: 0 14px; align-items: baseline;
}
.cn-sheetlab { font-size: .8rem; color: var(--muted); }
/* Labelled lines that follow a prose box need the air a heading would have
   given them. */
.cn-sheetgap { margin-top: 16px; }
/* Age, gender and the latex question across, then the three measurements
   under them (Dr Daniel, 2026-09-14). Six label-and-line pairs, three to a
   row, so the answers line up in columns down the panel. */
/* The three-across patient grid keeps its own UNIFORM pair track — 80px
   fits its longest label (Shoulder (cm), 79px) without eating the lines. */
.cn-sheet3 { grid-template-columns: repeat(3, 80px minmax(0, 1fr)); }
@media (max-width: 900px) { .cn-sheet3 { grid-template-columns: repeat(2, 80px minmax(0, 1fr)); } }
@media (max-width: 620px) { .cn-sheet3 { grid-template-columns: minmax(0, 1fr); } }
/* Positions: Supine __ %  Side __ %  Prone __ %, printed once and typed in
   three (Dr Daniel, 2026-09-14). The names are quiet because they are not
   answers — the numbers are. Spinners off: this is a percentage, not a
   quantity anybody wants to click up and down five at a time. */
/* Centred, not baseline-aligned: a right-aligned number box sits its
   baseline lower than a plain word, and the row grew 12px taller than every
   other answer on the panel to hold both. */
.cn-sheet .pct-trio .cn-chips { gap: 2px 20px; align-items: center; }
.cn-sheet .pct-trio .pct-name {
  font-size: .8rem; font-weight: 400; color: var(--muted); margin: 0 3px 0 0;
}
.cn-sheet .pct-trio .pct-box {
  /* .cn-opts input adds a 7px top margin for the free-text box that used
     to sit under a chip group; there is no chip group here. */
  width: 54px; min-height: 30px; text-align: right; padding: 5px 3px; margin: 0;
  -moz-appearance: textfield; appearance: textfield;
}
.cn-sheet .pct-trio .pct-box::-webkit-outer-spin-button,
.cn-sheet .pct-trio .pct-box::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cn-sheet .pct-trio .pct-pc { font-size: .8rem; color: var(--muted); margin-left: 2px; }
.cn-sheet .pct-trio .pct-legacy { flex-basis: 100%; margin: 2px 0 0; }
/* An empty note still claimed a flex row, which made the Positions line half
   as tall again as every other answer on the panel. */
.cn-sheet .pct-trio .pct-legacy:empty { display: none; }

/* A catalogue picker on a panel line: it keeps its button, drops the frame
   the button used to sit in. */
.cn-sheet .cn-opts { border: 0; padding: 0; }
.cn-sheet .cn-opts .cn-chips { padding: 3px 0; }
.cn-linewrap { display: block; }
/* The phrases this clinic writes, offered under the box while it has focus
   (Dr Daniel, 2026-09-14). Quiet by default — they are a shortcut, not an
   instruction — and element-qualified so the house button styling does not
   paint them like something to be pressed. */
.cn-suggest { display: flex; flex-wrap: wrap; gap: 5px; margin: 6px 0 2px; }
button.cn-sugg {
  background: transparent; border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); font-size: .74rem; font-weight: 600;
  padding: 2px 10px; min-height: 0; box-shadow: none; flex: 0 0 auto;
}
@media (hover: hover) { button.cn-sugg:hover {
  border-color: var(--brand); color: var(--brand); background: var(--brand-tint);
} }
/* A line, not a box: the frame drops away and only the rule under the answer
   is left. Element-qualified base rules (input, select, textarea) lose to a
   class, so this needs no !important — but it DOES need the radius and the
   shadow cleared, or the old box shows through at the corners. */
.cn-line {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  background: transparent; box-shadow: none;
  width: 100%; padding: 6px 2px; min-height: 34px;
}
.cn-sheet textarea.cn-line { min-height: 62px; resize: vertical; }
.cn-line:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 1px 0 0 var(--brand);
}
/* A signed note is read, not typed: the rules fade but the words do not. */
.cn-line:disabled { color: var(--text); opacity: 1; border-bottom-style: dotted; }
@media (max-width: 620px) { .cn-sheetgrid { grid-template-columns: minmax(0, 1fr); } }

/* Who served and when ride the camera's row, each label in front of its box so
   the row stays one line tall (Dr Daniel, 2026-09-14). */
/* Age is two digits; the two questions beside it are chips. A narrow track
   for the box and an equal share for each chip group (Dr Daniel, 2026-09-14). */
.cn-patient-a {
  display: grid; grid-template-columns: 150px minmax(0, 1fr) minmax(0, 1fr);
  gap: 14px 16px; align-items: start;
}
@media (max-width: 860px) { .cn-patient-a { grid-template-columns: minmax(0, 1fr); } }
/* The camera, the gallery, who served and when ride the note's title row
   (Dr Daniel, 2026-09-14): the holder takes the room left after the title and
   keeps its own row one line tall. */
/* The title row carries what the note IS; the body opens with what you do to
   it (Dr Daniel, 2026-09-14, swapping the two). */
.cn-headtools { flex: none; margin-left: auto; }
.cn-headtools .cn-typebar { margin: 0; flex-wrap: nowrap; }
.cn-toptools { margin-bottom: 14px; }
/* The item leads every order card (Dr Daniel, 2026-09-14): what it IS, above
   where it goes. It carries the count, so it reads a little larger than the
   rows under it. */
.fo-itemline { font-size: 1.02rem; color: var(--text); }
.fo-itemhead {
  display: flex; align-items: baseline; gap: 10px; margin: 0 0 10px;
  padding-bottom: 8px; border-bottom: 1px solid var(--line);
}
.fo-itemhead .muted { font-size: .78rem; }

/* One drawing per unit, SIDE BY SIDE (Dr Daniel, 2026-09-14). Never stacked:
   the diagram builds bottom-up, so two 2.5cm toppers in a column look like one
   5cm topper, which is the whole thing being guarded against. Each drawing
   wears its own frame and says which unit it is. */
/* A grid, not a flex row: squeezed into a narrow column the drawing ran its
   "Embroidery Side" and "Label Side" captions together, and an unreadable
   drawing is a worse fault than a wrapped one. Each unit gets the width the
   diagram needs; they sit side by side where there is room and stack where
   there is not. Stacking is safe because the FRAME and the "Unit 1 of 2"
   caption are what stop two units reading as one taller build — the
   side-by-side arrangement was only ever the belt to that pair of braces. */
.fo-units {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px; align-items: start;
}
.fo-unit {
  margin: 0; border: 1px solid var(--line); border-radius: 12px;
  padding: 8px 10px 6px; background: var(--card); break-inside: avoid;
}
/* Top LEFT of the frame and in the brand ink, not grey underneath it (Dr
   Daniel, 2026-09-14: "make it at the top left of the preview component. And
   the font colour has to be obvious"). Which unit you are looking at is the
   first thing to know about a drawing, not a footnote to it. */
.fo-unit figcaption {
  margin: 0 0 8px; text-align: left; font-size: .82rem; font-weight: 800;
  letter-spacing: .05em; text-transform: uppercase; color: var(--brand);
}
@media print { .fo-unit figcaption { color: #000; } }
.fo-unit-one { display: inline-block; }
/* What was bought, under the drawing. It carries its own count, so it reads
   "2.5cm Lunox Latex Topper (Queen) x6" (Dr Daniel, 2026-09-14). */
.fo-unitlabel {
  margin: 8px 0 0; font-weight: 700; font-size: .92rem; color: var(--text);
}
@media print {
  .fo-units { gap: 10px; }
  .fo-unit { border: 1px solid #000; }
  .fo-unit figcaption { color: #000; }
}

/* How many to MAKE. The spec beside it describes one unit, so this has to
   carry more weight than a number in a list (Dr Daniel, 2026-09-14: two 2.5cm
   toppers must never be built as one 5cm one). Printed as well as on screen —
   the bench works off the paper. */
.fpo-units {
  color: var(--bad); font-weight: 800; letter-spacing: .04em;
}
/* Black on paper, not red: a mono printer renders red as a mid grey, which
   would make the most important line on the work order the FAINTEST thing on
   it. The weight carries it there instead. */
@media print { .fpo-units { color: #000; } }

/* A button on its own, no longer inside a panel (Dr Daniel, 2026-09-14). */
.cn-pressbtn { margin: 16px 0 4px; }
.cn-toptools .cn-toprow { margin: 0; }
/* On a phone the head WRAPS (Dr Daniel, 2026-09-14: "case note mobile version
   not user friendly"). Four pills beside a customer's name need 516px; a phone
   head has 327px, which squeezed the name to 42px and pushed the ✕ clean off
   the screen — the note could not be closed at all. The name and the ✕ keep
   the first line; the pills take their own underneath, two and two. They are
   still at the top of the box, which is where they were asked for. */
@media (max-width: 700px) {
  .modal .modal-head { flex-wrap: wrap; row-gap: 8px; }
  .modal .modal-head h2 { flex: 1 1 auto; min-width: 0; font-size: 1rem; }
  .cn-headtools { order: 3; flex: 1 0 100%; margin-left: 0; }
  .cn-headtools .cn-typebar { flex-wrap: wrap; gap: 6px; }
  .cn-headtools .cn-typebar button {
    flex: 1 1 calc(50% - 3px); min-width: 0; white-space: normal;
    /* A thumb, not a mouse pointer. */
    min-height: 40px;
  }
  .modal .modal-x { width: 40px; height: 40px; }
  /* Save Draft is `small` and came out 10px shorter than the Sign and Bill
     beside it — the one button in the foot you press to keep your work was
     the hardest of the three to hit. */
  /* `.modal .actions > button.small` sets 34px at (0,3,1), so a plainer
     selector here lost the tie. Matched, and later in the file. */
  .modal .actions button, .modal .actions > button.small { min-height: 42px; }

  /* The camera row: the spacer has nothing to push against here, and the two
     fields are easier to type into at full width. */
  .cn-toptools .cn-gap { display: none; }
  .cn-toptools .cn-toprow button { min-height: 40px; }
  .cn-toptools .cn-by { flex: 1 1 100%; }
  .cn-toptools .cn-by input, .cn-toptools .cn-by select {
    flex: 1 1 auto; max-width: none; min-height: 38px; font-size: .9rem;
  }

  /* Writing surfaces and the things you tap on them. */
  .cn-line { min-height: 40px; }
  .cn-sheet textarea.cn-line { min-height: 76px; }
  .cn-sheet .pct-trio .pct-box { min-height: 38px; width: 62px; }
  .cn-sheet .cn-pick { min-height: 44px; }
  button.cn-sugg { min-height: 32px; padding: 4px 12px; font-size: .8rem; }
  /* 16px stops iOS zooming the page when a field takes focus. */
  .cn-sheet input.cn-line, .cn-sheet textarea.cn-line { font-size: 16px; }
}
.cn-toprow { align-items: center; }
.cn-by { display: flex; align-items: center; gap: 6px; }
.cn-by label { margin: 0; font-size: .72rem; color: var(--muted); white-space: nowrap; }
.cn-by input, .cn-by select { width: auto; max-width: 170px; min-height: 34px; padding: 4px 9px; font-size: .84rem; }
.note-sec-ic { font-size: .9rem; line-height: 1; }
/* ONE label column for the whole card (Dr Daniel, 2026-09-14: "why is the case
   notes so messy"). Each section used to size its own, so the answers started
   at a different place in every one of them; a fixed column lines them all up
   down the card. */
.note-fields {
  display: grid; grid-template-columns: 210px minmax(0, 1fr);
  gap: 5px 16px; margin: 0; font-size: .87rem; align-items: baseline;
}
/* Current bedding is two columns, because it is two things. */
.note-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 24px; }
@container (max-width: 640px) { .note-cols { grid-template-columns: minmax(0, 1fr); } }
.note-subsec {
  margin: 0 0 4px; font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted);
}
/* An answer whose heading already named it: no label column at all, and the
   line breaks the clinician typed are kept (Dr Daniel, 2026-09-14). */
.note-solo {
  margin: 0 0 2px; font-size: .87rem; white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* Inside a column the label track is narrower — the words are shorter there. */
.note-fields-sub { grid-template-columns: 150px minmax(0, 1fr); column-gap: 12px; }
.note-fields dt { color: var(--muted); }
.note-fields dt .ic { vertical-align: -2px; }
/* An icon-only action in a modal's foot keeps a target to press. */
.actions button.iconlabel { min-width: 40px; }
/* The state and the version sit at the right end of the title row. */
.modal-head .note-headstate {
  flex: none; margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
}
.note-fields dd { margin: 0; white-space: pre-wrap; font-weight: 600; }
@container (max-width: 460px) { .note-fields { grid-template-columns: 1fr; gap: 0 0; }
  .note-fields dd { margin-bottom: 6px; } }
/* Uploads on a past note (Dr Daniel, 2026-09-07): thumbnails in a row,
   tap to open full size; non-image files as link chips. */
.note-atts { margin-top: 10px; }
.note-atts-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-start; }
.note-atts-row img {
  max-height: 150px; max-width: 220px; border-radius: 10px; display: block;
  border: 1px solid var(--line); cursor: zoom-in;
}
/* An upload tile in the editor carries its own ✕ remove button, top-right
   (Dr Daniel, 2026-09-07) — drafts only; the server refuses once signed. */
.att-tile { position: relative; text-align: center; }
.att-tile .att-del {
  position: absolute; top: -6px; right: -6px; z-index: 2;
  width: 22px; height: 22px; min-height: 0; padding: 0; border-radius: 50%;
  background: #fff; color: #99291f; border: 1px solid #e6c9c5;
  font-size: .78rem; font-weight: 800; line-height: 1; box-shadow: 0 1px 4px rgba(0,0,0,.18);
}
@media (hover: hover) { .att-tile .att-del:hover { background: #fbe4e2; } }
/* Full-size image viewer with a way out (Dr Daniel, 2026-09-07: "Allow us
   to exit from the full page") — ✕, backdrop click, or Esc. */
.imgview {
  position: fixed; inset: 0; z-index: 900; background: rgba(20, 12, 24, .92);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
}
.imgview-body { display: flex; align-items: center; justify-content: center; width: 100%; flex: 1; padding: 46px 16px 0; }
.imgview-body img { max-width: 96vw; max-height: 82vh; border-radius: 8px; box-shadow: 0 18px 50px rgba(0,0,0,.5); }
.imgview-cap { color: #e9e2ec; font-size: .8rem; padding-bottom: 14px; text-align: center; }
.imgview-x {
  position: absolute; top: 14px; right: 16px; z-index: 2;
  width: 40px; height: 40px; min-height: 0; padding: 0; border-radius: 50%;
  background: #fff; color: var(--text); border: none; font-size: 1.05rem; font-weight: 800;
  box-shadow: 0 4px 16px rgba(0,0,0,.35); cursor: pointer;
}
@media (hover: hover) { .imgview-x:hover { background: #f0ebf3; } }
.note-older { margin-top: 12px; border-top: 1px dashed var(--line); padding-top: 8px; }
.note-older > summary { cursor: pointer; font-size: .8rem; }

/* ---- appointment popup: who they are, and what they have bought ------- */
/* The customer's name is a link into their record (2026-08-28). */
.cust-link { color: var(--brand); text-decoration: none; border-bottom: 1px solid transparent; }
@media (hover: hover) { .cust-link:hover { border-bottom-color: currentColor; } }
.appt-buys { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 10px; }
.buys-head { display: flex; align-items: baseline; gap: 10px; }
.buys-head h3 { margin: 0; flex: 1; }
.buys-total { font-size: .82rem; font-weight: 700; color: var(--brand); white-space: nowrap; }
.buys-table { width: 100%; margin-top: 6px; }
.buys-table td { padding: 5px 6px; border-bottom: 1px solid var(--line); font-size: .85rem; }
.buys-table tr:last-child td { border-bottom: none; }
.buys-table tr.click { cursor: pointer; }
@media (hover: hover) { .buys-table tr.click:hover td { background: var(--brand-tint); } }
/* A pre-pilot purchase has no invoice here to open — say where it lives. */
.buys-kumo {
  margin-left: 6px; padding: 1px 6px; border-radius: 6px;
  background: var(--neutral-bg); color: var(--muted);
  font-size: .64rem; font-weight: 700; letter-spacing: .04em;
}
.buys-owed { display: block; font-size: .68rem; font-weight: 700; color: #b45309; }
/* The KumoDoc sales-order number, kept under its invoice number. */
.so-sub { font-size: .68rem; letter-spacing: .02em; }

/* ---- the online channel's calendar: which branch am I reading? --------- */
/* Only ever rendered in CS (2026-08-28). It sits beside Today in the header,
   nudged away from it so the two do not read as one control. */
.cal-branch { align-items: center; gap: 4px; margin-right: 4px; }
.cal-branch .cb-label {
  font-size: .68rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); margin-right: 2px;
}
/* One column per branch: the header carries the branch's own colour as a
   band, so a column is identifiable without reading its label. */
.cal-branches .hd.br-hd { color: #fff; border-bottom: none; }
.cal-branches .hd.br-hd strong { font-size: .88rem; letter-spacing: .03em; }
.cal-branches .hd.br-zp { background: #470054; }
.cal-branches .hd.br-8tr { background: #0e7490; }
.cal-branches .hd.br-ru { background: #a13d63; }
/* The bookings count rides the SAME row as the branch name, tucked in
   the right corner (Dr Daniel, 2026-09-06: "xx bookings dun use up a
   row"). */
.cal-branches .hd.br-hd { position: relative; }
.cal-branches .hd .hd-count {
  position: absolute; top: 6px; right: 8px;
  font-size: .62rem; font-weight: 600; opacity: .82; letter-spacing: .02em;
}

/* ---- register popup: allocation chips + payment rows (2026-08-27) ---- */
.alloc-chips { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
/* No fill (Dr Daniel, 2026-09-02): the name is a label, not a slab. */
.alloc-chip {
  display: inline-flex; align-items: center; gap: 7px;
  background: none; color: var(--brand); font-weight: 700;
  border-radius: 999px; padding: 6px 14px 6px 0; font-size: .88rem;
}
/* As a button it must lose the global button chrome, or a pill turns back into
   a slab (Dr Daniel, 2026-08-29). The pencil sits a shade back so the name
   stays the thing you read. */
button.alloc-chip {
  border: none; box-shadow: none; min-height: 0; cursor: pointer;
  font-family: inherit; line-height: 1.3;
}
@media (hover: hover) { button.alloc-chip:hover { background: var(--brand-tint); box-shadow: none; } }
.alloc-chip .ic:last-child { opacity: .65; }
@media (hover: hover) { button.alloc-chip:hover .ic:last-child { opacity: 1; } }
.pay-row {
  display: flex; align-items: center; gap: 12px;
  border: 1px solid var(--line); border-radius: 12px;
  padding: 8px 12px; margin-bottom: 6px;
}
.pay-row .pay-method { font-weight: 700; }
.pay-row .pay-amt { margin-left: auto; font-weight: 800; }

/* ---- embedded pages in popups (2026-08-27) ---- */
.embed-frame { width: 100%; height: 76vh; border: 0; display: block; border-radius: 8px; }
/* A full-screen popup: the whole viewport, no rounded card, and the frame
   takes every pixel under the title bar (2026-09-13). The BACKDROP carries
   its own class — an earlier `:has(> .modal.full)` selector was silently not
   matching, which left the 4vh padding on and the window short. */
/* Almost the whole screen, but still a CARD (Dr Daniel, 2026-09-13: "make it
   still rounded corner") — a hair of backdrop all round so the corners read. */
.modal-backdrop.full { padding: 10px; overflow: hidden; }
.modal.full {
  max-width: none; width: 100%; height: 100%; max-height: 100%; margin: 0;
  border-radius: 18px; padding: 0 14px 10px; display: flex; flex-direction: column;
}
.modal.full .modal-head { margin-bottom: 8px; flex: none; }
.embed-frame.full { flex: 1 1 auto; height: auto; min-height: 0; border-radius: 12px; }
body.embed .sidebar, body.embed .topbar { display: none !important; }
body.embed .main { margin-left: 0 !important; }
body.embed .shell.rail .main { margin-left: 0 !important; }
body.embed .content { padding: 8px 14px; }
/* A page inside a popup fills the popup (Dr Daniel, 2026-09-13, of the
   full-screen Pressure Mapping window: "dun have space here ... make
   everything extended to this area"). The ordinary 1320px reading width
   left a dead band down the right of a full-screen popup; in the narrow
   960px popups the cap never bound anyway, so nothing else moves. */
body.embed .content { max-width: none; }

/* ---- calendar header line (2026-08-27) ---- */
.cal-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.cal-head h1 { margin: 0; }
/* Three-zone calendar header (Dr Daniel, 2026-09-05): title + branch chips
   left, the date stepper dead centre, customer search right. The whole row
   is inset by the rail's width so all three sit WITHIN the calendar's own
   width (Dr Daniel, later that day) — left edge over the time column,
   right edge flush with the last branch column. */
/* The 224px left margin that used to clear the rail is GONE with the rail
   (Dr Daniel, 2026-09-06: "ALL THE WAY TO THE LEFT OF THE CALENDAR"). */
.cal-head3 { display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; }
.ch-left { display: flex; align-items: center; gap: 14px; min-width: 0; }
/* The page title moved onto the RAIL, above the mini calendar (Dr Daniel,
   2026-09-05: "MOVE THIS HEADER TO BE ABOVE THE MINI CALENDAR") — sized
   for the 224px column, one line, never a wrapped tower. */
.cal-rail h1 {
  margin: 8px 0 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 1.15rem;
  /* flex:none — the rail is a flex column and a shrinkable h1 with hidden
     overflow collapses to 0px when the rail runs tall. */
  flex: none;
}
/* The compact control cluster on the calendar's top row (Dr Daniel,
   2026-09-06): 1D·3D·1W chips, small By staff / Block / Roster words. */
/* ONE row for the whole control strip (Dr Daniel, 2026-09-06: "Dun all
   stack here, make it in 1 grid row") — tools left, stepper, search. */
.cal-head3.cal-head-flex {
  display: flex; align-items: center; gap: 6px; flex-wrap: nowrap;
  /* FROZEN under the topbar (Dr Daniel, 2026-09-06: "it shouldnt move
     when we scroll up and down") — same treatment as the popups' bars.
     Above the calendar's frozen branch header (100): this toolbar's
     stacking context carries the date popup, which the header briefly
     buried when it rose to 100 ("HALO FIX THIS BUG"). */
  position: sticky; top: 59px; z-index: 110;
  background: var(--bg, #f4f2f7); padding: 3px 0;
}
/* Tools hard LEFT, the date stepper at the exact MIDDLE of the row,
   branch toggles + search right (Dr Daniel, 2026-09-06). */
.cal-head3.cal-head-flex { position: sticky; }
.cal-head3.cal-head-flex .ch-left { flex: 0 1 auto; min-width: 0; }
.cal-head3.cal-head-flex .ch-mid {
  position: absolute; left: 50%; transform: translateX(-50%);
}
.cal-head3.cal-head-flex .ch-right {
  margin-left: auto; min-width: 110px; display: flex; align-items: center;
  justify-content: flex-end; gap: 6px;
}
@media (max-width: 1100px) {
  .cal-head3.cal-head-flex .ch-mid { position: static; transform: none; }
}
.cal-head3.cal-head-flex .ch-right input { max-width: 170px; }
.cal-head3.cal-head-flex .ch-right input { min-height: 32px; font-size: .8rem; }
.cal-head3.cal-head-flex .cal-step-date { font-size: .88rem; padding: 4px 8px; }
/* The CS branch chips squeeze with everything else on the one row. */
.cal-tools .branch-seg .br-chip { padding: 3px 7px; font-size: .66rem; }
@media (max-width: 1100px) { .cal-head3.cal-head-flex { flex-wrap: wrap; } }
.cal-tools { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; min-width: 0; }
/* The page title rides the control row now that the rail is gone
   (Dr Daniel, 2026-09-06) — one compact line, never a wrapped tower. */
.cal-tools h1 {
  margin: 0 4px 0 0; font-size: .94rem; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 122px; flex: 0 1 auto;
}
.cal-full .cal-main { width: 100%; }
/* The view is ONE small dropdown now (Dr Daniel, 2026-09-06):
   1D / 3D / 1W / By staff. */
.view-sel {
  min-height: 0; width: auto; padding: 4px 6px; font-size: .74rem;
  font-weight: 700; color: #57505e; border-radius: 8px;
  border: 1px solid #e0dce4; background: #f4f3f5; box-shadow: none; cursor: pointer;
}
/* Block wears the blocked band's own light-purple hatch tones. */
.block-btn {
  background: repeating-linear-gradient(45deg, #f5ecf7, #f5ecf7 7px, #e6d7ee 7px, #e6d7ee 14px);
  color: #5b4a68; border: 1px solid #d3bfdf; box-shadow: none; font-weight: 700;
}
@media (hover: hover) { .block-btn:hover { filter: brightness(.97); box-shadow: none; } }
/* Three tools, three looks (Giselle, 2026-09-26: "these 3 buttons too
   similar"). Block keeps the blocked-slot hatch above; Roster is the one
   solid brand button on the row; the next-mattress note is a green note,
   square-cornered so it does not read as a third pill. */
.roster-btn { background: #fff; color: var(--brand, #470054); border: 1.5px solid var(--brand-light, #6a2178); box-shadow: none; font-weight: 700; }
@media (hover: hover) { .roster-btn:hover:not(:disabled) { background: var(--brand-tint, #f5ecf7); box-shadow: none; } }
.mattress-next-btn { background: var(--ok-bg, #e3f4e8); color: var(--ok, #1a7a3c); border: 1px solid #bfe3c9; border-left: 4px solid var(--ok, #1a7a3c);
  border-radius: 6px; box-shadow: none; font-weight: 500; display: inline-flex; align-items: baseline; gap: 6px; }
.mattress-next-btn .mn-label { font-weight: 500; opacity: .85; }
.mattress-next-btn .mn-name { font-weight: 800; }
@media (hover: hover) { .mattress-next-btn:hover:not(:disabled) { background: #d6eddc; box-shadow: none; } }
.cal-tools .cal-tool { min-height: 0; padding: 3px 8px; font-size: .72rem; white-space: nowrap; }
.ch-mid { justify-self: center; position: relative; }
.ch-right { justify-self: end; min-width: 0; }
/* Mid widths: the search row drops under the title/stepper row instead of
   crushing the left column. */
@media (max-width: 1200px) {
  .cal-head3 { grid-template-columns: 1fr auto; }
  .ch-right { grid-column: 1 / -1; justify-self: end; }
}
.cal-branch { flex-wrap: nowrap; }
.cb-sep { width: 1px; align-self: stretch; margin: 3px 5px; background: var(--line); flex: none; }
/* The date button drops a one-month picker right under the stepper. */
button.cal-step-date { background: none; border: none; box-shadow: none; cursor: pointer; color: var(--text); }
@media (hover: hover) { button.cal-step-date:hover { background: var(--brand-tint); border-radius: 8px; } }
.date-pop {
  /* Slimmed 2026-09-06 ("make the calendar smaller"); bottom kept tight —
     every collapsible calendar now ends on the bare Clear word. */
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  z-index: 120; width: 232px; padding: 8px 12px 6px;
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 16px 40px rgba(42, 31, 46, .22);
}
/* The rail's mini-month is sized for a 170px sidebar — 22px cells and .68rem
   digits read as a BLANK WHITE BOX in a floating popup (Dr Daniel,
   2026-09-05: "where's the collapsible calendar?"). The popup's copy is a
   real date picker: big title, 38px day cells, readable digits. */
.date-pop .mc-head { margin-bottom: 4px; }
.date-pop .mc-title { font-size: .8rem; }
.date-pop .mc-nav { font-size: 1.05rem; padding: 1px 9px; }
.date-pop .mc-grid { gap: 2px; }
.date-pop .mc-wd { font-size: .64rem; padding-bottom: 2px; }
/* 28px cells ≈ the column width, so the chosen day reads as a CIRCLE. */
.date-pop .mc-day { height: 28px; font-size: .76rem; border-radius: 999px; }
/* Search customer on the header's right, results dropping over the grid. */
.head-cust { position: relative; }
.head-cust-in { width: 220px; min-height: 36px; }
.head-cust-drop {
  position: absolute; top: calc(100% + 4px); right: 0; z-index: 80; width: 280px;
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 14px 34px rgba(42, 31, 46, .18); padding: 6px; margin-top: 0;
}
/* .cust-find's display:flex beats the hidden ATTRIBUTE (same specificity,
   later rule) — the exact trap that once left the factory chat box open.
   Without this, the empty dropdown floats as a white sliver under the
   search bar from the moment the page loads (Dr Daniel, 2026-09-05:
   "Theres bugs"). */
.head-cust-drop[hidden], .date-pop[hidden] { display: none !important; }
/* Order Form title row: smaller chips (Dr Daniel, 2026-09-06: "These
   buttons make it smaller") and the hotel-style range picker. */
.of-title-row .br-chip { padding: 2px 8px; font-size: .66rem; }
.mc-day.inrange { background: var(--brand-tint, #f5ecf7); }

/* Clicking a side tab swaps only the PAGE — the rail must not redraw
   (Dr Daniel, 2026-09-06: "it shall just refresh the page, not the side
   tab"). Cross-document view transitions: the sidebar and topbar carry
   their own names, so their unchanged pixels ride across the navigation
   frozen while the content area alone cross-fades. Browsers without the
   feature simply keep doing full loads. */
@view-transition { navigation: auto; }
.sidebar { view-transition-name: lx-rail; }
.topbar { view-transition-name: lx-topbar; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
/* The picked CATEGORY tab fills brand purple — a different family from
   the dark branch ALL (Dr Daniel, 2026-09-06). */
.br-chip.on.tab-chip { background: var(--brand, #470054); border-color: var(--brand, #470054); }
.head-cust-drop:empty { display: none; }

/* ---- collapsed-rail alignment (2026-08-27) ----
   Two silent uglifiers: the rail's own vertical scrollbar ate 16px and pushed
   every icon off-centre, and the branch chips — invisible at opacity 0 —
   still held ~120px of dead space under the logo. The scrollbar hides while
   collapsed (the flyout keeps it), invisible things now take no space, the
   icon-label gap collapses so icons sit dead centre, and group separators
   become short centred ticks instead of edge-to-edge lines. */
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within), .shell.rail.rail-hold .sidebar:hover { scrollbar-width: none; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within)::-webkit-scrollbar, .shell.rail.rail-hold .sidebar:hover::-webkit-scrollbar { display: none; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) > .sub, .shell.rail .sidebar:not(:hover):not(:focus-within) .foot, .shell.rail.rail-hold .sidebar:hover > .sub, .shell.rail.rail-hold .sidebar:hover .foot { display: none; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav a, .shell.rail.rail-hold .sidebar:hover nav a { gap: 0; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav a .lbl, .shell.rail.rail-hold .sidebar:hover nav a .lbl { display: none; } }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav .nav-group + .nav-group, .shell.rail.rail-hold .sidebar:hover nav .nav-group + .nav-group {
  border-top: none; margin-top: 3px; padding-top: 3px; position: relative;
} }
@media (hover: hover) { .shell.rail .sidebar:not(:hover):not(:focus-within) nav .nav-group + .nav-group::before, .shell.rail.rail-hold .sidebar:hover nav .nav-group + .nav-group::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 22px; height: 2px; border-radius: 2px; background: var(--brand-tint-2);
} }

/* ============================================================================
   DESIGN PASS (2026-08-27) — cohesive polish over the whole system.
   Principles: brand purple leads; teal/amber/blue accents give the pages air
   without stealing meaning (red stays reserved for destructive); motion is
   small, quick and one-shot; everything switches off under reduced-motion.
   ========================================================================== */

:root {
  --accent-teal: #0e7490;  --accent-teal-bg: #e6f4f7;
  --accent-amber: #b45309; --accent-amber-bg: #fdf3e3;
  --accent-blue: #0b5fa5;  --accent-blue-bg: #e8f1fa;
  --grad-brand: linear-gradient(135deg, #5c0d6e 0%, #470054 55%, #36003f 100%);
  --shadow: 0 1px 3px rgba(71, 0, 84, .05), 0 6px 18px rgba(71, 0, 84, .06);
  --shadow-lift: 0 6px 14px rgba(71, 0, 84, .10), 0 16px 36px rgba(71, 0, 84, .10);
}

/* A quiet, colourful ground: lavender wash with a teal breath top-right and a
   warm one bottom-left. Fixed, so scrolling content glides over it. */
body {
  background:
    radial-gradient(1100px 480px at 88% -8%, rgba(14, 116, 144, .06), transparent 60%),
    radial-gradient(900px 540px at -8% 108%, rgba(180, 83, 9, .05), transparent 55%),
    linear-gradient(168deg, #f8f4fb 0%, #f3f0f8 55%, #f0f2f8 100%);
  background-attachment: fixed;
}

/* Glassy topbar — content slides beneath it. */
.topbar {
  background: rgba(255, 255, 255, .74);
  backdrop-filter: blur(10px) saturate(1.15);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  border-bottom: 1px solid rgba(71, 0, 84, .07);
}

/* Sidebar: a whisper of gradient, links that answer the cursor, the active
   page as a gradient pill. */
.sidebar { background: linear-gradient(178deg, #fff 0%, #fbf8fd 70%, #f8f3fb 100%); }
.sidebar nav a { transition: background .12s, color .12s, transform .12s; border-radius: 10px; }
@media (hover: hover) { .sidebar nav a:hover { transform: translateX(2px); } }
@media (hover: hover) { .shell.rail .sidebar nav a:hover { transform: none; } }
.sidebar nav a.active {
  background: var(--grad-brand); color: #fff; font-weight: 600;
  box-shadow: 0 4px 12px rgba(71, 0, 84, .28);
}

/* Headings: the h1 wears the brand gradient; content h2s get a colour tick. */
.content h1 {
  background: var(--grad-brand);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: var(--brand);
  width: fit-content;
}
.content h2 { position: relative; padding-left: 13px; }
.content h2::before {
  content: ""; position: absolute; left: 0; top: .16em; bottom: .16em; width: 5px;
  border-radius: 4px; background: var(--grad-brand);
}
/* Modals keep their own flat headings — the tick belongs to the page. */
.modal h2::before { content: none; }
.modal h2 { padding-left: 0; }

/* Cards: hairline edge, gentle lift when the cursor arrives. */
.card { border: 1px solid rgba(71, 0, 84, .05); }
@media (hover: hover) { .card:hover { box-shadow: var(--shadow-lift); border-color: rgba(71, 0, 84, .10); } }

/* Buttons: a small rise on hover, settle on press. The month-grid days and
   calendar chips stay still — motion there reads as jitter, not delight. */
button, .btn { transition: background .12s, color .12s, transform .09s, box-shadow .15s, border-color .12s, filter .12s; }
@media (hover: hover) { button:hover:not(:disabled):not(.mc-day):not(.mc-nav):not(.pt-chip):not(.br-chip):not(.modal-x):not(.rail-btn):not(.name-link) { transform: translateY(-1px); } }
button:active:not(:disabled) { transform: translateY(0) scale(.977); }
button.primary-action { background: var(--grad-brand); box-shadow: 0 6px 18px rgba(61, 0, 71, .38); }
@media (hover: hover) { button.primary-action:hover { background: var(--grad-brand); filter: brightness(1.12); } }

/* Pick buttons and header tiles float a little on hover. */
@media (hover: hover) { .pickbtn:hover { box-shadow: 0 6px 16px rgba(71, 0, 84, .10); } }
@media (hover: hover) { button.hdr-tile:hover { box-shadow: 0 8px 20px rgba(71, 0, 84, .10); } }

/* Tables: headers in muted brand, a soft wash under the pointed-at row. */
.content th { color: var(--brand-light); }
@media (hover: hover) { .content table tr:hover > td { background: rgba(71, 0, 84, .022); } }

/* Inputs: a clear brand focus ring. */
input:focus, select:focus, textarea:focus {
  border-color: var(--brand-light);
  box-shadow: 0 0 0 3px rgba(71, 0, 84, .10);
  outline: none;
}

/* Content entrance: a quick staggered fade — opacity only, so the frequent
   in-place re-renders (saving a draft, moving a booking) stay calm. */
@keyframes rise-in { from { opacity: 0; } }
.content > * { animation: rise-in .2s ease-out both; }
.content > *:nth-child(2) { animation-delay: .04s; }
.content > *:nth-child(3) { animation-delay: .08s; }
.content > *:nth-child(4) { animation-delay: .12s; }
.content > *:nth-child(5) { animation-delay: .15s; }
.content > *:nth-child(n+6) { animation-delay: .18s; }

/* Toast arrives with a small spring. */
@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px) scale(.95); }
  70% { transform: translateY(-1px) scale(1.01); }
}
.toast { animation: toast-in .28s cubic-bezier(.2, .8, .3, 1.1); }

/* Slim lavender scrollbars inside the app. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
  background: #d9cbe0; border-radius: 8px;
  border: 2px solid transparent; background-clip: content-box;
}
@media (hover: hover) { ::-webkit-scrollbar-thumb:hover { background-color: #c2a9cf; } }
::-webkit-scrollbar-track { background: transparent; }

/* Print stays ink-true: no washes, no gradient text. */
@media print {
  body { background: #fff !important; }
  .content h1 { -webkit-text-fill-color: #000; color: #000; background: none; }
}

/* Anyone who asked their OS for less motion gets exactly that. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---- factory stage bar (2026-08-27): five steps beside the customer name,
   filled in the current stage's colour; on hold turns red. ---- */
.fo-cust { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* The customer's name with the clinician who served the sale under it
   (Dr Daniel, 2026-09-12) — quiet, so the name still leads the card. */
.fo-who { display: flex; flex-direction: column; line-height: 1.25; }
/* Same weight and size as the invoice line under the FPO number (Dr Daniel,
   2026-09-12: "less bold … same font as the invoice number") — it is a note
   about the sale, not a heading. */
.fo-served { font-size: .74rem; font-weight: 400; color: var(--muted); }
/* Icon progression beside the name (2026-09-02): done filled, current
   ringed, future pale; each icon names its stage on hover. Calm tones. */
.fo-steps { display: inline-flex; gap: 4px; align-items: center; flex: none; margin-left: 10px; }
.fo-steps .fo-step {
  font-style: normal; font-size: .78rem; line-height: 1;
  width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: #f3f0f5; border: 1.5px solid transparent;
  filter: grayscale(1); opacity: .45; cursor: default;
}
.fo-steps .fo-step.done { background: #efeaf7; filter: none; opacity: 1; }
.fo-steps .fo-step.now {
  background: #fff; border-color: var(--brand, #470054);
  filter: none; opacity: 1; box-shadow: 0 0 0 2px rgba(71, 0, 84, .12);
}
.fo-steps.hold .fo-step.now { border-color: #a3352c; box-shadow: 0 0 0 2px rgba(163, 53, 44, .15); }
/* The portal's two filter rows pin to the top of the scroll (2026-09-02). */
.fo-filters {
  position: sticky; top: 0; z-index: 40;
  background: var(--bg);
  /* Bleed across the .content padding so no strip of the scrolling list
     peeks out at the corners (Dr Daniel, 2026-09-02: "white gap on the
     left upper corner"). */
  margin: 0 -22px;
  /* Slimmest breathing room top and bottom (Dr Daniel, 2026-09-06). */
  padding: 2px 22px 2px;
  box-shadow: 0 8px 10px -10px rgba(71, 0, 84, .25);
}
@media (max-width: 900px) {
  .fo-filters { margin: 0 -14px; padding: 2px 14px 2px; }
}
.fo-filters .toolbar { margin: 4px 0; }
/* The first section heading keeps its USUAL 16px under the pinned bar
   (Dr Daniel, 2026-09-06: "make it the usual gap. A bit too tight here"
   — reversing the same morning's shave). */
/* Mochi editor: identity row pinned while the page scrolls (2026-09-03) —
   pairs .fo-filters (sticky, bleed, shadow) with a flex layout. */
.mochi-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mochi-head h1 { margin: 0; font-size: 1.25rem; }

/* Print: worded, no fill (Dr Daniel, 2026-09-05: the bare icon read as
   missing). */
.fo-actbar button.fo-print {
  background: none; border: 1px solid var(--line); box-shadow: none;
  font-size: .9rem; font-weight: 600; padding: 8px 14px; min-height: 0; border-radius: 10px;
}
@media (hover: hover) { .fo-actbar button.fo-print:hover { background: rgba(71, 0, 84, .08); } }
/* Material chips coloured like their preview bands (2026-09-02). The chosen
   one keeps its material colour and takes a dark ink + its own outline. */
.st-chip.mat-latex_topper, .st-chip.mat-latex { background: var(--mat-latex); }
.st-chip.mat-memory_foam { background: var(--mat-memory); }
.st-chip.mat-soft_foam { background: var(--mat-soft); }
.st-chip.mat-medium_foam { background: var(--mat-medium); }
.st-chip.mat-hard_foam { background: var(--mat-hard); }
.st-chip[class*="mat-"] { color: #4a4150; }
.st-chip.mat-latex_topper.on, .st-chip.mat-latex.on { color: #6b5c20; }
.st-chip.mat-memory_foam.on { color: #2f5c39; }
.st-chip.mat-soft_foam.on { color: #7a5e12; }
.st-chip.mat-medium_foam.on { color: #53417c; }
.st-chip.mat-hard_foam.on { color: #2c567f; }
.st-chip[class*="mat-"].on { background: inherit; font-weight: 700; border-color: transparent; }
.st-chip.mat-latex_topper.on, .st-chip.mat-latex.on { background: var(--mat-latex); }
.st-chip.mat-memory_foam.on { background: var(--mat-memory); }
.st-chip.mat-soft_foam.on { background: var(--mat-soft); }
.st-chip.mat-medium_foam.on { background: var(--mat-medium); }
.st-chip.mat-hard_foam.on { background: var(--mat-hard); }
/* The CHOSEN chip must be unmistakable (Dr Daniel, 2026-09-05: "the option
   buttons didnt show obvious colour for the chosen one") — the unchosen
   ones fade to a pale, calm wash of their material colour, and the chosen
   one keeps the full colour plus a ring in its own ink. */
.st-chip.mat-latex_topper:not(.on), .st-chip.mat-latex:not(.on) { background: color-mix(in srgb, var(--mat-latex) 26%, #fff); }
.st-chip.mat-memory_foam:not(.on) { background: color-mix(in srgb, var(--mat-memory) 26%, #fff); }
.st-chip.mat-soft_foam:not(.on) { background: color-mix(in srgb, var(--mat-soft) 26%, #fff); }
.st-chip.mat-medium_foam:not(.on) { background: color-mix(in srgb, var(--mat-medium) 26%, #fff); }
.st-chip.mat-hard_foam:not(.on) { background: color-mix(in srgb, var(--mat-hard) 26%, #fff); }
.st-chip[class*="mat-"]:not(.on) { color: #938a99; }
@media (hover: hover) { .st-chip.mat-latex_topper:not(.on):hover, .st-chip.mat-latex:not(.on):hover { background: color-mix(in srgb, var(--mat-latex) 55%, #fff); color: #4a4150; } }
@media (hover: hover) { .st-chip.mat-memory_foam:not(.on):hover { background: color-mix(in srgb, var(--mat-memory) 55%, #fff); color: #4a4150; } }
@media (hover: hover) { .st-chip.mat-soft_foam:not(.on):hover { background: color-mix(in srgb, var(--mat-soft) 55%, #fff); color: #4a4150; } }
@media (hover: hover) { .st-chip.mat-medium_foam:not(.on):hover { background: color-mix(in srgb, var(--mat-medium) 55%, #fff); color: #4a4150; } }
@media (hover: hover) { .st-chip.mat-hard_foam:not(.on):hover { background: color-mix(in srgb, var(--mat-hard) 55%, #fff); color: #4a4150; } }
.st-chip[class*="mat-"].on { box-shadow: inset 0 0 0 2px currentColor; }

/* Add-on rows: the remove X rides the item-name line (2026-09-02). */
.addon-row .comp-summary { display: flex; align-items: center; gap: 8px; }
.addon-row .comp-summary strong { flex: 1; min-width: 0; }
/* The whose-cart dock: floats pinned to the page foot (2026-09-02). */
/* Pinned to the foot of the scrolling preview card (2026-09-02). */
/* The phone box and its Add to Cart are what stays pinned — not a white
   strip across the foot of the card (Dr Daniel, 2026-09-12: "no need freeze
   whole column, just freeze the Customer phone number and button"). The
   sticky layer is invisible and lets the pointer through; the lilac pill it
   carries is the only thing that floats, and the only thing that catches a
   click. */
.cart-dock {
  position: sticky; bottom: 0; z-index: 5;
  margin: 12px 0 0; padding: 0 0 10px;
  background: none; box-shadow: none; pointer-events: none;
}
.cart-dock-inner {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--brand-tint, #f5ecf7); border-radius: 14px; padding: 8px 12px;
  pointer-events: auto; box-shadow: var(--shadow-lift);
}
/* Flat add button: no fill, no outline (2026-09-02). */
button.flat-add {
  background: none; border: none; box-shadow: none;
  color: var(--brand); font-size: 1.05rem; padding: 4px 8px;
}
@media (hover: hover) { button.flat-add:hover { background: rgba(71, 0, 84, .08); } }
/* The add-level slot: unmissable, drawn like the empty level it becomes. */
button.add-level {
  display: block; width: 100%; margin: 8px 0 4px;
  background: var(--brand-tint, #f5ecf7); color: var(--brand);
  border: none; border-radius: 12px;
  padding: 12px; font-weight: 700; font-size: .95rem; box-shadow: none;
}
@media (hover: hover) { button.add-level:hover { background: #eee0f2; } }
/* Calm go-action: soft green tint with green ink (2026-09-02). */
button.mochi-go { background: #e5f4e9; color: #166534; border: none; box-shadow: none; font-weight: 700; }
@media (hover: hover) { button.mochi-go:hover { background: #d5ecdc; } }
/* Archive: flat text-only danger, no fill, no outline (2026-09-02). */
.fo-actbar button.mochi-flat, button.mochi-flat {
  background: none; border: none; box-shadow: none; color: #b3261e;
}
@media (hover: hover) { .fo-actbar button.mochi-flat:hover, button.mochi-flat:hover { background: rgba(179, 38, 30, .08); } }
/* Mochi register row actions: bare icons, no fill (2026-09-02). */
button.mochi-rowact, button.mochi-rowact.danger {
  background: none; border: none; box-shadow: none;
}
@media (hover: hover) { button.mochi-rowact:hover { background: rgba(71, 0, 84, .08); } }
/* Stage filter: flat text, underline marks the choice (2026-09-02). */
button.stage-flt {
  background: none; border: none; box-shadow: none; color: var(--muted);
  font-weight: 600; padding: 6px 4px; border-radius: 0;
  border-bottom: 2.5px solid transparent;
}
@media (hover: hover) { button.stage-flt:hover { color: var(--brand, #470054); background: none; } }
button.stage-flt.on {
  color: var(--brand, #470054);
  border-bottom-color: var(--brand, #470054);
}
/* Borderless flag chips on the factory card (2026-09-02). */
.fo-card .prio-asap, .fo-card .prio-later, .fo-card .edited-chip { border: none; }
/* Courier tracking save: small and calm (2026-09-02). */
.fo-card button.fo-save {
  background: #efeaf7; color: #5b4a86; border: none; box-shadow: none;
  padding: 7px 14px; font-weight: 700;
}
@media (hover: hover) { .fo-card button.fo-save:hover { background: #e4dbf1; } }
/* The card wears its ITEM TYPE as a calm accent colour instead of a label:
   mattress lavender, topper sky, delivery slate. */
.fo-card.type-mattress { border-left: 6px solid #b9a5d6; }
.fo-card.type-topper { border-left: 6px solid #9dbedd; }
.fo-card.type-delivery { border-left: 6px solid #b7bec9; }

.fo-bar { display: inline-flex; gap: 3px; align-items: center; flex: none; }
.fo-bar i {
  width: 15px; height: 6px; border-radius: 4px; background: var(--line);
  transition: background .25s ease;
}
.fo-bar i.on { background: var(--bar-c, var(--brand)); }
.fo-bar.hold i.on { background: repeating-linear-gradient(45deg, #a3352c, #a3352c 4px, #d9584f 4px, #d9584f 8px); }

/* Two-calendar range picker on the register (2026-08-27). */
.range-cals { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.range-cals .mc-day { min-height: 30px; font-size: .8rem; }
.flt-chip {
  width: auto; min-height: 38px; padding: 6px 14px; border-radius: 999px;
  font-size: .84rem; background: var(--card); color: var(--brand);
  border: 1px solid var(--line); box-shadow: none; font-weight: 700;
}
@media (hover: hover) { .flt-chip:hover { border-color: var(--brand); background: var(--brand-tint); box-shadow: none; } }
@media (max-width: 640px) { .range-cals { grid-template-columns: 1fr; } }

/* 0.5 cm value buttons in the Mochi measurement pickers (2026-08-27). */
.pick-grid.nums { grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); gap: 6px; }
.pickbtn.num { min-height: 46px; padding: 8px 4px; font-variant-numeric: tabular-nums; }

/* Mochi measurements display (2026-08-27): label · value pairs that never
   wrap — values size to their content and sit right after their label, so
   "0.5 cm" can never break onto two lines. */
.meas-kv {
  /* Labels stay tight (max-content, capped) with each value directly beside
     its label; the spare width lands in the value columns AFTER the value
     text, so nothing stretches to the card edge and nothing can overflow.
     The narrow fallback goes to one pair per line. */
  grid-template-columns: minmax(max-content, 230px) minmax(70px, 1fr) minmax(max-content, 230px) minmax(70px, 1fr);
  column-gap: 14px; row-gap: 8px; font-size: .92rem; align-items: baseline;
}
.meas-kv dd { white-space: nowrap; text-align: left; }
/* The card's own width decides one pair or two per line — the page lays
   cards in columns, so the viewport width says nothing about the card. */
.meas-wrap { container-type: inline-size; }
@container (max-width: 580px) {
  .meas-kv { grid-template-columns: minmax(max-content, 230px) minmax(70px, 1fr); }
}

/* The Mochi register on a phone (Dr Daniel, 2026-09-14: "Make the mochi table
   smaller so that it fits into the mobile view"). Nine columns will not fit
   375px at any type size, so the phone keeps the four that answer "who, when,
   what state, and how do I open it" and folds the rest away. Nothing is lost:
   the clinician moves under the customer's name, and the skin, the heights
   and the extra invoice are all on the card the row opens. */
.mochi-servedby { display: none; }
@media (max-width: 760px) {
  .mochi-table { font-size: .78rem; }
  .mochi-table th, .mochi-table td { padding: 6px 5px; }
  .mochi-table th { font-size: .62rem; letter-spacing: .02em; }
  /* 1 invoice · 2 customer · 3 served by · 4 date · 5 status
     6 skin · 7 heights · 8 extra invoice · 9 open */
  .mochi-table th:nth-child(3), .mochi-table td:nth-child(3),
  .mochi-table th:nth-child(6), .mochi-table td:nth-child(6),
  .mochi-table th:nth-child(7), .mochi-table td:nth-child(7),
  .mochi-table th:nth-child(8), .mochi-table td:nth-child(8) { display: none; }
  .mochi-table .mochi-servedby { display: block; font-size: .7rem; }
  .mochi-table td:first-child { font-size: .72rem; word-break: break-all; }
}
/* Narrower still — the base invoice number goes too, and the row leads with
   the person. It is on the card, one tap away. */
@media (max-width: 430px) {
  .mochi-table th:first-child, .mochi-table td:first-child { display: none; }
  .mochi-table th:nth-child(4), .mochi-table td:nth-child(4) { font-size: .68rem; white-space: nowrap; }
}

/* ---- Customers list (2026-08-28): compact rows so more of the book fits on
   one screen, with lifetime spend reading as money. ---- */
.cust-table th, .cust-table td { padding: 5px 10px; font-size: .85rem; }
.cust-table td { vertical-align: middle; }
.cust-spend { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* An emoji inflates the line box: the same button reads 32px with plain text
   and 51px with a leading emoji, which was quietly making every row half as
   dense again. An explicit height and line-height clamp it. */
.cust-table button.soft {
  padding: 4px 14px; min-height: 0; height: 30px; line-height: 1;
  font-size: .82rem; white-space: nowrap;
}

/* Delivery Order revamp (Dr Daniel, 2026-09-04): the submit button is BIG —
   the reviewer's final act after checking the delivery details. */
/* Aesthetic yet unmissable (Dr Daniel, 2026-09-05): a soft green gradient
   pill with a gentle lift on hover. */
button.submit-final {
  background: linear-gradient(135deg, #23904d, #146032);
  background-color: #1b7a41; color: #fff; font-weight: 800; font-size: 1rem;
  padding: 13px 26px; letter-spacing: .6px; width: 100%; justify-content: center;
  border: none; border-radius: 999px;
  box-shadow: 0 4px 14px rgba(23, 105, 56, .28);
  transition: transform .08s ease, box-shadow .12s ease, filter .12s ease;
}
@media (hover: hover) { button.submit-final:hover {
  filter: brightness(1.06); transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(23, 105, 56, .36);
} }
button.submit-final:active { transform: translateY(0); }
/* Title-bar variant (Dr Daniel, 2026-09-05: "must be as obvious as
   possible") — the same unmissable green, sized for the bar, with a soft
   breathing glow. */
button.submit-final.head {
  width: auto; font-size: .85rem; padding: 9px 18px; margin-left: 8px;
  white-space: nowrap; animation: submit-pulse 2s ease-in-out infinite;
}
@keyframes submit-pulse {
  0%, 100% { box-shadow: 0 4px 14px rgba(23, 105, 56, .28); }
  50% { box-shadow: 0 4px 22px rgba(23, 105, 56, .6); }
}

/* The three category tabs on Delivery Order / Ready for Delivery (Dr Daniel,
   2026-09-04): big pills, the picked one filled brand purple. */
button.cat-tab {
  background: #f4eef6; border: none; color: #6b5c72; font-weight: 700;
  font-size: 1rem; padding: 10px 22px; border-radius: 999px; line-height: 1.2;
}
@media (hover: hover) { button.cat-tab:hover { background: #eadff0; color: var(--brand, #470054); } }
button.cat-tab.on {
  background: var(--brand, #470054); color: #fff;
  box-shadow: 0 3px 10px rgba(71, 0, 84, .28);
}

/* ASAP / LATER priority buttons on the Order Form card (Dr Daniel,
   2026-09-05): green when rushing, amber when the customer waits. */
button.prio-btn {
  /* Slimmed 2026-09-06 ("Make these smaller, noneed use up so much space"). */
  border: 1.5px solid var(--line); background: #fff; color: var(--muted);
  font-weight: 700; padding: 4px 6px; border-radius: 999px; min-height: 30px;
  white-space: nowrap; justify-content: center; width: 100%; font-size: .78rem;
}
/* Calm tones (Dr Daniel, 2026-09-05): soft washes with deep text instead
   of loud solid fills — same language as the payment-method chips. */
button.prio-btn.normal.on { background: #e3f0e6; border-color: #b9d8c2; color: #1d5a33; }
button.prio-btn.asap.on { background: #fbe4e2; border-color: #edc3bf; color: #99291f; }
button.prio-btn.later.on { background: #f9eedb; border-color: #e9d4ad; color: #8a5a10; }

/* Yes / No / TBC chips for "Old Mattress Disposal?" (2026-09-05). */
button.tri-chip {
  /* Slimmed 2026-09-06 alongside the priority buttons. */
  border: 1.5px solid var(--line); background: #fff; color: var(--muted);
  font-weight: 700; padding: 3px 12px; border-radius: 999px; min-height: 28px;
  font-size: .78rem;
}
button.tri-chip.yes.on { background: #1d7a3f; border-color: #1d7a3f; color: #fff; }
button.tri-chip.no.on { background: #b3261e; border-color: #b3261e; color: #fff; }
button.tri-chip.tbc.on { background: #b26a00; border-color: #b26a00; color: #fff; }
@media (hover: hover) { button.tri-chip:not(.on):hover { border-color: var(--brand); color: var(--brand); background: #fff; } }
@media (hover: hover) { button.prio-btn:not(.on):hover { border-color: var(--brand); color: var(--brand); background: #fff; } }

/* A superseded specification is SLASHED OUT in red (Dr Daniel, 2026-09-05);
   hovering it says when and who amended it (the title tooltip). */
.spec-old { position: relative; opacity: .62; }
.spec-old::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top right,
    transparent 48.6%, #b3261e 48.6%, #b3261e 51.4%, transparent 51.4%);
}

/* WhatsApp-style order chat (Dr Daniel, 2026-09-05). LUNOX letters and the
   factory's replies share one thread; the two banners now have their own
   colours — NEWLY EDITED stays amber, the chat is purple-headed. */
.chat-box {
  border: 1.5px solid var(--line); border-radius: 14px; padding: 10px;
  background: #f7f4f9; margin-top: 10px;
}
.chat-head {
  font-weight: 800; color: var(--brand, #470054); margin-bottom: 6px;
  display: flex; align-items: center; gap: 8px;
}
.chat-unread {
  background: #b3261e; color: #fff; border-radius: 999px; font-size: .7rem;
  padding: 2px 8px; font-weight: 700;
}
.chat-scroll { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; }
.chat-bub {
  max-width: 85%; padding: 7px 10px; border-radius: 12px; font-size: .88rem;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .08); width: fit-content;
}
.chat-bub.recv { background: #fff; align-self: flex-start; border-top-left-radius: 4px; }
.chat-bub.sent { background: #dcf3d8; align-self: flex-end; border-top-right-radius: 4px; }
.chat-bub.unread { outline: 2px solid #b3261e33; background: #fff6f5; }
.chat-author { font-size: .72rem; font-weight: 700; color: var(--brand, #470054); }
.chat-meta { font-size: .68rem; color: var(--muted); text-align: right; margin-top: 2px; display: flex; gap: 6px; justify-content: flex-end; }
.chat-ticks { color: #9aa1ab; font-weight: 700; letter-spacing: -2px; }
.chat-ticks.read { color: #2f7fd1; }
.chat-box textarea { min-height: 38px; }

/* The floating order chat (Dr Daniel, 2026-09-05): a 💬 button pinned to
   the modal's bottom right, red unread badge, expanding to a floating box. */
.chat-fab {
  position: fixed; right: 26px; bottom: 22px; z-index: 60;
  width: 54px; height: 54px; border-radius: 50%; border: none;
  background: var(--brand, #470054); color: #fff; font-size: 1.45rem;
  box-shadow: 0 6px 18px rgba(71, 0, 84, .35); min-height: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
@media (hover: hover) { .chat-fab:hover { background: #5b1a6e; } }
.chat-fab-badge {
  position: absolute; top: -4px; right: -4px; background: #b3261e; color: #fff;
  border-radius: 999px; min-width: 20px; height: 20px; font-size: .7rem;
  font-weight: 800; display: flex; align-items: center; justify-content: center;
  padding: 0 5px; border: 2px solid #fff;
}
.chat-float {
  position: fixed; right: 26px; bottom: 22px; z-index: 220;
  border: 1.5px solid var(--line);
  width: min(400px, calc(100vw - 40px)); max-height: min(520px, calc(100vh - 60px));
  display: flex; flex-direction: column; background: #f7f4f9;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .28);
}
.chat-float .chat-scroll { max-height: none; min-height: 180px; }
/* The class's display:flex ties with [hidden]'s display:none on specificity
   and wins on order — restate it so the collapsed state actually collapses. */
.chat-float[hidden] { display: none; }

/* Factory-portal chat (2026-09-05, overlay retired the same day: it sat on
   top of the card's icons): the 💬 button lives INLINE in the card's button
   bar, and only the expanded box floats — fixed at the viewport corner,
   opaque and above everything. */
.chat-fab-inline .chat-fab-badge { top: -7px; right: -7px; }
/* The Order Form's title-row chat button wears the badge on its corner. */
.fo-msg-btn .chat-fab-badge { top: -8px; right: -8px; }

/* Clickable header chips (2026-09-05): they read as buttons on hover. */
.chip-door { cursor: pointer; }
@media (hover: hover) { .chip-door:hover { filter: brightness(.93); text-decoration: underline; } }

/* The allocation cell is one big button (2026-09-05). */
td.alloc-click { cursor: pointer; }
/* The item column as a click target (Dr Daniel, 2026-09-05): the whole cell
   opens the item's edit popup, so it hints like the allocation cell does. */
/* The whole row is the click target (Dr Daniel, 2026-09-05: "click anywhere
   in the item column, not just the description"). No hover wash — the
   pointer and the tooltip carry the hint (Dr Daniel: "No need purple
   highlight pls"). */
td.item-td-click { cursor: pointer; }
tr.row-click { cursor: pointer; }
@media (hover: hover) { td.alloc-click:hover .alloc-name { color: var(--brand, #470054); text-decoration: underline dotted; } }

/* The row's factory-message door (Dr Daniel, 2026-09-05): a proper round
   Messenger-blue button, clearly apart from the status badge. */
button.row-msg {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; min-height: 0; padding: 0; border: none; border-radius: 50%;
  background: #0084ff; color: #fff; font-size: 1.15rem;
  margin: 8px 0 0 2px; box-shadow: 0 2px 8px rgba(0, 132, 255, .35);
}
@media (hover: hover) { button.row-msg:hover { background: #006fd6; } }
button.row-msg .chat-fab-badge { top: -5px; right: -5px; }

/* The PIC's Note-to-Self pad (2026-09-05): ruled-paper feel, list of notes.
   Slimmed down 2026-09-06 ("make it smaller") — narrower, tighter, quieter. */
.self-notes {
  margin-top: 8px; background: #f7f7f5; border: 1px solid #e3e3de;
  border-radius: 10px; padding: 6px 10px 8px; max-width: 300px; font-size: .8rem;
}
.self-notes strong { font-size: .76rem; }
.self-notes input {
  background: transparent; border: none; border-bottom: 1.5px dashed #cfcfc8;
  border-radius: 0; font-size: .78rem; min-height: 0; padding: 3px 2px;
}
.self-notes button { min-height: 0; padding: 2px 8px; }
.self-note { border-bottom: 1px dashed #e0e0da; padding: 4px 2px; font-size: .8rem; }
.self-note:last-child { border-bottom: none; }

/* The Order Form's title-row chat button wears Messenger blue, matching
   the row icon (Dr Daniel, 2026-09-05). */
button.fo-msg-btn {
  background: #0084ff; color: #fff; border: none; font-weight: 700;
  border-radius: 999px; padding: 7px 16px;
}
@media (hover: hover) { button.fo-msg-btn:hover { background: #006fd6; color: #fff; } }

/* "All" buttons stand apart (Dr Daniel, 2026-09-05): brand-purple pill,
   filled when active, outlined when not. */
button.all-btn {
  border: 1.5px solid var(--brand, #470054); background: #fff;
  color: var(--brand, #470054); font-weight: 800; border-radius: 999px;
}
button.all-btn.on { background: var(--brand, #470054); color: #fff; }
@media (hover: hover) { button.all-btn:hover { background: #f2e7f6; } }
@media (hover: hover) { button.all-btn.on:hover { background: #5b1a6e; color: #fff; } }

/* Order numbers highlight and copy (2026-09-05) — the card head is a
   toggle, but the numbers themselves select like ordinary text. */
.fo-head .copyable { user-select: text; cursor: text; }
/* The suspended-preview panel a Revised Draft wears on the portal (Dr
   Daniel, 2026-09-07): draft-yellow, clearly a pause — not an error. */
.fo-amending {
  background: #fdf6df; border: 1px dashed #e5c878; border-radius: 10px;
  padding: 12px 14px; color: #7a5b12; font-size: .92rem;
}
.fo-amending strong { color: #b26a00; }
/* Superseded builds under the live preview (Dr Daniel, 2026-09-07):
   collapsed rows, red DO-NOT-PRODUCE wording, and the old drawing washes
   out so it can never be mistaken for the build in force. */
.fo-oldvers { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.fo-oldver { border: 1px dashed var(--line); border-radius: 10px; padding: 6px 10px; background: #faf8fb; }
.fo-oldver summary { cursor: pointer; font-size: .82rem; font-weight: 800; color: #99291f; }
.fo-oldver summary .muted { font-weight: 600; font-size: .78rem; }
.fo-oldver .fo-oldver-body { opacity: .55; margin-top: 6px; }
/* Flat undo in the PO popup (2026-09-05): plain muted text, no button box. */
button.flat-undo { background: none; border: none; box-shadow: none; color: var(--muted); font-weight: 600; }
@media (hover: hover) { button.flat-undo:hover { color: var(--brand, #470054); background: none; } }

/* The invoice's Order Form button (2026-09-05): the nav tab's icon on a
   light greyish-white pill. */
button.order-form-btn {
  background: #f5f5f2; border: 1.5px solid #e3e3de; color: var(--text);
  font-weight: 600; box-shadow: none; /* flat 2D (Dr Daniel, 2026-09-05) */
}
@media (hover: hover) { button.order-form-btn:hover { background: #ecece8; box-shadow: none; } }

/* Order Form rows hover greyish-white, not brand purple (Dr Daniel,
   2026-09-07: "Mouse hover purple highlight in orderform, change it to
   light grey … greyish white"). Draft rows keep their yellow wash and
   delivered rows their fade — only the purple goes. */
@media (hover: hover) { .fo-list tr.click:hover { background: #f5f5f4; } }
@media (hover: hover) { .fo-list tr.click:hover td { background: #f5f5f4; } }
@media (hover: hover) { .fo-list tr.row-draft.click:hover td { background: #f9edc7; } }

/* Invoice register — Summary sub-tab (Dr Daniel, 2026-09-07): the range's
   total, what was sold as a donut, and how the money arrived. Cards flow
   into one column on a narrow screen. */
.sum-wrap { display: flex; flex-direction: column; gap: 14px; }
.sum-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.sum-head h2 { margin: 0; font-size: 1.05rem; }
.sum-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; align-items: start; }
.sum-card h3 { margin: 0 0 8px; font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.sum-total { font-size: 1.9rem; font-weight: 800; color: var(--brand); line-height: 1.1; }
.sum-bars { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.sum-bar-row { display: grid; grid-template-columns: 1fr 2fr auto; gap: 8px; align-items: center; font-size: .82rem; }
.sum-bar-track { background: var(--line); border-radius: 999px; height: 10px; overflow: hidden; }
.sum-bar-fill { display: block; height: 100%; border-radius: 999px; }
.sum-bar-v { font-weight: 700; white-space: nowrap; }
.sum-donut-wrap { display: flex; justify-content: center; padding: 6px 0 12px; }
.sum-donut {
  width: 168px; height: 168px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.sum-donut-hole {
  width: 108px; height: 108px; border-radius: 50%; background: var(--card, #fff);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
}
.sum-donut-v { font-weight: 800; font-size: 1.05rem; }
.sum-donut-l { font-size: .72rem; color: var(--muted); }
.sum-legend { display: flex; flex-direction: column; gap: 6px; }
.sum-leg { display: grid; grid-template-columns: 12px 1fr auto; gap: 8px; align-items: center; font-size: .82rem; }
.sum-dot { width: 10px; height: 10px; border-radius: 50%; }
.sum-leg-l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sum-leg-v { font-weight: 700; white-space: nowrap; }

/* The WhatsApp customer-copy preview (Dr Daniel, 2026-09-07): the document
   on the left, the editable message on the right; one column on a narrow
   screen. */
.cc-preview { display: grid; grid-template-columns: 1.15fr 1fr; gap: 16px; align-items: start; }
.cc-preview > div > label { display: block; font-weight: 700; font-size: .82rem; margin-bottom: 6px; }
@media (max-width: 820px) { .cc-preview { grid-template-columns: 1fr; } }

/* Whose outreach map signed a partner — the register and QR cards colour
   the two apart (Dr Daniel, 2026-09-08: "put clement and daniel tan
   different color"). Deliberately NOT the status palette: a map is an
   origin, not a state. */
.badge.map-clement { background: #e7f0fd; color: #1c56ae; }
.badge.map-dtan { background: #fdefdd; color: #9a5b00; }

/* The Type/Map FILTER CHIPS wear identity colours (Dr Daniel, 2026-09-08:
   "distinct color for clement and daniel tan, and also distinct color for
   centre and personal"). Resting = soft tint; selected = solid fill of the
   SAME colour, so a picked chip keeps its identity instead of turning brand
   purple. The neutral chips (All / Both / Unclassified / Other) keep the
   brand treatment. Specificity matches .br-chip.on.tab-chip and these sit
   later in the sheet, so they win the tie. */
.br-chip.tab-chip.chip-clement { background: #e7f0fd; color: #1c56ae; border-color: #c8dcf7; }
.br-chip.on.tab-chip.chip-clement { background: #1c56ae; color: #fff; border-color: #1c56ae; }
.br-chip.tab-chip.chip-dtan { background: #fdefdd; color: #9a5b00; border-color: #f3ddb7; }
.br-chip.on.tab-chip.chip-dtan { background: #9a5b00; color: #fff; border-color: #9a5b00; }
.br-chip.tab-chip.chip-centre { background: #e0f2f1; color: #00695f; border-color: #bfe5e2; }
.br-chip.on.tab-chip.chip-centre { background: #00695f; color: #fff; border-color: #00695f; }
.br-chip.tab-chip.chip-personal { background: #fce7f0; color: #ad2a63; border-color: #f5c8db; }
.br-chip.on.tab-chip.chip-personal { background: #ad2a63; color: #fff; border-color: #ad2a63; }

/* ---- Production Detail Confirmation on paper (2026-09-06) --------------
   LAST in the sheet on purpose: these print sizes must outrank the screen
   .po-doc rules above, and with equal specificity the later rule wins.
   Sizes mirror the approved PDF's point values so the printed page IS the
   approved page; long orders continue cleanly onto page 2 rather than
   clipping (panels and section heads never split). */
@media print {
  /* Paper never animates: entry animations start at opacity 0, and a print
     snapshot taken at frame 0 (headless, or a very fast Ctrl+P) would ship
     a blank page. */
  *, *::before, *::after { animation: none !important; transition: none !important; }
  /* The browser prints its date/title header and URL footer INTO the page
     margins — a zero-margin named page on the WHOLE BODY leaves it nowhere
     to write and forces no break; the document carries its own margins. */
  body:has(.po-doc) { page: podoc; }
  @page podoc { margin: 0; }
  .po-doc { padding: 9mm 11mm 7mm; font-size: 10.5px; line-height: 1.32; }
  .po-doc .po-hd { margin-bottom: 7px; }
  .po-doc .po-logo { width: 110px; }
  .po-doc .po-hd-meta { font-size: 8.3px; gap: 2px; }
  .po-doc .po-hd-title { font-size: 8px; }
  .po-doc .po-hero { border-radius: 12px; padding: 13px 16px 10px; margin-bottom: 10px; }
  .po-doc .po-hero h1 { font-size: 23px; margin-bottom: 4px; }
  .po-doc .po-hero-hi { font-size: 12px; margin-bottom: 5px; }
  .po-doc .po-hero-sub { font-size: 9.5px; }
  .po-doc .po-sec-head { margin: 11px 0 5px; gap: 9px; break-after: avoid; }
  .po-doc .po-num { width: 24px; height: 24px; border-radius: 8px; font-size: 9px; }
  .po-doc .po-sec-head h2 { font-size: 13px; }
  .po-doc .po-panel { border-radius: 8px; padding: 8px 12px; }
  .po-doc .po-grid2 { gap: 7px 16px; }
  .po-doc .po-lab { font-size: 7.2px; }
  .po-doc .po-val { font-size: 10.5px; }
  .po-doc .po-size-val { font-size: 13px; }
  .po-doc .po-callout { padding: 1px 0; }
  .po-doc .po-crow { padding: 7px 12px; }
  .po-doc .po-callout-name { font-size: 14px; }
  .po-doc .po-callout-sub { font-size: 8.5px; }
  .po-doc .po-matt { gap: 12px; }
  .po-doc .po-matt-left { gap: 8px; }
  .po-doc .po-matt-right img { max-width: 190px; }
  .po-doc .po-fig-labs > div { font-size: 10px; }
  .po-doc .po-fig-labs strong { font-size: 7px; }
  .po-doc .fl-dot { width: 6px; height: 6px; }
  .po-doc .po-items-row { gap: 9px; margin-bottom: 7px; }
  .po-doc .po-items { padding: 7px 11px; border-radius: 8px; }
  .po-doc .po-items h4 { font-size: 7.2px; margin-bottom: 4px; }
  .po-doc .po-item { font-size: 9px; padding: 1.5px 0; }
  .po-doc .po-none { font-size: 8.5px; }
  .po-doc .po-cta { margin-top: 10px; padding: 8px 12px; border-radius: 8px; }
  .po-doc .po-cta h3 { font-size: 11px; margin-bottom: 2px; }
  .po-doc .po-cta p { font-size: 8.4px; }
  .po-doc .po-before { margin-top: 7px; }
  .po-doc .po-terms-p { font-size: 8.3px; margin-bottom: 0; }
  .po-doc .po-panel, .po-doc .po-items, .po-doc .po-items-row,
  .po-doc .po-matt, .po-doc .po-end, .po-doc .po-item { break-inside: avoid; }
  .po-doc .po-hero, .po-doc .po-num, .po-doc .po-panel, .po-doc .po-items,
  .po-doc .po-callout, .po-doc .po-cta, .po-doc .fl-dot {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}

/* Mochi assessment (Dr Daniel, 2026-09-09) */
/* The action bar freezes at the viewport foot so Save and the stage action
   are always in reach on a long assessment. */
.mochi-actbar {
  position: sticky; bottom: 0; z-index: 6;
  background: var(--bg);
  padding: 10px 0; margin-bottom: 0;
  border-top: 1px solid var(--line);
}
/* All four scents share one grid row; min-width 0 lets the long names wrap
   inside their cells instead of pushing a chip onto a second line. */
.aroma-row { display: grid; grid-template-columns: repeat(4, 1fr); }
.aroma-row button { min-width: 0; }

/* Topbar message bell (Dr Daniel, 2026-09-09) */
.bell-drop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 230px; background: var(--card); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(42, 31, 46, .18); padding: 4px 0 6px;
}
.bell-row {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  padding: 7px 12px; color: var(--text); text-decoration: none; font-size: .85rem;
}
@media (hover: hover) { .bell-row:hover { background: var(--brand-tint); } }
.bell-count {
  background: #b3261e; color: #fff; border-radius: 999px;
  min-width: 20px; text-align: center; font-size: .72rem; font-weight: 800; padding: 1px 6px;
}
.topbar .iconbtn { position: relative; }

/* ---- Pressure Lab (2026-09-10) ---------------------------------------- */
.pm-cols { display: grid; grid-template-columns: minmax(0, 1.75fr) minmax(280px, .85fr); gap: 14px; align-items: start; }
/* Save and Cancel are frozen along the foot (Dr Daniel, 2026-09-13): the row
   rides with the page instead of scrolling away with the header, and it is
   the same strip whether the fitting is a page of its own or the full-screen
   popup inside a case note. */
.pm-actionbar {
  position: sticky; bottom: 0; z-index: 6;
  display: flex; justify-content: flex-end; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 14px; padding: 10px 12px;
  background: color-mix(in srgb, var(--card) 94%, transparent);
  backdrop-filter: blur(6px);
  border-top: 1px solid var(--line);
  border-radius: 0 0 14px 14px;
}
.pm-actionbar > span { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
@media (max-width: 1100px) { .pm-cols { grid-template-columns: 1fr; } }
/* The mat gets the room (Dr Daniel, 2026-09-13: "make the pressure map
   bigger"): map and colour bar across the top, the readings in a row
   UNDERNEATH rather than in a column stealing 150px of width. */
.pm-stage { display: grid; grid-template-columns: minmax(0, 1fr) 78px; gap: 10px; align-items: start; }
@media (max-width: 900px) {
  .pm-stage { grid-template-columns: 1fr; }
  /* Stacked, the colour bar is an ordinary column again: its figures sit in
     the flow rather than hanging over the foot of the map above it. */
  .pm-vbar-out { position: static; transform: none; margin: 3px 0; }
}
.pm-map { display: flex; flex-direction: column; gap: 6px; align-items: stretch; min-width: 0; }
.pm-canvas { width: 100%; aspect-ratio: 9 / 4; height: auto; border-radius: 10px; border: 1px solid #e5e7eb; background: #f8f7fb; }
.pm-canvas-zoomable { cursor: zoom-in; }
@media (hover: hover) { .pm-canvas-zoomable:hover { border-color: var(--brand-tint-2, #c9a6d4); } }
/* The map on its own, as big as the screen will give it. */
.pm-zoom { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; }
.pm-zoomcanvas {
  width: 100%; height: 100%; object-fit: contain; max-height: 100%;
  border-radius: 12px; border: 1px solid #e5e7eb; background: #f8f7fb;
}
.pm-profile { width: 100%; height: auto; aspect-ratio: 1080 / 64; border-radius: 6px; border: 1px solid #e5e7eb; background: #fff; }
/* The colour bar is as tall as the map beside it and no taller (Dr Daniel,
   2026-09-14: "make it smaller so it fits within the pressure map preview").
   The strip takes whatever the row gives it rather than setting a floor of
   its own, and the two number boxes are the size of the figures in them, not
   the house's full 42px — that alone was 84px of the overhang. */
/* The column is the height of its own contents now, not of the row: the strip
   is set to the map's height in script and the two figure boxes stand outside
   it (Dr Daniel, 2026-09-14). The column is now the tallest thing in the row,
   so it starts level with the map and the lower box hangs below it; pulling the
   strip up to the map's own top edge would drive the upper box into the
   toolbar above. */
.pm-vbar {
  display: flex; flex-direction: column; align-items: center;
  align-self: start; position: relative;
}
/* mmHg and its two figures live outside the map's height, which is where Dr
   Daniel says they may be; inside it there is nothing but the gradient. */
.pm-vbar-out {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.pm-vbar-top { bottom: 100%; margin-bottom: 4px; }
.pm-vbar-bot { top: 100%; margin-top: 4px; }
.pm-vbar-body { display: flex; gap: 4px; align-items: stretch; flex: none; min-height: 118px; }
.pm-vbar-strip { width: 17px; border-radius: 6px; border: 1px solid #e5e7eb; }
.pm-vbar-ticks {
  display: flex; flex-direction: column; justify-content: space-between;
  font-size: .6rem; color: var(--muted); font-weight: 700; line-height: 1;
}
.pm-scale-in {
  width: 50px; padding: 2px 4px; min-height: 24px; font-size: .74rem;
  border-radius: 8px; text-align: center;
}
.pm-kpis {
  grid-column: 1 / -1; display: grid; gap: 8px; margin-top: 2px;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
}
.pm-kpi { padding: 8px 10px; }
.pm-kpi .v { font-size: 1.35rem; }
.pm-kpi.hot .v { color: #b91c1c; }
.pm-playback { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; background: #eef2ff; border: 1px solid #c7d2fe; border-radius: 8px; padding: 6px 10px; margin-bottom: 8px; }
.pm-layers { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-height: 34px; }
.pm-chip { display: inline-flex; align-items: center; gap: 4px; background: var(--brand-tint, #f3e8f7); color: var(--brand, #470054); border-radius: 999px; padding: 3px 8px; font-size: .82rem; }
.pm-chip .x { border: none; background: transparent; color: inherit; cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 2px; }
.pm-inline { display: inline-flex; align-items: center; gap: 4px; font-size: .85rem; }
/* The capture panel is no longer a card of its own: it is the right half of
   the working section, so it starts level with the mat and is divided from it
   by a hairline rather than by a gap and a second shadow (Dr Daniel,
   2026-09-13: "Make this 2 align, and combine into 1 section"). Stacked on a
   narrow screen the divider becomes a rule across the top instead. */
.pm-form { border-left: 1px solid var(--line); padding-left: 16px; }
/* Everything in this panel is built small (Dr Daniel, 2026-09-14: "the
   buttons in this component make it smaller, so this whole component take up
   lesser space for the pressure map") — the chips, the headings and the gaps
   between them. The mat beside it keeps the width it gains. */
.pm-form .pm-formhead { margin: 0 0 4px; font-size: 1rem; }
/* Whose half this panel is building, on its own line under the heading and in
   a different hand — lighter, wider, and in that side's colour, the same
   colour the person's chip and the chosen build wear (Dr Daniel, 2026-09-14:
   "make it at the second row, remove the brackets, use different font to
   distinguish it"). */
.pm-formwho {
  display: block; margin-top: 1px;
  font-size: .76rem; font-weight: 600; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted);
}
.pm-formwho[data-side="embroidery"] { color: #6d28d9; }
.pm-formwho[data-side="label"] { color: #0f766e; }
.pm-form label { font-size: .74rem; }
.pm-form .st-chips { grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 5px; margin: 2px 0 1px; }
.pm-form .st-chip {
  padding: 4px 8px; font-size: .73rem; min-height: 26px; border-radius: 8px; border-width: 1px;
}
.pm-form input, .pm-form textarea, .pm-form select { padding: 5px 9px; font-size: .82rem; }
.pm-form fieldset { padding: 7px 9px 8px; margin: 0 0 6px; }
.pm-form legend { font-size: .72rem; }
/* Remove belongs to the level and sits at its far right, on the line its name
   is written on (Dr Daniel, 2026-09-14: "on the right of the component, not
   directly next to the word Level 1"). It rides the fieldset's top border, so
   it is pinned rather than laid out. */
fieldset.pm-lev { position: relative; }
button.pm-levx {
  position: absolute; top: -18px; right: 6px;
  padding: 0 5px; min-height: 18px; line-height: 1; font-size: .78rem;
  background: #fff; box-shadow: none; border: none; color: var(--bad, #a3352c);
}
@media (hover: hover) { button.pm-levx:hover { background: var(--bad-bg, #fde8e6); border-radius: 6px; } }
/* Annotate and Void wear their icon and nothing else — no fill, no box: a
   border round a single glyph reads as a missing label. */
button.pm-capicon {
  background: transparent; border: none; box-shadow: none;
  padding: 2px 5px; font-size: .95rem; line-height: 1.1;
}
@media (hover: hover) { button.pm-capicon:hover {
  background: transparent; border: none; box-shadow: none;
  padding: 2px 5px; font-size: .95rem; line-height: 1.1;
} }
@media (hover: hover) { button.pm-capicon:hover { filter: brightness(.86); } }
.pm-capicons { display: flex; align-items: center; gap: 2px; margin-left: auto; }
/* One section per base (Dr Daniel, 2026-09-14): everything tried on the same
   base is together, and the base is named over it. */
.pm-basegroup + .pm-basegroup { margin-top: 16px; border-top: 1px solid var(--line); padding-top: 12px; }
.pm-baselabel { font-size: .82rem; color: var(--muted); margin: 0; white-space: nowrap; }
.pm-baselabel strong { color: var(--text); }
/* Save closes the captures card, hard against its right edge. */
.pm-saverow { margin: 12px 0 0; }
/* The whole capture chip opens it. */
.pm-cap-open { cursor: pointer; }
.pm-cap-open button, .pm-cap-open .pm-rename, .pm-cap-open input { cursor: auto; }
.pm-cap-open button { cursor: pointer; }
.pm-form .pm-buildhead { margin: 8px 0 4px; }
.pm-form > * + * { margin-top: 5px; }
@media (max-width: 1100px) {
  .pm-form { border-left: none; border-top: 1px solid var(--line); padding-left: 0; padding-top: 14px; }
}
/* Captures sit side by side along the row (Dr Daniel, 2026-09-13), so each
   one is a tile with its map on top and its figures under it, and three
   trials can be read against each other without scrolling. */
.pm-capgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 10px; align-items: start; }
.pm-cap { display: flex; flex-direction: column; gap: 8px; padding: 8px; border: 1px solid #e5e7eb; border-radius: 10px; background: #fff; }
.pm-cap.final { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-tint) inset; }
.pm-cap.voided { opacity: .55; }
.pm-cap img { width: 100%; aspect-ratio: 9 / 4; height: auto; border-radius: 8px; border: 1px solid #e5e7eb; cursor: zoom-in; object-fit: cover; }
.pm-cap-body { min-width: 0; }
.pm-stat-line { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: .84rem; margin: 3px 0; }
/* Rename a capture where you read it (2026-09-13). */
.pm-rename { cursor: text; border-bottom: 1px dashed transparent; }
@media (hover: hover) { .pm-rename:hover { border-bottom-color: var(--brand-tint-2, #c9a6d4); } }
.pm-rename-in { width: auto; min-width: 180px; max-width: 100%; padding: 2px 6px; font-size: .9rem; font-weight: 700; }
.pm-stat-line .hot { color: #b91c1c; }
/* A coded build is read letter by letter, so it is set in the same face the
   figures are and never squeezed. */
.pm-code { font-variant-numeric: tabular-nums; letter-spacing: .01em; font-weight: 700; }
.pm-profile-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.pm-profile-cards img { width: 100%; aspect-ratio: 2 / 1; border-radius: 8px; border: 1px solid #e5e7eb; object-fit: cover; cursor: zoom-in; }
.pm-profile-cards .cap { border: 1px solid #e5e7eb; border-radius: 10px; padding: 6px; background: #fff; font-size: .82rem; }
.pm-profile-cards .cap.final { border-color: #d9a441; box-shadow: 0 0 0 2px #fdf3df inset; }
/* Two finals that cannot be billed together are outlined in red, whatever
   else they are (Dr Daniel, 2026-09-14). */
.pm-profile-cards .cap.clash, .pm-profile-cards .cap.final.clash {
  border-color: #b91c1c; box-shadow: 0 0 0 2px #fdeaea inset;
}
.pm-clash { margin-top: 10px; }
/* The badge's display:flex used to beat the hidden attribute, so the bell
   wore a red 0 when there was nothing unread (Dr Daniel's 2026-09-10
   screenshot). */
.chat-fab-badge[hidden] { display: none !important; }

/* Report Centre (Dr Daniel, 2026-09-11) */
.rc-cat { font-size: .95rem; margin: 18px 0 8px; }
/* A report in sections (2026-10-20): each table its own titled card. */
.rc-section { margin-bottom: 10px; }
.rc-section-t { margin: 0 0 6px; font-size: .95rem; color: var(--brand); }
.rc-section-n { margin: 6px 0 0; font-size: .76rem; }
.rc-table tr.rc-total td { font-weight: 800; border-top: 2px solid var(--line); }
.rc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
/* Catalogue cards, redesigned 2026-09-17 ("Design the cards abit to make
   it nicer"): icon in a tinted chip, title beside it, the description in a
   quiet line UNDER them — the button base had been sitting title and
   description side by side. A gentle lift on hover instead of a repaint. */
.rc-card {
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  text-align: left; background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; padding: 14px 16px; cursor: pointer; box-shadow: none;
  min-height: 0; transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
@media (hover: hover) { .rc-card:hover {
  border-color: var(--brand-light); background: var(--card);
  box-shadow: 0 4px 14px rgba(71, 0, 84, .09); transform: translateY(-1px);
} }
.rc-card-head { display: flex; align-items: center; gap: 9px; min-width: 0; }
.rc-card-ic {
  width: 34px; height: 34px; border-radius: 10px; flex: 0 0 auto;
  background: var(--brand-tint); display: inline-flex;
  align-items: center; justify-content: center; font-size: 1.05rem;
}
.rc-card-t { font-weight: 800; color: var(--brand-dark); font-size: .92rem; line-height: 1.25; }
.rc-star { color: #b8860b; margin-left: auto; flex: 0 0 auto; }
.rc-card-d { color: var(--muted); font-size: .78rem; line-height: 1.45; }
.rc-flt { display: inline-flex; align-items: center; gap: 6px; font-size: .8rem; color: var(--muted); }
.rc-sums { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; margin-bottom: 10px; }
.rc-sum { padding: 10px 12px; }
.rc-sum-v { font-size: 1.15rem; font-weight: 800; color: var(--brand-dark); }
.rc-sum-l { color: var(--muted); font-size: .72rem; margin-top: 2px; }
.rc-table th { position: sticky; top: 0; background: var(--card); }
@media print {
  .sidebar, .topbar, .rc-controls, .foot { display: none !important; }
  .content { padding: 0 !important; }
  .card { box-shadow: none !important; border: none !important; }
}
.rc-cat-row td { background: var(--brand-tint-2); }

/* ---- a drill-down that reads as levels (Dr Daniel, 2026-09-23: "make it
   easier to see and diffentiate each categories and sections") --------------
   A breakdown opens as a CARD standing on a tinted band, under a line saying
   whose breakdown it is. Three levels therefore read as three things, where
   before they were one wash of lavender with the same table three times. */
.rc-drill-cell {
  /* The band deepens a step per level, so depth is visible without counting. */
  background: color-mix(in srgb, var(--brand-tint) calc(100% - var(--rc-depth, 0) * 22%), var(--brand-tint-2));
  padding: 10px 12px 12px calc(14px + var(--rc-depth, 0) * 10px);
  border-left: 3px solid var(--brand-light);
}
.rc-drill-head {
  display: flex; align-items: baseline; gap: 8px; margin: 0 0 7px 2px;
}
.rc-drill-of { font-weight: 800; color: var(--brand-dark); font-size: .84rem; }
.rc-drill-by { color: var(--muted); font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; }
.rc-drill > .rc-table,
.rc-drill table {
  background: var(--card); border-radius: 12px; overflow: hidden;
  box-shadow: 0 1px 2px rgba(71, 0, 84, .06);
  font-size: .82rem;
}
/* The nested header is a band of its own, not a repeat of the page header. */
.rc-drill th {
  position: static; background: var(--brand-tint-2); color: var(--brand-dark);
  font-size: .68rem; letter-spacing: .07em; text-transform: uppercase;
}
.rc-drill tbody tr:nth-child(odd) > td:not(.rc-drill-cell) { background: #fbf9fc; }
@media (hover: hover) { .rc-table tr.click:hover > td:not(.rc-drill-cell) { background: var(--brand-tint); } }
/* The chevron turns instead of the label changing shape. */
.rc-chev {
  display: inline-block; width: 14px; margin-right: 4px; color: var(--brand-light);
  transition: transform .12s ease; transform-origin: 45% 50%;
}
.rc-chev.on { transform: rotate(90deg); color: var(--brand); }
td.rc-drillable { font-weight: 700; }
/* A share reads as a bar with its number inside it. */
td.rc-sharecell { width: 108px; }
.rc-share {
  position: relative; display: block; min-width: 76px; padding: 2px 6px;
  border-radius: 999px; background: var(--brand-tint-2); overflow: hidden;
}
.rc-share i {
  position: absolute; left: 0; top: 0; bottom: 0; display: block;
  background: var(--brand-light); opacity: .38;
}
.rc-share b { position: relative; font-size: .74rem; font-weight: 800; color: var(--brand-dark); }
/* Distinct tones for measures shown side by side (Dr Daniel, 2026-10-06:
   "Change these 3 bar to 3 distant colors for easy viewing"). */
.rc-share.tone-green { background: #e2f3e8; }
.rc-share.tone-green i { background: #2e9e5b; opacity: .5; }
.rc-share.tone-green b { color: #1b5a35; }
.rc-share.tone-blue { background: #e1edfa; }
.rc-share.tone-blue i { background: #2f7fd1; opacity: .5; }
.rc-share.tone-blue b { color: #194777; }
.rc-share.tone-amber { background: #fcefd9; }
.rc-share.tone-amber i { background: #e08a14; opacity: .55; }
.rc-share.tone-amber b { color: #74450a; }

/* A payment opened out of the line above it: same columns, tinted, and
   tighter than a heading row so a long list stays compact
   (Dr Daniel, 2026-09-12: "Why there's so much gap in between?"). */
tr.pay-sub > td { background: var(--brand-tint); padding: 4px 10px; border-bottom: none; font-size: .82rem; }
tr.pay-sub + tr:not(.pay-sub) > td { border-top: 1px solid var(--line); }
td.pay-sub-inv { padding-left: 26px; }

/* ---- Daily Closing (Dr Daniel, 2026-09-12) ----
   The day's title and its two controls ride one frozen row under the topbar;
   the two actions ride a frozen bar at the foot. Neither goes out of reach on
   a long close. Same treatment as the Order Form's filter rows and the mochi
   action bar, bleeding across the .content padding so nothing peeks out at
   the corners as the page scrolls beneath them. */
.dc-head {
  position: sticky; top: 59px; z-index: 20;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--bg); margin: 0 -22px 10px; padding: 8px 22px;
  box-shadow: 0 8px 10px -10px rgba(71, 0, 84, .25);
}
.dc-actbar {
  position: sticky; bottom: 0; z-index: 6;
  /* Copy on the left of the pair, the close on the right, both hard right
     (Dr Daniel, 2026-09-12). */
  display: flex; justify-content: flex-end; align-items: center; gap: 8px;
  background: var(--bg); margin: 10px -22px 0; padding: 10px 22px;
  border-top: 1px solid var(--line);
}
.dc-actbar:empty { display: none; }
/* A card's own title row, with its action sitting on the same line. */
.card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.card-head h2 { margin: 0; }
/* What a part-paid bill still owes, said in words beside its payments
   (Dr Daniel, 2026-09-12). Red like the Balance line it echoes. */
.pay-due {
  font-size: .82rem; font-weight: 700; white-space: nowrap;
  background: var(--bad-bg); color: var(--bad); border-radius: 999px; padding: 3px 11px;
}
.pay-settled {
  font-size: .82rem; font-weight: 700; white-space: nowrap;
  background: var(--ok-bg); color: var(--ok); border-radius: 999px; padding: 3px 11px;
}
/* A payment that no longer counts — corrected or removed — with the Status
   column gone (Dr Daniel, 2026-09-12). The row steps back and its amount is
   struck through, so no cancelled figure can be read as money in hand. */
tr.pay-void td { color: var(--muted); }
tr.pay-void td.num { text-decoration: line-through; }
/* Proof of payment filed with the reference it belongs to (Dr Daniel,
   2026-09-12) — a quiet line, the filename doing the talking. */
/* One line (Giselle, 2026-10-02): the reference, then each file by its
   underlined name with the red bin beside it, then "+ Slip". */
.pay-refline { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; }
.pay-slip { display: inline-flex; align-items: center; gap: 2px; min-width: 0; max-width: 100%; }
.pay-slip a {
  font-size: .78rem; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-decoration: underline;
}
button.pay-slip-x { min-height: 22px; padding: 0 4px; }
button.pay-slip-x svg { width: 15px; height: 15px; }
.pay-slip-add {
  min-height: 0; padding: 2px 4px; background: none; box-shadow: none;
  color: var(--muted); font-size: .78rem; font-weight: 600;
}
@media (hover: hover) { .pay-slip-add:hover { background: none; box-shadow: none; color: var(--brand); text-decoration: underline; } }
.pay-slip-add:active { transform: none; }
/* Edit and remove side by side, tight to the right edge. */
td.pay-acts { width: 1%; white-space: nowrap; }
td.pay-acts > button { padding: 4px 6px; }
/* The Payments table aligns its Amount heading and figures LEFT, like its
   other columns (Giselle, 2026-10-02). */
table th.pay-amt, table td.pay-amt { text-align: left; }
@media (max-width: 900px) {
  .dc-head { margin: 0 -14px 10px; padding: 8px 14px; }
  .dc-actbar { margin: 10px -14px 0; padding: 10px 14px; }
}

/* A class that sets `display` beats the browser's own [hidden] rule, so an
   element hidden from script stayed on screen as an empty box — the
   playback bar and the custom-size row both did (2026-09-13). */
[hidden] { display: none !important; }

/* The build panel: the configurator's chips, and a heading that reads as one */
.pm-buildhead { margin: 10px 0 2px; font-size: .95rem; }
.pm-addlabel { font-size: .74rem; color: var(--muted); font-weight: 700; }
.pm-details { margin-top: 8px; }
.pm-details > summary { cursor: pointer; font-size: .82rem; }
.pm-details[open] > summary { margin-bottom: 6px; }

/* ---- Pressure Lab: who the mattress is for (2026-09-13) --------------- */
.pm-kinds { display: flex; gap: 10px; flex-wrap: wrap; }
.pm-kind {
  display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 10px 14px; border-radius: 12px; border: 1px solid #e5e7eb;
  background: #fff; color: inherit; cursor: pointer; min-width: 210px;
}
@media (hover: hover) { .pm-kind:hover:not([disabled]) { border-color: var(--brand-tint-2, #c9a6d4); } }
.pm-kind.on { border-color: var(--brand, #470054); box-shadow: 0 0 0 2px var(--brand-tint, #f3e8f7) inset; }
.pm-kind[disabled] { opacity: .55; cursor: default; }
.pm-kind-ic { font-size: 1.5rem; line-height: 1; }
.pm-kind strong { display: block; font-size: .95rem; }
.pm-kind small { display: block; color: var(--muted); font-size: .74rem; }
.pm-sides { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.pm-sides[hidden] { display: none; }
.pm-side {
  flex: 1 1 220px; display: flex; flex-direction: column; gap: 1px; text-align: left;
  padding: 8px 12px; border-radius: 10px; border: 1px solid #e5e7eb;
  border-left: 5px solid #d8dbe2; background: #fff; color: inherit; cursor: pointer;
}
/* The app-wide `button:hover` paints brand purple, which swallowed the side's
   own colour and turned the teal half violet on hover (Dr Daniel,
   2026-09-13). Each side hovers in a pale wash of ITS colour, and an active
   tab does not change at all — there is nothing to preview. */
@media (hover: hover) { .pm-side:hover { border-color: var(--brand-tint-2, #c9a6d4); box-shadow: none; } }
@media (hover: hover) { .pm-side[data-side="embroidery"]:hover { background: #f8f4fe; } }
@media (hover: hover) { .pm-side[data-side="label"]:hover { background: #f0faf8; } }
@media (hover: hover) { .pm-side[data-side="embroidery"].on:hover { background: #f3ecfe; } }
@media (hover: hover) { .pm-side[data-side="label"].on:hover { background: #e4f6f3; } }
.pm-side.on { border-left-color: var(--brand, #470054); background: var(--brand-tint, #f3e8f7); }
.pm-side strong { font-size: .84rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
/* A colour per person (Dr Daniel, 2026-09-13: "Make 2 distinct colors for the
   2 sides") — violet for the embroidery half, teal for the label half, and
   the same two carry onto the capture list and every capture card, so a
   picture always says whose side it came from. Colour is never the only
   signal: the side is written out beside it. */
.pm-side[data-side="embroidery"] { border-left-color: #ddd0f7; }
.pm-side[data-side="embroidery"] strong { color: #6d28d9; }
.pm-side[data-side="embroidery"].on { border-left-color: #7c3aed; background: #f3ecfe; }
.pm-side[data-side="label"] { border-left-color: #bfe9e2; }
.pm-side[data-side="label"] strong { color: #0f766e; }
.pm-side[data-side="label"].on { border-left-color: #0d9488; background: #e4f6f3; }
.pm-sidetag[data-side="embroidery"] { color: #6d28d9; }
.pm-sidetag[data-side="label"] { color: #0f766e; }
/* The side's colour marks the build that was CHOSEN for that half, and
   nothing else (Dr Daniel, 2026-09-14). Every chip in the list already belongs
   to the person named above it, so a stripe down all of them said nothing; on
   the one final chip it says which half this build is for. The rest keep the
   plain hairline. */
.pm-cap.final[data-side="embroidery"] {
  border: 1px solid #7c3aed; border-left: 5px solid #7c3aed;
  box-shadow: 0 0 0 2px #f3ecfe inset;
}
.pm-cap.final[data-side="label"] {
  border: 1px solid #0d9488; border-left: 5px solid #0d9488;
  box-shadow: 0 0 0 2px #e4f6f3 inset;
}
.pm-side-who { font-size: 1rem; font-weight: 800; }
/* The register's filters share the title's row, so each takes only the width
   it needs (Dr Daniel, 2026-09-14). */
.pm-reghead h2 { margin: 0; }
.pm-reghead input[type="text"], .pm-reghead input:not([type]) { width: 210px; }
.pm-reghead select { max-width: 150px; }
/* Date and clinician sit at the right end of the fitting's own line, each
   label in front of its control so the row stays one line tall (Dr Daniel,
   2026-09-14). On a narrow screen the toolbar wraps them as a pair. */
/* Date and clinician are one group: they travel together, and when the row is
   tight it is the phone number that gives way, not the pair. */
.pm-headfields { display: flex; align-items: center; gap: 10px; flex: none; flex-wrap: nowrap; }
.pm-headphone { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pm-headfield { display: flex; align-items: center; gap: 6px; }
.pm-headfield select { max-width: 168px; }
.pm-headfield input { width: 132px; }
/* When the row gets tight the phone number goes before the pair does: it is
   on the customer's own page, and the date and the clinician are what this
   screen is about (Dr Daniel, 2026-09-14: "clinician same grid row as the
   date"). */
@media (max-width: 1330px) {
  .pm-headphone { display: none; }
  .pm-headfields { gap: 8px; }
  .pm-headfield select { max-width: 140px; }
  .pm-headfield input { width: 116px; }
  .pm-headfield label { font-size: .68rem; }
}
.pm-headfield label { margin: 0; font-size: .72rem; color: var(--muted); white-space: nowrap; }
.pm-headfield input, .pm-headfield select {
  min-height: 30px; padding: 3px 9px; font-size: .82rem; border-radius: 9px;
}
/* The mat settings box (Dr Daniel, 2026-09-14): the things you set once and
   then forget, in sections, each control on a line of its own rather than
   three dropdowns shoulder to shoulder in a toolbar. */
.pm-settings { display: flex; flex-direction: column; gap: 16px; }
.pm-set-sect { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.pm-set-sect > select, .pm-set-sect > button { width: 100%; }
.pm-set-sect > button { justify-content: center; }
.pm-set-note { margin: 0; font-size: .8rem; }
/* The frozen row that names whose half is on the mat (Dr Daniel, 2026-09-14).
   It sits between the header card and the working card, so it takes the top
   of the screen the moment the two side cards scroll away — and the other
   person's chip on it switches the page to that side. It slides UNDER the
   topbar (z-index 7 against the topbar's 10); in a popup there is no topbar,
   so it rides at the very top. */
/* Whose half is on the mat, at the left end of the frozen foot beside Save
   and Cancel (Dr Daniel, 2026-09-14: "relocate the buttons to the left of the
   bottom row, same row as save and billing button"). No frame of its own — it
   is part of that bar. */
.pm-stick { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-right: auto; }
.pm-stick[hidden] { display: none; }
/* Colour means CHOSEN, and nothing else (Dr Daniel, 2026-09-14: "the
   not-clicked one shall have no colour. When click then the colour shall
   appear"). The one you are not building is plain grey on white; the one you
   are is filled in its own colour, white type, its name in full. */
.pm-stickchip {
  display: inline-flex; align-items: baseline; gap: 7px; min-width: 0;
  border: 1px solid var(--line); background: var(--card); color: var(--muted);
  border-radius: 999px; padding: 5px 13px; font: inherit; cursor: pointer;
  transition: background .12s, color .12s, border-color .12s;
}
.pm-stickchip .pm-stick-side {
  font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
  color: inherit;
}
.pm-stickchip strong { font-size: .95rem; color: inherit; }
.pm-stick-n { font-size: .72rem; color: inherit; opacity: .78; }
@media (hover: hover) { .pm-stickchip:not(.on):hover { border-color: var(--brand-tint-2, #c9a6d4); color: var(--ink, #1f2937); } }
.pm-stickchip.on { cursor: default; color: #fff; border-color: transparent; }
/* Flip sits with the two people it swaps, in the foot bar — with no fill of
   its own (Dr Daniel, 2026-09-14: "flip sides remove the background colour"),
   because on this bar a filled chip means "this is the half you are building"
   and nothing else. */
/* Bands, Values and Raw bytes are ways of LOOKING at the map, not states, so
   they carry no fill (Dr Daniel, 2026-09-14: "bands & values buttons remove
   the background colour"). Raw bytes goes with them: one tinted button in a
   row of three plain ones reads like an accident. */
/* No outline either (Dr Daniel, 2026-09-14): on this bar a box round a
   button reads as a state, and these are only ways of LOOKING at the map.
   The colour of the words carries the hover and the chosen one. */
button.pm-viewbtn, button.pm-plain {
  background: transparent; border: none; color: var(--muted); box-shadow: none;
}
@media (hover: hover) { button.pm-viewbtn:hover, button.pm-plain:hover {
  background: transparent; border: none; color: var(--muted); box-shadow: none;
} }
@media (hover: hover) { button.pm-viewbtn:hover, button.pm-plain:hover { color: var(--brand); } }
button.pm-viewbtn.active, button.pm-plain.active { color: var(--brand); font-weight: 700; }
/* button.pm-flip, not .pm-flip: the house ghost button is written
   button.ghost, which outweighs a bare class and would keep its tint. */
button.pm-flip {
  background: transparent; border: none; border-radius: 999px;
  color: var(--muted); box-shadow: none;
}
@media (hover: hover) { button.pm-flip:hover {
  background: transparent; border: none; border-radius: 999px;
  color: var(--muted); box-shadow: none;
} }
@media (hover: hover) { button.pm-flip:hover { color: var(--brand); } }
/* The capture box: the map across the top, its two readings side by side
   under it, and the build's code up in the title row (Dr Daniel, 2026-09-14). */
.pm-capshot { margin-bottom: 12px; }
.pm-capstats { align-items: start; }
.modal-head .pm-headcode { flex: none; margin-left: auto; color: var(--muted); font-size: .86rem; }
/* Freeze in the title row of the full-page map. */
.modal-head .pm-zoomfreeze { flex: none; margin-left: auto; }
.pm-stickchip[data-side="embroidery"].on { background: #7c3aed; box-shadow: 0 2px 8px rgba(124, 58, 237, .35); }
.pm-stickchip[data-side="label"].on { background: #0d9488; box-shadow: 0 2px 8px rgba(13, 148, 136, .35); }
/* On a phone the row keeps one line: the two chips say whose side they are
   without the word in front of them, and the capture count goes — the row is
   there to name the person, and it is eating the top of an 800px screen. */
@media (max-width: 560px) {
  .pm-stick { gap: 6px; padding: 6px 10px; }
  .pm-stick-lead, .pm-stick .pm-stick-n { display: none; }
  .pm-stickchip { flex: 1 1 0; justify-content: center; padding: 5px 8px; }
}
.pm-sizes { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 10px; }
.pm-size {
  padding: 7px 14px; border-radius: 999px; border: 1px solid #e5e7eb;
  background: #fff; color: inherit; cursor: pointer; font-weight: 600;
}
.pm-size.on { background: var(--brand, #470054); color: #fff; border-color: var(--brand, #470054); }
/* The fitting as it reads back on a case note */
.note-pressure { border: 1px solid #e5e7eb; border-radius: 10px; padding: 10px; margin-bottom: 10px; background: #fff; }
.note-pressure-maps { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; }
.note-pressure-maps figure { margin: 0; }
.note-pressure-maps img {
  width: 100%; aspect-ratio: 2 / 1; object-fit: cover; cursor: zoom-in;
  border-radius: 8px; border: 1px solid #e5e7eb;
}
.note-pressure-maps figcaption { font-size: .8rem; margin-top: 2px; }

/* Upsell lines (2026-09-17): stage chips and their remark share one grid
   row; the remark takes what width the chips leave and wraps under them on
   a phone. */
.cn-chipline { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
/* A picked product and its bought-tick, flexing as one item so the remark
   sits BESIDE it on the same grid row (Dr Daniel, 2026-09-23: "I wan the
   remark to be at the same grid row as the title"). */
.cn-chipline > .cn-growwrap { flex: 1 1 190px; min-width: 0; }
.cn-chipline .cn-chips { display: contents; }
.cn-chipline .cn-chipline-remark { flex: 1 1 140px; min-width: 120px; }
/* The word "Remark" wears the label track's own dress — same size, same
   grey (Dr Daniel, 2026-09-17: "the word remark pls make it same font and
   size and colour as the label track"). What is TYPED stays answer-sized. */
.cn-chipline-remark::placeholder { font-size: .8rem; color: var(--muted); opacity: 1; }
/* Type-first-then-chips rows (2026-09-17, third pass): now that the chips
   are a fixed right rail, the typed line and the bolster picker GROW to
   fill everything up to it (Dr Daniel: "Bolster chip, topper and mattress
   line make it full and stop at right before the educated button"). */
.cn-chipline .cn-stagetext { flex: 1 1 150px; min-width: 150px; width: auto; }
.cn-chipline .cn-opts { flex: 1 1 auto; min-width: 0; }
/* Inside the picker the chips are a real flex box again — the chipline's
   display:contents would otherwise stop the dashed button stretching. */
.cn-chipline .cn-opts .cn-chips { display: flex; }
/* The automatic "they bought it" tick (2026-09-17): a green badge at the
   right end of a Top selection row, lit from the customer's issued bills. */
.cn-bought { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; flex: 0 0 auto;
  background: #16a34a; color: #fff; }
.cn-bought svg { width: 12px; height: 12px; }
.cn-bought[hidden] { display: none; }
.cn-chipline .cn-bought { margin-left: auto; }
/* On the read-only card the tick rides at the end of the answer text. */
dd > .cn-bought { margin-left: 8px; vertical-align: -4px; }
/* What was actually bought, under a ticked Topper / Mattress row
   (2026-09-26: "remark what combination they purchased"). */
.cn-boughtwhat { display: block; flex-basis: 100%; font-size: .78rem; line-height: 1.3; margin-top: 2px; color: #15803d; }
/* The tick and its day, on the item's own row (Giselle, 2026-09-30: "make
   it same grid row as the item"). */
.cn-boughtin { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; white-space: nowrap; }
.cn-boughtin[hidden] { display: none; }
.cn-boughtin .cn-bought { margin-left: 0; }
.cn-boughtday { font-size: .76rem; font-weight: 600; color: #15803d; }
/* The Bolster picker keeps its own width, so the tick, the remark and the
   stage chips still fit on its row. */
.cn-chipline > .cn-opts:has(+ .cn-boughtin) { flex: 0 1 auto; }
/* A lit tick takes ~120px; the remark on that row gives way rather than
   pushing the stage chips onto a second line. */
.cn-chipline:has(> .cn-boughtin:not([hidden])) .cn-chipline-remark { flex-basis: 60px; min-width: 60px; }
dd > .cn-boughtday { margin-left: 6px; }
/* A billed item is locked, but still reads as the answer, not greyed out. */
.cn-pick.cn-billed:disabled { opacity: 1; cursor: default; }
.cn-line.cn-billed:disabled { border-bottom-style: solid; cursor: default; }
/* "Why no buy?" under a row the customer was told about and did not buy
   (Giselle, 2026-09-30): a soft red panel while the reason is missing, grey
   once written. The quick reasons fill the box; the box is what is saved. */
/* Tidied the same day ("Messy leh fix this"): the reason is a card line,
   the quick reasons are the small suggestion chips and only show while the
   reason is owed; a written reason is one quiet line with no panel. */
/* Why no buy (redesigned 2026-10-10, Giselle: "A bit crowded"): a soft amber
   box like the signed card's, the label on its own line, a full-width white
   box to type in, the quick reasons spaced out underneath. Once written it
   reads as the label and the reason. */
.cn-why { background: #faeeda; border-radius: 10px; padding: 10px 14px 12px; margin: 6px 0 10px; }
.cn-why.done { padding: 8px 14px 10px; }
.cn-why-top { display: block; }
.cn-why-lab { display: block; font-size: .78rem; font-weight: 700; color: #854f0b; margin-bottom: 6px; white-space: normal; }
.cn-why-need { font-weight: 400; color: #ba7517; }
/* "Need CS follow up on this item?" beside the reason (Giselle, 2026-11-10). */
.cn-why-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cn-why-row > input.cn-why-input { flex: 1 1 260px; width: auto !important; }
.cn-cs { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; padding: 4px 6px 4px 10px; border-radius: 10px; }
.cn-cs.owed { background: #fff; box-shadow: 0 0 0 1px #ef9f27 inset; }
.cn-cs-q { font-size: .8rem; font-weight: 600; color: #854f0b; white-space: nowrap; }
.cn-cs-btns { display: inline-flex; gap: 6px; }
.cn-cs-btn { min-height: 32px; padding: 4px 14px; border-radius: 999px; border: 1px solid #ef9f27; background: #fff;
  color: #854f0b; font-weight: 600; font-size: .85rem; cursor: pointer; }
.cn-cs-btn.yes.on { background: #2e7d4f; border-color: #2e7d4f; color: #fff; }
.cn-cs-btn.no.on { background: #6b6b6b; border-color: #6b6b6b; color: #fff; }
.note-whynobuy-cs { font-weight: 600; }
.cn-why.done .cn-why-lab { margin-bottom: 2px; }
.cn-why input.cn-why-input { display: block; width: 100%; box-sizing: border-box; min-height: 38px; padding: 8px 10px;
  background: #fff; border: 1px solid #fac775; border-radius: 8px; font-size: .88rem; color: #412402; }
.cn-why input.cn-why-input:focus { border-color: #ef9f27; box-shadow: 0 0 0 2px rgba(239, 159, 39, .25); }
.cn-why input.cn-why-input::placeholder { color: #ba7517; opacity: .8; }
.cn-why.done input.cn-why-input:not(:focus) { background: transparent; border-color: transparent; padding: 0; min-height: 0; box-shadow: none; }
.cn-why-quick { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.cn-why-quick button.cn-sugg { border: 1px solid #ef9f27; color: #854f0b; background: #fff; padding: 4px 14px; }
@media (hover: hover) { .cn-why-quick button.cn-sugg:hover { background: #fac775; border-color: #ba7517; color: #412402; } }
/* On the signed card: the reason under its product's line. */
.note-whynobuy { display: block; font-size: .82rem; line-height: 1.55; color: #412402; background: #faeeda; border-radius: 8px; padding: 6px 12px 8px; margin-top: 6px; font-weight: 400; }
.note-whynobuy-lab { display: block; font-size: .7rem; font-weight: 700; color: #854f0b; letter-spacing: .02em; margin-bottom: 1px; }
/* The rule between what WE did and where THEY stand (2026-09-17:
   "Educated, gave sample pack | Interested, not interested"). */
.cn-chipsep { width: 1.5px; height: 22px; border-radius: 1px;
  background: var(--line); flex: 0 0 auto; margin: 0 3px; }
/* The stage chips form one right-aligned rail down the Top selection
   (Dr Daniel, 2026-09-17: "option chips align to the right of the grid
   row"). The cluster is its own flex box — out of display:contents — so
   it can take the auto margin and still wrap inside itself on a phone. */
.cn-chipline .cn-chips.cn-stagechips { display: flex; flex-wrap: wrap;
  align-items: center; gap: 6px; margin-left: 12px; justify-content: flex-end; }
/* The bought tick then sits right beside the rail, not across a gulf. */
.cn-chipline .cn-stagechips ~ .cn-bought { margin-left: 8px; }
/* The bed-sinking answers (2026-09-17, third pass: "make the buttons
   smaller? 'Pt claimed' 'we observed'. Both need remarks"): two stacked
   lines, each a printed label at a FIXED width (so the ✅/❌ pairs line up
   between the two), a small chip pair, and that answer's own remark
   filling the rest of the line. */
.cn-sinkrow { display: flex; flex-direction: column; gap: 4px; }
.cn-sinkline { display: flex; align-items: center; gap: 6px; min-width: 0; }
.cn-sinkline .cn-chipline-remark { flex: 1 1 60px; min-width: 60px; }
.cn-sinkgrp { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
/* No reserved label width — the marks sit right after the words ("the gap
   between subtitle and answer why so far away? Pls minimise it"), and
   "Pt claimed" / "We observed" are near enough the same length that the
   pairs still read as a column. */
.cn-sinklab { font-size: .76rem; font-weight: 600; color: var(--muted); flex: 0 0 auto; }
.cn-sinkgrp .cn-chip { padding: 1px 6px; font-size: .7rem; }
/* The ❌ keeps its red, drawn at the SAME size as the check (2026-09-17:
   "why x chip bigger than the tick?"). */
.cn-sinkgrp .cn-chip.cn-sinkno { color: #a8322a; }
.cn-sinkgrp .cn-chip.cn-sinkno.on { background: #d9584f; border-color: #d9584f; color: #fff; }
/* Rows without a flex line (the pillow picker, 2nd/3rd choice) keep their
   full width; the tick floats at the row's right end instead, and only a
   LIT tick reserves its lane — so it never covers the picker's ✕ clear. */
.cn-boughtwrap { position: relative; display: block; }
.cn-boughtwrap:has(> .cn-bought:not([hidden])) { padding-right: 26px; }
.cn-boughtwrap > .cn-bought { position: absolute; right: 0; top: 50%;
  transform: translateY(-50%); }

/* ---- touch devices (2026-09-26) ---- */
/* No grey flash on tap, and no press-nudge where there is no mouse — the
   tap-hover-active-repaint sequence read as flicker on iPad and phones. */
button, .btn, .pickbtn, a, [role="button"], .chip, tr.click, .iconbtn {
  -webkit-tap-highlight-color: transparent;
}
@media (hover: none) {
  button:active, .btn:active { transform: none; }
}

/* ---- Issue invoice popup (Giselle, 2026-09-30, final spec) ----
   Compact and centred: total, payment rows (method + amount side by side,
   a quiet 50% toggle inside the amount, card-machine chips), balance left,
   "More options", Cancel / Issue invoice. Built in invoice.js issuePopup. */
.modal.ivx { max-width: 480px; padding: 0 20px 16px; }
.modal.ivx:focus { outline: none; }
.modal.ivx .modal-head.ivx-head { border-bottom: none; box-shadow: none; margin-bottom: 2px; padding: 16px 0 6px; align-items: flex-start; }
.ivx-titles { flex: 1; min-width: 0; }
.modal.ivx .ivx-head h2 { font-size: 1.15rem; }
.ivx-sub { font-size: .82rem; color: var(--muted); margin-top: 2px; }
.ivx-line { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 4px 0; font-size: .9rem; color: var(--muted); }
.ivx-line strong { color: var(--text); font-size: 1rem; font-variant-numeric: tabular-nums; }
.ivx-line.small { font-size: .82rem; padding-top: 0; font-variant-numeric: tabular-nums; }
.ivx-info { font-size: .8rem; color: var(--muted); margin: 2px 0 4px; }
.ivx-pays { margin-top: 6px; border-top: 1px solid var(--line); }
.ivx-pay { padding: 12px 0 4px; }
.ivx-pay + .ivx-pay { border-top: 1px dashed var(--line); }
.ivx-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 10px; }
.ivx-label { display: block; font-size: .78rem; font-weight: 700; color: var(--muted); margin: 0 0 4px; }
.modal.ivx select, .modal.ivx input { min-height: 40px; padding: 8px 12px; }
.modal.ivx select { padding-right: 30px; }
/* The 50% toggle lives inside the amount box; the box keeps room for it so
   it never sits on the figures. */
.ivx-amt { position: relative; }
.modal.ivx .ivx-amt input { padding-right: 62px; font-variant-numeric: tabular-nums; }
.ivx-half {
  position: absolute; right: 2px; top: 2px; bottom: 2px; min-width: 52px; min-height: 0;
  padding: 0 8px; border: none; border-radius: 10px; background: none; box-shadow: none;
  color: var(--muted); font-size: 12px; font-weight: 600; gap: 2px; white-space: nowrap;
}
@media (hover: hover) { .ivx-half:hover { background: none; box-shadow: none; color: var(--brand); } }
.ivx-half:active { transform: none; }
.ivx-half[aria-pressed="true"] { color: var(--brand); font-weight: 700; }
.ivx-half:focus-visible { outline: 2px solid var(--brand-light); outline-offset: -2px; }
.ivx-rownote { font-size: .78rem; color: var(--brand); font-weight: 600; margin-top: 6px; }
.ivx-extra { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: flex-end; }
.ivx-machine, .ivx-months { margin-top: 10px; }
.ivx-machine[hidden], .ivx-months[hidden], .ivx-rownote[hidden], .ivx-error[hidden], .ivx-more-body[hidden], .ivx-link[hidden] { display: none; }
.ivx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ivx-chip {
  min-height: 34px; padding: 4px 12px; border-radius: 999px; font-size: .82rem; font-weight: 600;
  background: var(--card); color: var(--text); border: 1.5px solid var(--line); box-shadow: none;
}
@media (hover: hover) { .ivx-chip:hover { background: var(--brand-tint); color: var(--brand); box-shadow: none; } }
.ivx-chip[aria-checked="true"] { background: var(--brand-tint); color: var(--brand); border-color: var(--brand); }
.modal.ivx .ivx-months select { width: auto; min-width: 130px; }
.ivx-link {
  background: none; border: none; box-shadow: none; min-height: 0; padding: 8px 0 4px;
  color: var(--brand); font-weight: 700; font-size: .86rem; border-radius: 6px;
}
@media (hover: hover) { .ivx-link:hover { background: none; box-shadow: none; text-decoration: underline; } }
.ivx-link:active { transform: none; }
/* Remove payment: a red ✕ in its own column after the amount. */
.ivx-grid.has-x { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 32px; }
.ivx-x {
  align-self: end; width: 32px; height: 40px; min-height: 0; padding: 0; border: none; border-radius: 10px;
  background: none; box-shadow: none; color: var(--bad); font-size: 1rem; font-weight: 700;
}
@media (hover: hover) { .ivx-x:hover { background: var(--bad-bg); color: var(--bad); box-shadow: none; } }
.ivx-x:active { transform: none; }
.ivx-x[hidden] { display: none; }
.ivx-balance { margin-top: 8px; padding: 10px 0 2px; border-top: 1.5px solid var(--line); }
.ivx-balmain { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.ivx-balmain span { font-weight: 700; font-size: .92rem; }
.ivx-balmain strong { font-size: 1.1rem; font-variant-numeric: tabular-nums; }
.ivx-balance.ok strong { color: var(--ok); }
.ivx-balance.bad .ivx-balmain span, .ivx-balance.bad strong { color: var(--bad); }
.ivx-balhelp { font-size: .78rem; color: var(--muted); text-align: right; margin-top: 2px; }
.ivx-balhelp.warn { color: var(--bad); font-weight: 600; }
.ivx-toggle { margin-top: 6px; }
.ivx-chev { display: inline-block; width: 1.1em; font-size: .8em; }
.ivx-hint { color: var(--muted); font-weight: 600; }
.ivx-field { margin-top: 8px; }
.ivx-help { font-size: .76rem; color: var(--muted); margin-top: 4px; }
.modal.ivx .ivx-date { max-width: 200px; }
.ivx-error { margin-top: 10px; padding: 8px 10px; border-radius: 8px; background: var(--bad-bg); color: var(--bad); font-size: .86rem; font-weight: 600; }
.modal.ivx .actions.ivx-foot { border-top: none; margin-top: 14px; padding-top: 0; }
.ivx-go { background: var(--brand); color: #fff; }
.ivx-go[aria-busy="true"] { opacity: .75; cursor: progress; }
@media (max-width: 560px) {
  .modal-backdrop.ivx-backdrop { padding: 16px; }
  .modal.ivx { padding: 0 16px 14px; }
  .modal.ivx .actions.ivx-foot > button { flex: 1; }
}

/* "Which pillow is <name>'s?" — asked when one family bill carries more
   than one pillow or bolster (Giselle, 2026-10-04). One quiet row per item. */
.cn-whose { display: flex; flex-direction: column; gap: 8px; }
.cn-whose-lead { margin: 0 0 4px; font-size: .88rem; }
.cn-whose-opt {
  display: flex; align-items: center; gap: 10px; width: 100%; justify-content: flex-start;
  padding: 10px 14px; border-radius: 12px; border: 1.5px solid var(--line);
  background: var(--card); color: var(--text); box-shadow: none; font-weight: 600; text-align: left;
}
@media (hover: hover) { .cn-whose-opt:hover { background: var(--brand-tint); border-color: var(--brand-light); color: var(--brand); box-shadow: none; } }
.cn-whose-name { flex: 1; min-width: 0; }
.cn-whose-day { font-size: .76rem; font-weight: 600; color: #15803d; white-space: nowrap; }
.cn-whose-none {
  align-self: flex-start; min-height: 0; padding: 6px 2px; background: none; box-shadow: none;
  color: var(--muted); font-size: .84rem; font-weight: 600;
}
@media (hover: hover) { .cn-whose-none:hover { background: none; box-shadow: none; color: var(--brand); text-decoration: underline; } }
/* Tick one or more (2026-10-04: one person can take two different pillows). */
.cn-whose-box {
  flex: none; width: 20px; height: 20px; border-radius: 6px; border: 1.5px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center; font-size: .8rem; color: #fff;
}
.cn-whose-opt[aria-pressed="true"] { background: var(--brand-tint); border-color: var(--brand); color: var(--brand); }
.cn-whose-opt[aria-pressed="true"] .cn-whose-box { background: var(--brand); border-color: var(--brand); }
.cn-whose-err { font-size: .82rem; font-weight: 600; color: var(--bad); }
.cn-whose-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 6px; }
.cn-whose-done { background: var(--brand); color: #fff; }
/* "+" beside a picked pillow or bolster adds another to the same row. */
.cn-chip.cn-addmore { min-width: 30px; font-weight: 700; font-size: 1rem; padding: 2px 8px; }
/* Top selection tidy (Giselle, 2026-10-04: "so messy"): the picked name on
   ONE line, cut with "…" (whole name on hover); "+" and "✕" small round
   marks centred on the row rather than tall boxes; a ticked Bolster keeps
   its picker short so the Educated / Interested chips stay on its line. */
.cn-pick .cn-pick-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cn-chipline .cn-opts .cn-chips { align-items: center; flex-wrap: nowrap; }
.cn-chipline .cn-opts .cn-chip.clear, .cn-chipline .cn-opts .cn-chip.cn-addmore {
  flex: none; align-self: center; width: 30px; height: 30px; min-width: 0; min-height: 0;
  padding: 0; border-radius: 50%; border: none; background: none; box-shadow: none; color: var(--muted);
}
.cn-chipline .cn-opts .cn-chip.cn-addmore { font-size: 1.05rem; font-weight: 700; }
@media (hover: hover) {
  .cn-chipline .cn-opts .cn-chip.clear:hover:not(:disabled) { background: var(--bad-bg); color: var(--bad); border: none; }
  .cn-chipline .cn-opts .cn-chip.cn-addmore:hover:not(:disabled) { background: var(--brand-tint); color: var(--brand); border: none; }
}
.cn-chipline > .cn-opts:has(+ .cn-boughtin:not([hidden])) { max-width: 300px; }
/* Topper / Mattress rows get the same small "+" and "✕" as the pillow
   (Giselle, 2026-10-04), right after the typed line. */
.cn-rowbtns { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.cn-rowbtns:empty { display: none; }
.cn-rowbtns .cn-chip.clear, .cn-rowbtns .cn-chip.cn-addmore {
  flex: none; width: 30px; height: 30px; min-width: 0; min-height: 0;
  padding: 0; border-radius: 50%; border: none; background: none; box-shadow: none; color: var(--muted);
}
.cn-rowbtns .cn-chip.cn-addmore { font-size: 1.05rem; font-weight: 700; }
@media (hover: hover) {
  .cn-rowbtns .cn-chip.clear:hover:not(:disabled) { background: var(--bad-bg); color: var(--bad); border: none; }
  .cn-rowbtns .cn-chip.cn-addmore:hover:not(:disabled) { background: var(--brand-tint); color: var(--brand); border: none; }
}
/* Top selection columns (Giselle, 2026-10-04, third pass: "the + and x make
   it narrower and close to each other. Purchased also can make it smaller
   ... the educated etc buttons to be same grid row as the respective item").
   Only the PRODUCT rows — Pillow, Bolster, Topper, Mattress and their 2 / 3
   rows — share the columns, sized by the case-note box (container query),
   not the window: the item (34% of the box, 160–300px, the same on
   every row) with "+" and ✕ tight at its right end, the green tick and the
   day only ("✓ 4 Oct", 2026-10-04; its space kept even when not bought), the remark filling the middle, the
   stage chips at the right of the SAME line. A box too narrow for that puts
   the chips under the item; a phone wraps as before. */
.cn-topgrid { container-type: inline-size; }
.cn-topgrid .cn-chip.cn-addmore, .cn-topgrid .cn-chip.clear { width: 24px !important; height: 24px !important; }
.cn-topgrid .cn-opts .cn-chip.cn-addmore + .cn-chip.clear { margin-left: -4px; }
.cn-topgrid .cn-rowbtns { gap: 2px; }
.cn-topgrid .cn-boughtin { gap: 4px; }
.cn-topgrid .cn-boughtin .cn-bought { width: 16px; height: 16px; }
.cn-topgrid .cn-boughtin .cn-bought svg { width: 11px; height: 11px; }
.cn-topgrid .cn-boughtday { font-size: .74rem; }
.cn-topgrid .cn-pick { padding: 7px 10px; gap: 6px; }
/* The stage chips on these rows a touch more compact, so a ~836px box fits
   item, tick, remark and chips on one line. */
.cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips { gap: 4px; }
.cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips .cn-chip { padding: 4px 9px; font-size: .76rem; min-height: 28px; }
.cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips .cn-chipsep { margin: 0 1px; height: 18px; }
@container (min-width: 600px) {
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) { flex-wrap: wrap; row-gap: 6px; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-growwrap, .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-opts { flex: 0 0 clamp(160px, 34cqi, 300px); width: clamp(160px, 34cqi, 300px); max-width: none; min-width: 0; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-growwrap > .cn-opts { width: 100%; max-width: none; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagetext { flex: 0 0 calc(clamp(160px, 34cqi, 300px) - 56px); width: calc(clamp(160px, 34cqi, 300px) - 56px); min-width: 0; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-rowbtns { display: inline-flex; flex: 0 0 50px; width: 50px; justify-content: flex-end; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-boughtin { flex: 0 0 60px; min-width: 0; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-boughtin[hidden] { display: inline-flex !important; visibility: hidden; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-chipline-remark { flex: 1 1 50px; min-width: 50px; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips { flex: 1 0 100%; margin-left: 0; justify-content: flex-start; flex-wrap: wrap; }
}
@container (min-width: 800px) {
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) { flex-wrap: nowrap; }
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips { flex: none; margin-left: 12px; justify-content: flex-end; flex-wrap: nowrap; }
}

/* Walk away (Giselle, 2026-10-06): the branch's walk-aways for the day, under
   the form, so a second tap on the same person is seen at once. */
.wa-list { margin-top: 10px; }
.wa-list-head { font-size: .78rem; font-weight: 700; color: var(--muted); margin-bottom: 4px; }
.wa-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-top: 1px solid var(--line); font-size: .86rem; }
.wa-row .wa-when { font-variant-numeric: tabular-nums; color: var(--muted); min-width: 44px; }
.wa-row .wa-who { font-weight: 600; flex: 1; min-width: 0; }
.wa-row.void .wa-who { text-decoration: line-through; color: var(--muted); }

/* ---- Walk-aways on the calendar + report (Giselle, 2026-10-06) ---- */
.wa-badge {
  display: inline-flex; align-items: center; gap: 3px; margin-left: 8px; vertical-align: middle;
  padding: 0 2px; border-radius: 0; border: none;
  background: none; color: inherit;
  font-size: .68rem; font-weight: 700; line-height: 17px; cursor: pointer; opacity: .6;
  min-height: 0; height: 19px; box-shadow: none; flex: none;
}
/* Plain icon + number, no outline or fill (Giselle, 2026-10-06). */
.wa-badge.has { opacity: 1; }
@media (hover: hover) { .wa-badge:hover { opacity: 1; } }
.cal .hd.hd-corner { display: flex; align-items: center; justify-content: center; padding: 0; }
.cal .hd.hd-corner .wa-badge { margin-left: 0; }
/* ALL view: the count sits in the column's top-left corner, the mirror of
   "n bookings" top-right (Giselle, 2026-10-06). */
.cal-branches .hd.br-hd .wa-badge { position: absolute; top: 5px; left: 8px; margin-left: 0; }
.wa-rbar { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px; margin: 0 0 12px; }
.wa-table-wrap { overflow-x: auto; max-height: 55vh; overflow-y: auto; border: 1px solid var(--line); border-radius: 10px; }
.wa-table { width: 100%; border-collapse: collapse; font-size: .82rem; }
.wa-table th { position: sticky; top: 0; background: var(--card, #fff); text-align: left; font-size: .74rem; color: var(--muted); padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.wa-table td { padding: 6px 8px; border-top: 1px solid var(--line); vertical-align: top; white-space: nowrap; }
.wa-table td.wa-td-remark { white-space: normal; min-width: 180px; }
.wa-table tr.void td { color: var(--muted); text-decoration: line-through; }

/* The bought build gets its own full line under the row, even where the row
   is otherwise held to one line (Giselle, 2026-10-10: "why the words
   squeezing there?"). */
.cn-topgrid .cn-chipline > .cn-boughtwhat { flex: 0 0 100%; order: 99; min-width: 0; }
@container (min-width: 800px) {
  .cn-topgrid .cn-chipline:has(> .cn-boughtwhat) { flex-wrap: wrap; row-gap: 2px; }
}

/* Case note head on a phone, tidied (Giselle, 2026-10-19: "Sleep
   consultation, general note, outcall case note, testing account that area
   very messy in mobile version"). The note types become ONE compact
   segmented bar - three equal parts in a single row, the chosen one white -
   and the people on the visit sit in one scrollable row of smaller chips. */
@media (max-width: 700px) {
  .cn-headtools .cn-typebar {
    flex-wrap: nowrap; gap: 3px; padding: 3px; border-radius: 12px; background: #f1ebf4;
  }
  .cn-headtools .cn-typebar button,
  .cn-headtools .cn-typebar button.off {
    flex: 1 1 0; min-height: 38px; padding: 4px 6px; border-radius: 9px; border: 0; box-shadow: none;
    font-size: .76rem; line-height: 1.15; white-space: normal; background: transparent; color: var(--muted);
  }
  .cn-headtools .cn-typebar button:not(.off) {
    background: #fff; color: var(--brand, #470054); font-weight: 800; box-shadow: 0 1px 3px rgba(60, 20, 70, .14);
  }
  .modal .modal-head .st-chips {
    flex-wrap: nowrap !important; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px;
  }
  .modal .modal-head .st-chips::-webkit-scrollbar { display: none; }
  .modal .modal-head .st-chips .st-chip {
    flex: none; min-height: 36px; padding: 4px 12px; font-size: .85rem; max-width: 78vw; overflow: hidden; text-overflow: ellipsis;
  }
}

/* Top selection on a phone, tidied (Giselle, 2026-10-19: "Mobile version so
   messy"). Each item reads top to bottom on its own: the label with air
   above it and a hairline between items; the item box (pillow / bolster
   picker, or the typed topper / mattress, drawn as the same box) on its own
   line with its + / x and tick; then the chips
   as ONE even row - three equal parts for Educated | Interested | Not
   interested, two pairs for the Maizisleepz / Pillow mist / Goatly rows; the
   remark last, full width. */
@container (max-width: 599px) {
  .cn-topgrid > .cn-sheetlab { padding-top: 12px; margin-top: 10px; border-top: 1px solid var(--line);
    font-weight: 700; color: var(--text); font-size: .82rem; }
  .cn-topgrid > .cn-sheetlab:first-child { padding-top: 0; margin-top: 0; border-top: 0; }
  .cn-topgrid > .cn-chipline { flex-wrap: wrap; row-gap: 8px; column-gap: 6px; margin-top: 6px; }
  .cn-topgrid > .cn-chipline > .cn-growwrap,
  .cn-topgrid > .cn-chipline > .cn-opts,
  .cn-topgrid > .cn-chipline > .cn-stagetext { flex: 1 1 0; min-width: 0; width: auto; max-width: none; }
  .cn-topgrid > .cn-chipline > .cn-stagetext {
    min-height: 44px; padding: 8px 12px; border: 1.5px dashed var(--line); border-radius: 12px; background: var(--card);
  }
  .cn-topgrid > .cn-chipline > .cn-chipline-remark { flex: 1 0 100%; min-width: 0; order: 5; }
  .cn-topgrid > .cn-chipline > .cn-boughtin[hidden] { display: none !important; }
  .cn-topgrid > .cn-chipline > .cn-chips {
    display: flex; flex: 1 0 100%; order: 4; margin: 0; gap: 4px; flex-wrap: nowrap; justify-content: stretch;
  }
  .cn-topgrid > .cn-chipline > .cn-chips .cn-chipsep { display: none; }
  .cn-topgrid > .cn-chipline > .cn-chips .cn-chip,
  .cn-topgrid > .cn-chipline:has(> .cn-growwrap, > .cn-opts, > .cn-stagetext) > .cn-stagechips .cn-chip {
    flex: 1 1 0; min-width: 0; min-height: 40px; padding: 4px 4px; font-size: .74rem; line-height: 1.15;
    border-radius: 10px; text-align: center; white-space: normal;
  }
  /* The "what we did" chips and the "where they stand" chips told apart by
     a wider gap instead of the rule. */
  .cn-topgrid > .cn-chipline > .cn-chips .cn-chipsep + .cn-chip { margin-left: 6px; }
}
/* The four-chip rows as two pairs: what we did (Educated, Gave sample
   pack) over where they stand (Interested, Not interested) - one row of
   four squeezed "Gave sample pack" onto three lines. */
@container (max-width: 599px) {
  .cn-topgrid > .cn-chipline > .cn-chips:not(.cn-stagechips) { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
  .cn-topgrid > .cn-chipline > .cn-chips:not(.cn-stagechips) .cn-chipsep + .cn-chip { margin-left: 0; }
  .cn-topgrid > .cn-chipline > .cn-chips:not(.cn-stagechips) .cn-chip { white-space: nowrap; font-size: .78rem; }
}

/* ---- New appointment form, redesigned (Giselle, 2026-10-19) -------------
   Everything below is scoped to .modal.apx - the appointment / block form
   only. A purple head with the customer in it, three soft purpose cards,
   Branch + a searchable Clinician dropdown, Date · Time · Duration · Pax on
   one row, short Remarks, a quiet secondary action and one purple button. */
.modal.apx { max-width: 700px; padding: 0 22px 18px; }
.modal.apx .modal-head {
  margin: 0 -22px 14px; padding: 16px 22px 16px; border-bottom: 0;
  background: linear-gradient(120deg, #4d1469 0%, #6a2484 55%, #8b3c9c 100%);
  color: #fff; flex-wrap: wrap; align-items: flex-start; gap: 4px 10px;
  border-radius: 18px 18px 0 0;
}
.modal.apx .apx-titles { flex: 1 1 auto; min-width: 0; }
.modal.apx .apx-eyebrow { font-size: .66rem; font-weight: 700; letter-spacing: .12em; opacity: .75; }
.modal.apx .modal-head h2 { color: #fff; margin: 1px 0 0; font-size: 1.2rem; }
.modal.apx .modal-x { color: #fff; background: rgba(255, 255, 255, .14); border: 0; box-shadow: none; }
@media (hover: hover) { .modal.apx .modal-x:hover { background: rgba(255, 255, 255, .26); color: #fff; } }

/* customer, inside the head */
.modal.apx .apx-cust { flex: 1 0 100%; position: relative; margin-top: 8px; }
.modal.apx .apx-hlab { display: block; font-size: .7rem; font-weight: 600; opacity: .8; margin-bottom: 3px; color: #fff; }
.modal.apx .apx-cust-row { display: flex; gap: 8px; align-items: stretch; }
.modal.apx .apx-cust-box {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px;
  background: #fff; border-radius: 10px; padding: 0 10px; color: #7a6c80;
}
.modal.apx .apx-cust-box svg { width: 16px; height: 16px; flex: none; }
.modal.apx .apx-cust-box input, .modal.apx .apx-cust-row > input {
  border: 0; box-shadow: none; background: transparent; min-height: 40px; padding: 0; width: 100%; color: var(--text);
}
.modal.apx .apx-cust-row > input { background: #fff; border-radius: 10px; padding: 0 12px; }
.modal.apx .apx-cust-new {
  flex: none; border: 1px solid rgba(255, 255, 255, .55); background: rgba(255, 255, 255, .12); color: #fff;
  border-radius: 10px; padding: 0 14px; min-height: 40px; font-weight: 700; box-shadow: none;
}
@media (hover: hover) { .modal.apx .apx-cust-new:hover { background: rgba(255, 255, 255, .24); color: #fff; } }
.modal.apx .apx-cust-res {
  position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 30;
  background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: 6px;
  box-shadow: 0 10px 28px rgba(42, 31, 46, .18); max-height: 300px; overflow-y: auto; color: var(--text);
}
.modal.apx .apx-cust-res .cust-pick { width: 100%; margin: 0 0 4px; }
.modal.apx .apx-cust-none { margin: 6px 4px; font-size: .8rem; color: var(--muted); }
.modal.apx .apx-cust-sel {
  display: flex; align-items: center; gap: 10px; background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .35); border-radius: 12px; padding: 7px 8px 7px 8px;
}
.modal.apx .apx-cust-sel .cust-face { background: #fff; color: #4d1469; }
.modal.apx .apx-cust-name { flex: 1 1 auto; min-width: 0; }
.modal.apx .apx-cust-name strong { display: block; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.modal.apx .apx-cust-name .apx-hlab { margin: 0; }
.modal.apx .apx-cust-change {
  flex: none; background: #fff; color: #4d1469; border: 0; border-radius: 8px; padding: 6px 12px;
  min-height: 32px; font-weight: 700; font-size: .8rem; box-shadow: none;
}

/* labels and fields */
.modal.apx .apx-lab { display: block; font-size: .76rem; font-weight: 700; color: var(--text); margin: 0 0 5px; }
.modal.apx .apx-lab-row { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; }
.modal.apx .apx-lab-row .apx-lab { margin-bottom: 5px; }
.modal.apx .apx-hint { font-size: .72rem; color: var(--muted); }
.modal.apx .apx-body > * + * { margin-top: 14px; }
.modal.apx .apx-body input:not([type=search]), .modal.apx .apx-body select, .modal.apx .apx-body textarea {
  border: 1px solid #ddd3e2; border-radius: 10px; box-shadow: none; width: 100%; min-height: 40px; margin: 0;
}
.modal.apx .apx-body textarea { min-height: 0; resize: vertical; }
.modal.apx .apx-body input:focus-visible, .modal.apx .apx-body select:focus-visible,
.modal.apx .apx-body textarea:focus-visible, .modal.apx button:focus-visible, .modal.apx [role=option].act {
  outline: 2px solid #8b3c9c; outline-offset: 1px;
}

/* purpose cards */
.modal.apx .apx-cats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.modal.apx .apx-cat {
  position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 82px; padding: 10px 8px 9px; border-radius: 14px; border: 1.5px solid transparent;
  box-shadow: none; color: var(--text); font-weight: 700; font-size: .84rem; text-align: center;
}
.modal.apx .apx-cat.tone-lav { background: #f1eafa; }
.modal.apx .apx-cat.tone-blue { background: #e7f0fb; }
.modal.apx .apx-cat.tone-peach { background: #fdeee3; }
.modal.apx .apx-cat-emo { font-size: 1.55rem; line-height: 1; }
.modal.apx .apx-cat-chev { position: absolute; top: 6px; right: 9px; font-size: .7rem; color: var(--muted); }
.modal.apx .apx-cat.open { border-color: #8b3c9c; border-style: dashed; }
.modal.apx .apx-cat.has-sel { border-color: #6a2484; border-style: solid; }
.modal.apx .apx-cat-sel {
  font-size: .64rem; font-weight: 700; color: #fff; background: #6a2484; border-radius: 999px; padding: 1px 7px;
}
@media (hover: hover) { .modal.apx .apx-cat:hover { filter: brightness(.97); color: var(--text); } }
.modal.apx .apx-opts {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; padding: 10px; border-radius: 12px;
}
.modal.apx .apx-opts.tone-lav { background: #f7f2fc; }
.modal.apx .apx-opts.tone-blue { background: #f1f6fd; }
.modal.apx .apx-opts.tone-peach { background: #fef5ee; }
.modal.apx .apx-opt {
  display: inline-flex; align-items: center; gap: 7px; min-height: 38px; padding: 6px 12px;
  background: #fff; border: 1px solid #ddd3e2; border-radius: 999px; box-shadow: none;
  color: var(--text); font-weight: 600; font-size: .84rem;
}
.modal.apx .apx-opt.on { background: #4d1469; border-color: #4d1469; color: #fff; }
.modal.apx .apx-opt.walk { border-style: dashed; }
.modal.apx .apx-opt-tick { font-weight: 800; }
@media (hover: hover) { .modal.apx .apx-opt:not(.on):hover { border-color: #8b3c9c; color: var(--text); background: #fff; } }
.modal.apx .apx-emo { font-size: 1.05rem; line-height: 1; }
.modal.apx .apx-sum {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 8px; padding: 5px 12px 5px 10px;
  background: #f5ecf7; border-radius: 999px; font-size: .84rem;
}
.modal.apx .apx-sum-k { color: var(--muted); font-weight: 600; }
.modal.apx .apx-sum-v { font-weight: 700; }
.modal.apx .apx-sum-note { color: var(--muted); font-size: .76rem; }

/* the pillow mark: a small plump pillow, pale blue and white */
.modal.apx .ap-pillow-icon {
  display: inline-block; position: relative; flex: 0 0 28px; width: 28px; height: 21px; box-sizing: border-box;
  border: 1.3px solid #8da4bc; border-radius: 28% 22% 26% 23%;
  background: linear-gradient(155deg, #ffffff 35%, #d8e8f5 100%);
  box-shadow: 0 2px 2px #37567422, inset 0 -2px 2px #b6cde566; transform: rotate(-8deg);
}
.modal.apx .ap-pillow-icon::before {
  content: ""; position: absolute; left: 2px; top: 2px; width: 4px; height: 4px;
  border-left: 1px solid #a7bed1; border-top: 1px solid #a7bed1; border-radius: 1px; transform: rotate(180deg);
}
.modal.apx .ap-pillow-icon::after {
  content: ""; position: absolute; right: 2px; bottom: 2px; width: 4px; height: 4px;
  border-right: 1px solid #a7bed1; border-bottom: 1px solid #a7bed1; border-radius: 1px; transform: rotate(180deg);
}

/* Branch | Clinician */
.modal.apx .apx-who { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; align-items: start; }
.modal.apx .apx-branches { margin: 0; flex-wrap: wrap; }
.modal.apx .apx-dd { position: relative; }
.modal.apx .apx-dd-btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 40px; padding: 0 12px; background: #fff; border: 1px solid #ddd3e2; border-radius: 10px;
  box-shadow: none; color: var(--text); font-weight: 600; text-align: left;
}
.modal.apx .apx-dd-btn[aria-expanded=true] { border-color: #8b3c9c; }
.modal.apx .apx-dd-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.modal.apx .apx-dd-val.ph { color: var(--muted); font-weight: 500; }
.modal.apx .apx-dd-chev { color: var(--muted); font-size: .8rem; flex: none; }
.modal.apx .apx-dd-pop {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 40; background: #fff;
  border: 1px solid #ddd3e2; border-radius: 12px; box-shadow: 0 12px 30px rgba(42, 31, 46, .18); overflow: hidden;
}
.modal.apx .apx-dd-pop.up { top: auto; bottom: calc(100% + 4px); }
.modal.apx .apx-dd-search { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-bottom: 1px solid var(--line); color: var(--muted); }
.modal.apx .apx-dd-search svg { width: 15px; height: 15px; flex: none; }
.modal.apx .apx-dd-search input { border: 0; box-shadow: none; padding: 4px 0; min-height: 30px; width: 100%; background: transparent; }
.modal.apx .apx-dd-search input:focus-visible { outline: none; }
.modal.apx .apx-dd-list { max-height: 260px; overflow-y: auto; padding: 4px; }
.modal.apx .apx-dd-head { font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); padding: 8px 8px 4px; }
.modal.apx .apx-dd-opt {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 8px; cursor: pointer; font-size: .88rem;
}
.modal.apx .apx-dd-opt .apx-dd-nm { flex: 1 1 auto; min-width: 0; }
.modal.apx .apx-dd-opt.act { background: #f5ecf7; outline-offset: -2px; }
.modal.apx .apx-dd-opt.on { background: #ede0f1; font-weight: 700; }
@media (hover: hover) { .modal.apx .apx-dd-opt:hover { background: #f5ecf7; } }
.modal.apx .apx-dd-tick { color: #4d1469; font-weight: 800; }
.modal.apx .apx-dd-empty { padding: 14px 10px; color: var(--muted); font-size: .85rem; text-align: center; }
.modal.apx .apx-badge { flex: none; font-size: .68rem; font-weight: 700; border-radius: 999px; padding: 2px 8px; }
.modal.apx .apx-badge.ok { background: #e3f4e8; color: #1a6b36; }
.modal.apx .apx-badge.warn { background: #fdf0dc; color: #8a5200; }
.modal.apx .apx-note:empty { display: none; }
.modal.apx .apx-note { margin-top: 6px; font-size: .78rem; color: var(--muted); }
.modal.apx .apx-note.warn { color: #8a5200; background: #fdf3e3; border-radius: 8px; padding: 6px 9px; }

/* Date | Time | Duration | Pax */
.modal.apx .apx-when { display: grid; grid-template-columns: 1.45fr 1fr 1fr .7fr; gap: 10px; }
.modal.apx .apx-when.three { grid-template-columns: 1.45fr 1fr 1fr; }
.modal.apx .apx-when .apx-f { min-width: 0; }
.modal.apx .apx-paxhint { margin: 6px 0 0; font-size: .78rem; color: var(--muted); }
.modal.apx .apx-paxhint:empty { display: none; }

/* footer */
.modal.apx .apx-err { color: var(--bad); background: var(--bad-bg); border-radius: 10px; padding: 8px 12px; font-size: .84rem; }
.modal.apx .apx-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  border-top: 1px solid var(--line); padding-top: 14px; margin-top: 16px;
}
.modal.apx .apx-alt {
  background: none; border: 0; box-shadow: none; color: var(--muted); font-weight: 600; padding: 8px 4px;
  text-decoration: underline; text-underline-offset: 3px; min-height: 0;
}
@media (hover: hover) { .modal.apx .apx-alt:hover { color: var(--text); background: none; } }
.modal.apx .apx-save {
  background: linear-gradient(120deg, #4d1469, #8b3c9c); color: #fff; border: 0; border-radius: 12px;
  padding: 0 22px; min-height: 44px; font-weight: 700; box-shadow: none;
}
.modal.apx .apx-save:disabled { opacity: .7; cursor: progress; }
@media (hover: hover) { .modal.apx .apx-save:hover:not(:disabled) { filter: brightness(1.08); color: #fff; } }

/* phones */
@media (max-width: 560px) {
  .modal.apx { padding: 0 14px 14px; }
  .modal.apx .modal-head { margin: 0 -14px 12px; padding: 14px; }
  .modal.apx .apx-cats { gap: 6px; }
  .modal.apx .apx-cat { min-height: 74px; font-size: .76rem; padding: 10px 4px 8px; }
  .modal.apx .apx-cat-emo { font-size: 1.35rem; }
  .modal.apx .apx-who { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .modal.apx .apx-when { grid-template-columns: 1fr 1fr; }
  .modal.apx .apx-when.three { grid-template-columns: 1fr 1fr; }
  .modal.apx .apx-when.three > :first-child { grid-column: 1 / -1; }
  /* The options as even tiles (Giselle, 2026-10-20: "pillow, topper mattress
     there a bit messy"): three in a row, icon over the word, like the cards
     above them; Other visits two to a row, icon beside the word. A slimmer
     one-line version was tried and she preferred these ("previous version
     better"). */
  .modal.apx .apx-opts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 8px; }
  .modal.apx .apx-opt {
    position: relative; flex-direction: column; justify-content: center; gap: 5px;
    min-height: 66px; padding: 8px 4px; border-radius: 12px; font-size: .8rem; text-align: center;
  }
  .modal.apx .apx-opt .apx-emo { font-size: 1.3rem; }
  /* Stacked, the pillow keeps its own 28 x 21 shape (its flex basis would
     otherwise become its height and square it). */
  .modal.apx .apx-opt .ap-pillow-icon { flex: none; margin: 3px 0; }
  .modal.apx .apx-opt-tick { position: absolute; top: 4px; right: 7px; font-size: .75rem; }
  .modal.apx .apx-opts.tone-peach { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .modal.apx .apx-opts.tone-peach .apx-opt {
    flex-direction: row; justify-content: flex-start; min-height: 46px; padding: 6px 10px; text-align: left; line-height: 1.2;
  }
  .modal.apx .apx-opts.tone-peach .apx-opt .apx-emo { font-size: 1.1rem; }
  /* The list opens in place on a phone, so every name stays reachable. */
  .modal.apx .apx-dd-pop { position: static; margin-top: 6px; box-shadow: none; }
  .modal.apx .apx-foot { flex-direction: column-reverse; align-items: stretch; }
  .modal.apx .apx-save { width: 100%; }
}

/* While any popup is open, the calendar's frozen toolbar (110) and frozen
   branch header (100) drop beneath the popup layer (50). They were painting
   over the top of every popup on the calendar page - the title, and now the
   appointment form's purple head (seen 2026-10-19). Closed, nothing changes. */
body:has(.modal-backdrop) .cal-head3.cal-head-flex,
body:has(.modal-backdrop) .cal-hdrow { z-index: 40; }

/* The appointment form on an iPhone (Giselle, 2026-10-20: "Why so messy?").
   Two iPhone habits broke it: Safari zooms the whole page in when a box
   under 16px is tapped (ours were 15px), leaving the form oversized and cut
   off on the right; and its date box keeps a wide native size of its own,
   pushing Time past the edge. Boxes are 16px on phones, the date box is
   drawn plainly at the column's width, and no column may grow past its
   share. */
@media (max-width: 560px) {
  .modal.apx input, .modal.apx select, .modal.apx textarea { font-size: 16px; }
  .modal.apx .apx-when { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .modal.apx .apx-when.three { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .modal.apx input[type=date] {
    -webkit-appearance: none; appearance: none; display: block;
    min-width: 0; max-width: 100%; width: 100%; height: 40px; text-align: left; line-height: 38px; padding: 0 12px;
  }
  .modal.apx input[type=date]::-webkit-date-and-time-value { text-align: left; }
  .modal.apx .apx-when select { height: 40px; }
  .modal.apx .apx-clin .apx-hint { display: none; }
  .modal.apx .apx-cust-row { min-width: 0; }
}

/* ---- Appointment details popup, redesigned (Giselle, 2026-10-20) --------
   Scoped to .modal.apd - the popup that opens on an existing booking. A
   plain white head (small APPOINTMENT label, name, phone, branch, status
   dropdown + Mark arrived), Visit details in lavender beside Previous
   purchases, a slim audit row, and pill buttons along the foot. */
.modal.apd { max-width: 780px; padding: 0 22px 16px; background: #fffdfe; }
.modal.apd .modal-head { border-bottom: 0; padding: 14px 0 0; margin-bottom: 4px; background: #fffdfe; box-shadow: 0 8px 0 #fffdfe; }
.modal.apd .modal-head h2 {
  font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.modal.apd .apd-pill { border-radius: 999px; }
.modal.apd button:focus-visible, .modal.apd a:focus-visible, .modal.apd select:focus-visible {
  outline: 2px solid #8051a5; outline-offset: 2px;
}

/* head */
.modal.apd .apd-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px 16px; padding-bottom: 14px; }
.modal.apd .apd-who { flex: 1 1 260px; min-width: 0; }
.modal.apd .apd-name {
  display: block; font-size: 1.35rem; font-weight: 800; color: var(--text); line-height: 1.25;
  overflow-wrap: anywhere; text-decoration: none;
}
.modal.apd a.apd-namelink { color: var(--text); }
@media (hover: hover) { .modal.apd a.apd-namelink:hover { color: #6a2484; text-decoration: underline; text-underline-offset: 3px; } }
.modal.apd .apd-namego { font-size: .9rem; color: var(--muted); font-weight: 600; }
.modal.apd .apd-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 4px; color: var(--muted); font-size: .88rem; }
.modal.apd .apd-br { min-height: 0; padding: 1px 8px; font-size: .7rem; cursor: default; }
.modal.apd .apd-ctl { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: 0 0 auto; }
.modal.apd .apd-status {
  width: auto; min-height: 36px; padding: 0 34px 0 14px; border-radius: 999px; font-weight: 700; font-size: .84rem;
  border: 1.5px solid color-mix(in srgb, var(--st, #6b7280) 45%, #fff);
  background-color: color-mix(in srgb, var(--st, #6b7280) 9%, #fff); color: color-mix(in srgb, var(--st, #6b7280) 80%, #000);
}
.modal.apd .apd-arrive {
  display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 16px;
  background: #6a2484; color: #fff; border: 0; box-shadow: none; font-weight: 700; font-size: .84rem;
}
.modal.apd .apd-arrive svg { width: 16px; height: 16px; }
.modal.apd .apd-arrive:disabled { background: #ece4f1; color: #8a7a92; cursor: default; }
.modal.apd .apd-arrive.done:disabled { background: #efe4f7; color: #6a2484; }
@media (hover: hover) { .modal.apd .apd-arrive:hover:not(:disabled) { background: #57196f; color: #fff; } }
.modal.apd .apd-statusmsg { flex: 1 0 100%; color: var(--bad); background: var(--bad-bg); border-radius: 10px; padding: 6px 10px; font-size: .82rem; }

/* two columns */
.modal.apd .apd-cols {
  display: grid; grid-template-columns: minmax(0, 54fr) minmax(0, 46fr);
  border: 1px solid #ece3f1; border-radius: 16px; overflow: hidden;
}
.modal.apd .apd-cols.one { grid-template-columns: minmax(0, 1fr); }
.modal.apd .apd-sec { font-size: .66rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: #6a2484; }
.modal.apd .apd-visit { background: linear-gradient(160deg, #f0e5fa 0%, #faf6fd 100%); padding: 16px 18px; min-width: 0; }
.modal.apd .apd-purpose { font-size: 1.12rem; font-weight: 800; color: #5b1d75; margin: 4px 0 12px; overflow-wrap: anywhere; }
.modal.apd .apd-when { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.modal.apd .apd-tile {
  flex: none; width: 52px; height: 56px; border-radius: 12px; background: #8051a5; color: #fff;
  display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1;
}
.modal.apd .apd-tile-d { font-size: 1.35rem; font-weight: 800; }
.modal.apd .apd-tile-m { font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; margin-top: 3px; opacity: .9; }
.modal.apd .apd-time { font-size: 1.02rem; font-weight: 800; color: var(--text); }
.modal.apd .apd-date { font-size: .82rem; color: var(--muted); margin-top: 2px; }
.modal.apd .apd-field { margin-top: 10px; }
.modal.apd .apd-lab { font-size: .72rem; font-weight: 700; color: #7c5a93; }
.modal.apd .apd-val { font-weight: 700; color: var(--text); overflow-wrap: anywhere; }
.modal.apd .apd-remarks {
  margin-top: 3px; padding: 2px 0 2px 10px; border-left: 3px solid #cdb3e3; font-size: .88rem; color: var(--text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.modal.apd .apd-people { margin-top: 10px; }
.modal.apd .apd-people:empty { display: none; }
.modal.apd .apd-peoplerow { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }
.modal.apd .apd-peoplerow .st-chip { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.modal.apd .apd-peoplerow a { color: inherit; text-decoration: none; font-weight: 600; }
.modal.apd .apd-x { padding: 0 2px; min-height: 0; line-height: 1; }
.modal.apd .apd-utils { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.modal.apd .apd-util {
  display: inline-flex; align-items: center; gap: 6px; min-height: 34px; padding: 0 14px;
  background: #fff; border: 1px solid #dccbe8; color: #6a2484; font-weight: 700; font-size: .8rem; box-shadow: none;
}
.modal.apd .apd-util svg { width: 15px; height: 15px; }
@media (hover: hover) { .modal.apd .apd-util:hover { background: #f7f0fb; color: #57196f; border-color: #c9add9; } }

/* previous purchases */
.modal.apd .apd-buys { background: #fcfafc; padding: 16px 16px 14px; border-left: 1px solid #ece3f1; min-width: 0; }
.modal.apd .apd-buys-top { margin-bottom: 10px; }
.modal.apd .apd-life { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: 6px; font-size: .84rem; color: var(--muted); }
.modal.apd .apd-life strong { color: var(--text); font-size: 1rem; white-space: nowrap; }
.modal.apd .apd-quiet { color: var(--muted); font-size: .85rem; margin: 6px 0 0; }
.modal.apd .apd-bad { color: var(--bad); }
.modal.apd .apd-invs, .modal.apd .apd-more { display: flex; flex-direction: column; gap: 10px; }
.modal.apd .apd-more { margin-top: 10px; }
.modal.apd .apd-inv { background: #fff; border: 1px solid #ebe3ee; border-radius: 12px; padding: 15px; }
.modal.apd .apd-inv-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.modal.apd .apd-inv-no { font-weight: 800; color: #5b1d75; text-decoration: none; overflow-wrap: anywhere; }
@media (hover: hover) { .modal.apd a.apd-inv-no:hover { text-decoration: underline; text-underline-offset: 3px; } }
.modal.apd .apd-kumo { font-size: .66rem; font-weight: 700; color: var(--muted); background: #f1edf3; border-radius: 999px; padding: 1px 8px; }
.modal.apd .apd-inv-sub { font-size: .78rem; color: var(--muted); margin-top: 2px; }
.modal.apd .apd-inv-what { font-size: .8rem; color: var(--text); margin-top: 6px; overflow-wrap: anywhere; }
.modal.apd .apd-inv-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: 10px; font-size: .84rem; color: var(--muted); }
.modal.apd .apd-inv-row strong { color: var(--text); font-size: .95rem; white-space: nowrap; }
.modal.apd .apd-owe { margin-top: 10px; background: #fff1dc; color: #96520e; border-radius: 10px; padding: 8px 12px; }
.modal.apd .apd-owe span { display: block; font-size: .74rem; font-weight: 700; }
.modal.apd .apd-owe strong { display: block; font-size: 21px; font-weight: 800; line-height: 1.25; overflow-wrap: anywhere; }
.modal.apd .apd-paid { display: inline-block; margin-top: 10px; background: #e3f4e8; color: #1a6b36; border-radius: 999px; padding: 2px 10px; font-size: .76rem; font-weight: 700; }
.modal.apd .apd-credit { margin-top: 10px; background: #e8f1fa; color: #0b5fa5; border-radius: 10px; padding: 6px 10px; font-size: .8rem; font-weight: 600; }
.modal.apd .apd-inv-note { margin-top: 8px; font-size: .76rem; color: var(--muted); }
.modal.apd .apd-view {
  display: inline-flex; align-items: center; margin-top: 12px; min-height: 32px; padding: 0 14px;
  border: 1px solid #dccbe8; background: #fff; color: #6a2484; font-weight: 700; font-size: .78rem; text-decoration: none;
}
@media (hover: hover) { .modal.apd .apd-view:hover { background: #f7f0fb; } }
.modal.apd .apd-allbtn {
  margin-top: 10px; width: 100%; min-height: 34px; background: #fff; border: 1px dashed #d6c4e3; color: #6a2484;
  font-weight: 700; font-size: .8rem; box-shadow: none;
}

/* audit row */
.modal.apd .apd-audit {
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 6px 12px;
  padding: 10px 2px 0; font-size: .78rem; color: var(--muted);
}
.modal.apd .apd-histbtn { background: none; border: 0; box-shadow: none; color: #6a2484; font-weight: 700; font-size: .78rem; padding: 4px 2px; min-height: 0; }
.modal.apd .apd-hist { margin-top: 6px; background: #faf7fb; border-radius: 10px; padding: 6px 10px; font-size: .8rem; overflow-x: auto; }
.modal.apd .apd-hist table { width: 100%; border-collapse: collapse; }
.modal.apd .apd-hist td { padding: 4px 8px 4px 0; vertical-align: top; }

/* footer */
.modal.apd .apd-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line);
}
.modal.apd .apd-main {
  display: inline-flex; align-items: center; gap: 7px; min-height: 40px; padding: 0 18px;
  background: #6a2484; color: #fff; border: 0; box-shadow: none; font-weight: 700;
}
.modal.apd .apd-main svg { width: 17px; height: 17px; }
@media (hover: hover) { .modal.apd .apd-main:hover { background: #57196f; color: #fff; } }
.modal.apd .apd-sec-btn {
  display: inline-flex; align-items: center; min-height: 40px; padding: 0 16px;
  background: #fff; border: 1px solid #dccbe8; color: #4d1469; box-shadow: none; font-weight: 700;
}
@media (hover: hover) { .modal.apd .apd-sec-btn:hover { background: #f7f0fb; color: #4d1469; } }
.modal.apd .apd-manage { position: relative; margin-left: auto; }
.modal.apd .apd-menu {
  position: absolute; right: 0; bottom: calc(100% + 6px); z-index: 30; min-width: 200px;
  background: #fff; border: 1px solid #e3d8ea; border-radius: 12px; padding: 5px; box-shadow: 0 10px 28px rgba(42, 31, 46, .18);
}
.modal.apd .apd-mi {
  display: block; width: 100%; text-align: left; background: none; border: 0; box-shadow: none; border-radius: 8px;
  padding: 9px 12px; min-height: 0; color: var(--text); font-weight: 600;
}
.modal.apd .apd-mi:focus-visible { outline: 2px solid #8051a5; outline-offset: -2px; }
@media (hover: hover) { .modal.apd .apd-mi:hover { background: #f5ecf7; color: var(--text); } }
.modal.apd .apd-mi-del { color: var(--bad); }
@media (hover: hover) { .modal.apd .apd-mi-del:hover { background: var(--bad-bg); color: var(--bad); } }

/* phones: stack, controls under the name, footer wraps */
@media (max-width: 640px) {
  .modal.apd { padding: 0 14px 14px; }
  .modal.apd .apd-cols { grid-template-columns: minmax(0, 1fr); }
  .modal.apd .apd-buys { border-left: 0; border-top: 1px solid #ece3f1; }
  .modal.apd .apd-ctl { flex: 1 0 100%; }
  .modal.apd .apd-status { font-size: 16px; }
  .modal.apd .apd-foot > .apd-main { flex: 1 0 100%; justify-content: center; }
  .modal.apd .apd-manage { margin-left: 0; }
  .modal.apd .apd-menu { left: 0; right: auto; }
}

/* The phone's booking page wears the details popup's styles (2026-10-20):
   the .modal.apd block as a plain page section, not a floating box. */
.modal.apd.mob-apd {
  position: static; max-width: none; max-height: none; overflow: visible; margin: 0 0 90px;
  padding: 14px 14px 12px; border-radius: 16px; box-shadow: var(--shadow); animation: none; background: #fff;
}
.modal.apd.mob-apd .apd-ctl { flex: 1 0 100%; }
.modal.apd.mob-apd .apd-status { font-size: 16px; min-height: 40px; }
.modal.apd.mob-apd .apd-arrive { min-height: 40px; }
.modal.apd.mob-apd .apd-sub a { color: var(--muted); }
.mob-apd-del { text-align: center; margin-top: 16px; }
.modal.apd .apd-del {
  background: none; border: 0; box-shadow: none; color: var(--bad); font-weight: 600; font-size: .85rem;
  min-height: 40px; padding: 0 12px; text-decoration: underline; text-underline-offset: 3px;
}

/* The drawn chevron on Status history and Manage (2026-10-20). */
.modal.apd .apd-chev { display: inline-flex; margin-left: 5px; vertical-align: middle; transition: transform .15s; }
.modal.apd .apd-chev svg { display: block; }
.modal.apd [aria-expanded="true"] > .apd-chev { transform: rotate(180deg); }
.modal.apd .apd-histbtn, .modal.apd .apd-manage > button { display: inline-flex; align-items: center; }
/* The status controls were clipped along their top edge (Giselle,
   2026-10-20: "the pending and mark arrived button why cut off"): the
   frozen head's 8px white under-band painted over them. No band here, and
   the head row gets a little air of its own. */
.modal.apd .modal-head { box-shadow: none; margin-bottom: 0; padding-bottom: 6px; }
.modal.apd .apd-head { padding-top: 4px; }

/* Edit and Delete as round icon buttons at the foot's right end (2026-10-20). */
.modal.apd .apd-manage { display: inline-flex; gap: 8px; }
.modal.apd .apd-icon {
  width: 40px; height: 40px; min-height: 0; padding: 0; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid #dccbe8; color: #4d1469; box-shadow: none;
}
.modal.apd .apd-icon svg { width: 18px; height: 18px; }
.modal.apd .apd-icon-del { color: var(--bad); border-color: #f0c9c5; }
@media (hover: hover) {
  .modal.apd .apd-icon:hover { background: #f7f0fb; color: #4d1469; }
  .modal.apd .apd-icon-del:hover { background: var(--bad-bg); color: var(--bad); }
}
@media (max-width: 640px) { .modal.apd .apd-manage { margin-left: auto; } }

/* The order popup on a phone (Giselle, 2026-10-24: "Fix this mobile
   version"): the stepper becomes six dots spread across the width with the
   current stage named underneath; the title bar is tighter; and the frozen
   footer lays its buttons out as a grid - Print and WhatsApp side by side,
   the big submit across the full width - instead of a ragged right edge. */
.steps-cap { display: none; }
@media (max-width: 640px) {
  .steps-wrap { margin: 6px 0 14px; }
  .steps-wrap .steps { overflow: visible; margin: 0; }
  .steps-wrap .step { flex: 1 1 0; gap: 0; }
  .steps-wrap .step:last-child { flex: none; }
  .steps-wrap .step .lbl { display: none; }
  .steps-wrap .step::after { flex: 1 1 auto; margin: 0 4px; min-width: 4px; }
  .steps-wrap .step.done::after { background: var(--brand); }
  .steps-wrap .step .dot { width: 28px; height: 28px; }
  .steps-cap { display: block; margin-top: 8px; font-size: .84rem; color: var(--muted); }
  .steps-cap strong { color: var(--brand); }
  .modal .modal-head h2 { font-size: .95rem; line-height: 1.35; }
  .modal .modal-head button.fo-msg-btn { margin-left: 0 !important; padding: 5px 12px; font-size: .8rem; min-height: 32px; }
  .modal .actions.po-foot { flex-wrap: wrap; padding: 10px 22px 14px; }
  .modal .actions.po-foot > span[style*="flex:1"],
  .modal .actions.po-foot > span[style*="flex: 1"] { display: none; }
  .modal .actions.po-foot > button.small { flex: 1 1 calc(50% - 6px); min-width: 0; justify-content: center; font-size: .76rem; padding: 6px 8px; white-space: normal !important; }
  .modal .actions.po-foot > .po-gate { flex: 1 1 100%; justify-content: center; }
  .actions.po-foot > button.submit-final.head { flex: 1 1 100%; margin-left: 0; padding: 12px 14px; font-size: .9rem; }
}
/* "When to deliver?" fits a phone (2026-10-24): the three choices share the
   width and their words wrap, instead of Process Later running off the edge. */
@media (max-width: 640px) {
  .po-prio > button { min-width: 0; white-space: normal; padding: 6px 6px; font-size: .78rem; line-height: 1.2; justify-content: center; text-align: center; }
}

/* ==== Bedrock on a phone (Giselle, 2026-10-24: "go through the mobile
   version, see if there's any bugs or any layouts are messy ... everything
   in bedrock") ==== */
/* No zoom-in on tap: iPhone Safari zooms the whole page into any box whose
   text is under 16px and leaves it zoomed. On a touch phone every box is
   16px. */
@media (max-width: 760px) and (pointer: coarse) {
  input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]):not([type=color]),
  select, textarea { font-size: 16px !important; }
  /* ...and a box never pokes out of the space it was given (the larger type
     made some boxes wider than their column). */
  input:not([type=checkbox]):not([type=radio]), select, textarea { max-width: 100%; }
}
/* Lists as cards (labels from nav.js phoneTables): one card per row, each
   cell a line with its column name on the left and the value on the right. */
@media (max-width: 640px) {
  table.ph-cards, table.ph-cards > tbody, table.ph-cards > tbody > tr, table.ph-cards > tr { display: block; width: auto; }
  table.ph-cards > thead, table.ph-cards tr.ph-head { display: none; }
  table.ph-cards > tbody > tr, table.ph-cards > tr {
    border: 1px solid var(--line); border-radius: 12px; padding: 6px 12px; margin: 0 0 10px;
    background: var(--card, #fff);
  }
  table.ph-cards > tbody > tr > td, table.ph-cards > tr > td {
    display: flow-root; border: 0; padding: 5px 0; text-align: right; min-width: 0;
    overflow-wrap: anywhere; width: auto !important; max-width: none !important; white-space: normal;
  }
  table.ph-cards > tbody > tr > td + td, table.ph-cards > tr > td + td { border-top: 1px dashed var(--line); }
  table.ph-cards td[data-l]::before {
    content: attr(data-l); float: left; max-width: 46%; margin-right: 12px; text-align: left;
    color: var(--muted); font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    line-height: 1.6;
  }
  table.ph-cards td[data-l=""]::before { display: none; }
  table.ph-cards td[data-l=""] { text-align: left; }
  table.ph-cards > tbody > tr > td:empty, table.ph-cards > tr > td:empty { display: none; }
  /* The first cell leads the card. */
  table.ph-cards td.ph-nil { display: none; }
  /* The first cell leads the card, as its title. */
  table.ph-cards > tbody > tr > td:first-child, table.ph-cards > tr > td:first-child { font-weight: 700; font-size: .95rem; text-align: left; }
  table.ph-cards > tbody > tr > td:first-child::before, table.ph-cards > tr > td:first-child::before { display: none; }
  table.ph-cards > tbody > tr > td:first-child + td, table.ph-cards > tr > td:first-child + td { border-top: 0; }
  @media (hover: hover) { .content table.ph-cards tr:hover > td { background: none; } }
}

/* The Order Form list on a phone (2026-10-24): one compact card per order -
   customer and status on top, the product, then branch · date and the
   invoice, then the clinician and any remarks. Delivery details (the same
   name, phone and address) are one tap away in the order itself. */
@media (max-width: 640px) {
  table.po-list { display: block; }
  table.po-list > tr:first-child, table.po-list > tbody > tr:first-child:not(.click) { display: none; }
  table.po-list tr.click {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 10px; align-items: baseline;
    padding: 12px 14px; margin: 0 0 10px; border: 1px solid var(--line); border-radius: 14px; background: var(--card, #fff);
  }
  table.po-list tr.click.row-draft { background: #fdf6df; border-color: #f1dfa6; }
  table.po-list tr.click > td { display: block; border: 0; padding: 0; background: none !important; min-width: 0; }
  table.po-list tr.click > td:nth-child(2) { grid-column: 1; grid-row: 1; font-size: .95rem; overflow-wrap: anywhere; }
  table.po-list tr.click > td:nth-child(8) { grid-column: 2; grid-row: 1; text-align: right; white-space: nowrap; }
  table.po-list tr.click > td:nth-child(5) { grid-column: 1 / -1; grid-row: 2; font-size: .88rem; }
  table.po-list tr.click > td:nth-child(1) { grid-column: 1; grid-row: 3; font-size: .78rem; }
  table.po-list tr.click > td:nth-child(1) > * { display: inline; margin: 0 0 0 6px !important; }
  table.po-list tr.click > td:nth-child(1) > :first-child { margin-left: 0 !important; }
  table.po-list tr.click > td:nth-child(3) { grid-column: 2; grid-row: 3; text-align: right; font-size: .78rem; }
  table.po-list tr.click > td:nth-child(3) > div { display: none; }
  table.po-list tr.click > td:nth-child(4) { grid-column: 1 / -1; grid-row: 4; font-size: .78rem; white-space: normal !important; }
  table.po-list tr.click > td:nth-child(4) > div { display: inline; margin-right: 8px; }
  table.po-list tr.click > td:nth-child(6) { display: none; }
  table.po-list tr.click > td:nth-child(7) {
    grid-column: 1 / -1; grid-row: 5; font-size: .76rem;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  table.po-list tr.click > td:nth-child(7):is(:empty) { display: none; }
  table.po-list tr.row-done { opacity: .5; }
  table.po-list tr.row-done > td { opacity: 1; }
}
@media (max-width: 640px) { table.po-list tr.click > td.po-norem { display: none; } }
/* Dashboard tiles on a phone (2026-10-24): two to a row, icon above the
   figure, no empty height - one tile used to fill a third of the screen. */
@media (max-width: 560px) {
  .kpis-money, .kpis-day { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .kpis-money > .kpi:last-child:nth-child(odd), .kpis-day > .kpi:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .kpis-money .kpi-fun, .kpis-day .kpi-fun { flex-direction: column; align-items: flex-start; padding: 12px; gap: 6px; min-height: 0; }
  .kpis-money .kpi-fun .kpi-ic, .kpis-day .kpi-fun .kpi-ic { width: 30px; height: 30px; flex: none; font-size: 1rem; border-radius: 9px; }
  .kpis-money .kpi-fun .v, .kpis-day .kpi-fun .v { font-size: 1.05rem; }
}
/* Customer pick lists on a phone: one to a row, the whole name shown. */
@media (max-width: 560px) {
  .pick-grid:has(> .cust-pick) { grid-template-columns: minmax(0, 1fr); }
  .cust-pick .cust-nm { white-space: normal; overflow: visible; text-overflow: clip; }
}
/* The calendar on a phone (2026-10-24): the walk-away count sat on top of
   the branch name in the narrow columns - it steps under the name instead;
   and the tool row (view · Block · Roster · Next mattress) wraps rather than
   running off the screen. */
@media (max-width: 560px) {
  .cal-branches .hd.br-hd .wa-badge { position: static; display: flex; margin: 2px auto 0; width: max-content; }
  .cal-tools { flex-wrap: wrap; row-gap: 6px; }
  .cal-tools .mattress-next-btn { white-space: normal; text-align: left; }
}

/* Headless lists on a phone (nav.js phoneTables): each row one card, its
   cells flowing as a wrapped line - time, name, clinician, purpose, status. */
@media (max-width: 640px) {
  table.ph-stack, table.ph-stack > tbody { display: block; width: auto; }
  table.ph-stack > tbody > tr, table.ph-stack > tr {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px;
    padding: 9px 2px; border-bottom: 1px solid var(--line);
  }
  table.ph-stack > tbody > tr > td, table.ph-stack > tr > td {
    display: block; border: 0; padding: 0; min-width: 0; width: auto !important; max-width: 100%;
    white-space: normal !important; overflow-wrap: anywhere; text-align: left;
  }
  table.ph-stack > tbody > tr > td:first-child, table.ph-stack > tr > td:first-child { font-weight: 700; }
  table.ph-stack > tbody > tr > td:empty, table.ph-stack > tr > td:empty { display: none; }
}

/* Inventory on a phone (2026-10-24): the item's name on top with its SKU
   and supply route under it, then the six counts as small tiles three to a
   row, then its buttons - instead of ten lines per item. */
@media (max-width: 640px) {
  table.ph-cards.inv-list > tr:not(.ph-head) { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; padding: 12px; }
  table.ph-cards.inv-list > tr > td { border: 0 !important; padding: 0; text-align: left; }
  table.ph-cards.inv-list > tr > td:nth-child(2) { grid-column: 1 / -1; grid-row: 1; font-weight: 700; font-size: .95rem; }
  table.ph-cards.inv-list > tr > td:nth-child(2)::before { display: none; }
  table.ph-cards.inv-list > tr > td:nth-child(1) { grid-column: 1 / 3; grid-row: 2; font-weight: 600; font-size: .78rem; color: var(--muted); }
  table.ph-cards.inv-list > tr > td:nth-child(3) { grid-column: 3; grid-row: 2; text-align: right; font-size: .78rem; }
  table.ph-cards.inv-list > tr > td:nth-child(3)::before { display: none; }
  table.ph-cards.inv-list > tr > td:nth-child(n+4):nth-child(-n+9) {
    display: block; background: var(--brand-tint, #f5ecf7); border-radius: 10px; padding: 6px 8px; text-align: left; font-weight: 700;
  }
  table.ph-cards.inv-list > tr > td:nth-child(n+4):nth-child(-n+9)::before { float: none; display: block; max-width: none; margin: 0; font-size: .62rem; }
  table.ph-cards.inv-list > tr > td.ph-nil:nth-child(n+4) { display: block; }
  table.ph-cards.inv-list > tr > td:nth-child(10) { grid-column: 1 / -1; display: flex; gap: 6px; flex-wrap: wrap; }
}
/* Director header on a phone (2026-10-24): "n of 5 systems live" was squeezed
   into a narrow column beside the buttons - it takes its own line above them. */
@media (max-width: 640px) {
  .panel-head > div { flex-wrap: wrap; }
  .panel-head > div > .muted:first-child { flex: 1 0 100%; }
}
/* A finished bill on a phone (2026-10-24): Invoice PDF, Receipt, Credit to
   a partner and Void ran off the right edge in one row - they wrap, two to
   a row, each button's words on one line. */
@media (max-width: 640px) {
  .cart-actions .doc-actions { flex-wrap: wrap; gap: 8px; width: 100%; }
  .cart-actions .doc-actions > * { flex: 1 1 calc(50% - 8px); min-width: 0; justify-content: center; white-space: nowrap; }
  .cart-actions .doc-actions > :first-child:empty, .cart-actions .doc-actions > button:first-child { flex: 0 0 auto; }
}

/* New / edit customer on a phone (2026-10-24): one field per row. */
@media (max-width: 560px) {
  .cf-form .row > * { flex: 1 0 100%; }
}
/* The invoice register on a phone (2026-10-24): each bill reads as a card -
   the customer and the total on top, the bill number and branch, then the
   date. The middle-width layout squeezed the name to one word a line and
   clipped the total. */
@media (max-width: 560px) {
  .inv-row, .inv-row.no-acts {
    grid-template-columns: 30px minmax(0, 1fr) auto;
    grid-template-areas: "tk who amt" "tk no amt" "tk dt amt" "tk acts acts";
  }
  .inv-row > * { border-left: 0 !important; padding: 2px 8px; }
  .inv-row > .inv-tick { grid-area: tk; }
  .inv-row > .inv-who { grid-area: who; padding-top: 8px; }
  .inv-row > .inv-amts { grid-area: amt; padding-top: 8px; justify-content: flex-start; }
  .inv-row > .inv-no { grid-area: no; display: flex !important; flex-direction: row; justify-content: flex-start; }
  .inv-row > .inv-no > div { display: inline; }
  .inv-row > .inv-dt { grid-area: dt; flex-direction: row; justify-content: flex-start; gap: 6px; font-size: .74rem; color: var(--muted); padding-bottom: 8px; }
  .inv-row > .inv-mode { display: none !important; }
  .inv-row > .inv-acts { grid-area: acts; flex-direction: row; justify-content: flex-end; }
  .inv-row .inv-name { font-size: .95rem; }
  .inv-head-row { grid-template-areas: "tk who who"; }
  .inv-head-row > :not(.inv-tick):not(:nth-child(6)) { display: none !important; }
  .inv-head-row > :nth-child(6) { grid-area: who; }
}
/* The roster popup on a phone (2026-10-24): the month and arrows on one
   line, the branch buttons wrap onto the next - they ran off the edge. */
@media (max-width: 560px) {
  .rc-bar { flex-wrap: wrap; row-gap: 8px; }
  .rc-bar .rc-picks { flex: 1 0 100%; display: flex; flex-wrap: wrap; gap: 6px; margin-left: 0; }
}

/* The phone calendar's red "now" line (2026-10-24), the desktop's look: a red
   rule across today's columns with a dot at its start, and the time in a red
   pill in the hour gutter. Under the sticky names, over the bookings. */
.mcal-now {
  position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid #dc2626;
  z-index: 3; pointer-events: none;
}
.mcal-colwrap:nth-child(2) .mcal-now::before, .mob-wbody .mcal-now::before {
  content: ""; position: absolute; left: -4px; top: -5px;
  width: 8px; height: 8px; border-radius: 50%; background: #dc2626;
}
.mcal-nowtime {
  position: absolute; right: 2px; transform: translateY(-50%); z-index: 3;
  font-size: .6rem; font-weight: 800; color: #fff; background: #dc2626;
  padding: 1px 4px; border-radius: 6px; font-variant-numeric: tabular-nums; pointer-events: none;
}

/* CS lead of the day (2026-10-24): a gold phone - the clinicians' lead wears a
   gold crown - and the name, on a soft pill. */
.cs-lotd {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  padding: 2px 10px 2px 3px; border-radius: 999px; font-size: .78rem; font-weight: 700;
  background: #fff7e0; color: #6b4e00; border: 1px solid #f1d98a;
}
.cs-lotd-ic {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 20px; height: 20px; border-radius: 50%; background: #f5b400; color: #fff;
}
.cs-lotd-ic .ic, .cs-lotd-ic .ic svg, .cs-lotd-ic svg { width: 12px; height: 12px; }
.cs-lotd-box:empty { display: none; }
.cal-tools .cs-lotd { font-size: .72rem; }
.mcal-title .cs-lotd { margin-left: auto; }
.rc-who.rc-cs { display: inline-flex; align-items: center; gap: 3px; background: #fff7e0; color: #6b4e00; }
.rc-who.rc-cs .cs-lotd-ic { width: 15px; height: 15px; }
.rc-who.rc-cs .cs-lotd-ic svg { width: 9px; height: 9px; }
button.rd-cs {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--card); color: var(--text); border: 1.5px solid var(--line); box-shadow: none; font-weight: 700;
}
button.rd-cs .cs-lotd-ic { background: #e9e3ee; }
button.rd-cs.on { background: #fff7e0; border-color: #f1d98a; }
button.rd-cs.on .cs-lotd-ic { background: #f5b400; }
/* ...wearing a small crown (Giselle, 2026-10-25: "give it a crown on the
   phone") - the lead's crown, on the CS team's phone. */
.cs-lotd .cs-lotd-ic, .rc-who.rc-cs .cs-lotd-ic, button.rd-cs.on .cs-lotd-ic { position: relative; overflow: visible; }
.cs-lotd .cs-lotd-ic::after, .rc-who.rc-cs .cs-lotd-ic::after, button.rd-cs.on .cs-lotd-ic::after {
  content: "👑"; position: absolute; top: -9px; left: 50%; transform: translateX(-50%) rotate(-12deg);
  font-size: 11px; line-height: 1; pointer-events: none;
}
.rc-who.rc-cs .cs-lotd-ic::after { font-size: 8px; top: -7px; }
.cs-lotd { margin-top: 4px; }
.mcal-title .cs-lotd .cs-lotd-ic::after { font-size: 13px; top: -12px; }
/* The drawn crown-and-phone mark replaces the gold circle and the emoji crown. */
.cs-lotd-ic, button.rd-cs .cs-lotd-ic, button.rd-cs.on .cs-lotd-ic { background: none !important; width: 22px; height: 22px; border-radius: 0; }
.cs-lotd-ic .ic, .cs-lotd-ic .ic svg, .cs-lotd-ic svg { width: 22px !important; height: 22px !important; }
.cs-lotd .cs-lotd-ic::after, .rc-who.rc-cs .cs-lotd-ic::after, button.rd-cs.on .cs-lotd-ic::after, .mcal-title .cs-lotd .cs-lotd-ic::after { content: none; }
.cs-lotd { padding-left: 5px; margin-top: 0; }
.rc-who.rc-cs .cs-lotd-ic, .rc-who.rc-cs .cs-lotd-ic svg { width: 16px !important; height: 16px !important; }
button.rd-cs:not(.on) .cs-lotd-ic { opacity: .35; filter: grayscale(1); }
/* 🌟 for the CS lead (Giselle, 2026-10-25: "Use this for CS lead"). */
.cs-lotd-ic { font-size: 16px; line-height: 1; width: auto !important; height: auto !important; }
.rc-who.rc-cs .cs-lotd-ic { font-size: 12px; }

/* The day picker in two groups (2026-10-25): Clinicians, then Customer Service
   with its own 🌟 lead. */
.rd-sec {
  display: flex; align-items: baseline; gap: 8px; margin: 10px 7px 4px; padding-top: 8px;
  border-top: 1px solid var(--line); font-size: .7rem; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--brand);
}
.rd-sec:first-child { border-top: 0; margin-top: 0; padding-top: 0; }
.rd-sec span { text-transform: none; letter-spacing: 0; font-weight: 600; color: var(--muted); font-size: .72rem; }
.rd-cs-row { cursor: default; }
.rd-star { font-size: .9rem; line-height: 1; opacity: .35; filter: grayscale(1); }
.rd-cs-lead.on .rd-star { opacity: 1; filter: none; }
.rd-cs-note { margin: 4px 7px; font-size: .8rem; }
.rd-cs-row > span:first-child { font-size: .84rem; font-weight: 600; color: #6d6378; }

/* The 🌟 chip as a button (2026-10-25): tap to choose the CS lead. */
button.cs-lotd { min-height: 0; box-shadow: none; cursor: pointer; font-family: inherit; }
@media (hover: hover) { button.cs-lotd:hover { background: #ffefb8; border-color: #e7c35a; } }
.cs-lotd.empty { background: #fff; border-style: dashed; color: var(--muted); font-weight: 600; }
.cs-lotd.empty .cs-lotd-ic { opacity: .45; filter: grayscale(1); }
/* Block and Roster as icons (2026-10-25): square-ish, the same height as the pills. */
.cal-tools .cal-ico { padding: 3px 8px; min-width: 32px; justify-content: center; }
.cal-tools .cal-ico .ic, .cal-tools .cal-ico .ic svg { width: 16px; height: 16px; }
.cal-tools { gap: 6px; }

/* ---- Phone app (2026-10-25) --------------------------------------------
   The visit's paperwork strip on the PHONE calendar's cards, as on the
   desktop calendar: case note / billing $ / factory order, red to do, green
   done (Dr Daniel: "add this for the mobile"). */
.mcal-appt .wf-strip {
  position: absolute; bottom: 1px; right: 1px; z-index: 1;
  display: inline-flex; gap: 3px; align-items: center;
  background-color: inherit; padding: 2px 3px; border-radius: 4px 0 0 0;
}
.mcal-appt .wf-ic { width: 10px; height: 10px; display: inline-block; background: #dc2626; }
.mcal-appt .wf-ic.ok { background: #059669; }
.mcal-appt .wf-note {
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M5 2h9l5 5v15H5z M13 3.2V8h4.8z M8 12h8v1.6H8z M8 15.5h8v1.6H8z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M5 2h9l5 5v15H5z M13 3.2V8h4.8z M8 12h8v1.6H8z M8 15.5h8v1.6H8z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mcal-appt .wf-cash {
  background: none !important; width: auto; min-width: 7px;
  color: #dc2626; font: 900 11px/10px ui-sans-serif, system-ui, sans-serif; text-align: center;
}
.mcal-appt .wf-cash.ok { color: #059669; }
.mcal-appt .wf-fpo {
  -webkit-mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1 5h13v10H1z M14 8h4l3 3v4h-7z'/%3E%3Ccircle cx='6.5' cy='17.5' r='2.6'/%3E%3Ccircle cx='17.5' cy='17.5' r='2.6'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M1 5h13v10H1z M14 8h4l3 3v4h-7z'/%3E%3Ccircle cx='6.5' cy='17.5' r='2.6'/%3E%3Ccircle cx='17.5' cy='17.5' r='2.6'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* A page opened from the phone app: its own "‹ App" back, unless the page
   already carries one (Day Close's .dc-back). */
.app-back {
  position: fixed; top: 8px; left: 8px; z-index: 50; padding: 6px 12px; border-radius: 999px;
  background: var(--card); color: var(--brand); font-weight: 800; text-decoration: none;
  box-shadow: 0 4px 14px rgba(71, 0, 84, .18); font-size: .85rem;
}
body:has(.dc-back) .app-back { display: none; }
body.from-app .content { padding-top: 46px; }
body.from-app:has(.dc-back) .content { padding-top: 8px; }

/* An appointment's bill on the phone (2026-10-25): tap for the bill pop-up. */
.apd-bill {
  display: block; width: 100%; text-align: left; margin-top: 4px; padding: 8px 10px;
  background: var(--card); color: var(--brand); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: none; min-height: 0; font-weight: 600; font-size: .9rem;
}
.apd-bill span { color: var(--muted); font-weight: 600; }
.mob-invpop .card { margin: 0 0 10px; }

/* The bill's footer buttons (Giselle, 2026-10-25: "why the chips so ugly"):
   in a middle-width window the four buttons were squeezed until each word
   took its own line. Each keeps its words on one line at a normal button
   size; when the row is full they wrap to a second row instead. */
@media (min-width: 641px) {
  .cart-actions { flex-wrap: wrap; row-gap: 10px; }
  .cart-actions .doc-actions { flex-wrap: wrap; gap: 8px; }
  .cart-actions .doc-actions > * { flex: none; white-space: nowrap; min-height: 38px; padding: 7px 16px; font-size: .86rem; }
  .cart-actions .doc-actions > button:first-child:not(:only-child) { padding: 7px 10px; }
}
/* An amount never breaks across lines ("– RM1,547.50" split in two). */
.cart-lines td.num { white-space: nowrap; }

/* The phone's Roster sheet (2026-10-25). */
.mob-rs-br { display: flex; gap: 6px; margin: 0 0 8px; }
.mob-rs-list { max-height: 58vh; }
.mob-rs-list .rd-row { padding: 8px 6px; }
.mob-rs-list .inv-tick { width: 20px; height: 20px; }

/* One look for the calendar's top bar (Giselle, 2026-10-25: "These chips
   design too many styles ... 统一"): Block and Roster are round white icon
   buttons with a thin lavender outline; the 🌟 CS lead and "Next: name" are
   plain words - the lead in purple, the mattress note in quiet grey so it
   never outshines the lead. Same on the phone app's 🌟. */
.cal-tools .block-btn, .cal-tools .roster-btn {
  height: 30px; min-height: 30px; width: 34px; min-width: 34px; padding: 0; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: #fff; background-image: none; border: 1px solid #dccbe8; color: #4d1469; box-shadow: none;
}
@media (hover: hover) { .cal-tools .block-btn:hover, .cal-tools .roster-btn:hover { background: #f7f0fb; filter: none; } }
.cal-tools .cs-lotd, .mcal-title .cs-lotd {
  background: none; border: 0; padding: 0 4px; height: 30px; min-height: 30px; border-radius: 8px;
  font-size: .76rem; font-weight: 800; color: #4d1469; box-shadow: none; gap: 6px;
}
.mcal-title .cs-lotd { font-size: .86rem; }
@media (hover: hover) { button.cs-lotd:hover { background: #f7f0fb; border-color: transparent; } }
.cal-tools .cs-lotd.empty, .mcal-title .cs-lotd.empty { border: 0; background: none; color: #7a6a86; font-weight: 600; }
.cal-tools .mattress-next-btn {
  background: none; border: 0; padding: 0 4px; height: 30px; min-height: 30px; box-shadow: none;
  font-size: .76rem; display: inline-flex; align-items: center; gap: 4px;
}
.cal-tools .mattress-next-btn, .cal-tools .mattress-next-btn .mn-label, .cal-tools .mattress-next-btn .mn-name {
  color: #7a6a86; font-weight: 500; opacity: 1;
}
@media (hover: hover) { .cal-tools .mattress-next-btn:hover:not(:disabled) { background: #f7f0fb; } }
/* Three buttons on the phone's bottom bar (Block · Roster · + Appointment):
   each label on one line. */
.mob-fabbar:has(> button:nth-child(3)) { gap: 6px; }
.mob-fabbar:has(> button:nth-child(3)) button { white-space: nowrap; padding-left: 8px; padding-right: 8px; font-size: .9rem; min-width: 0; }
.mob-fabbar:has(> button:nth-child(3)) button:last-child { flex: 1.35; }
/* The phone's bottom bar in the top bar's look (Giselle, 2026-10-25: "the
   chips down there, same style"): Block and Roster white with a thin
   lavender outline and purple ink; + Appointment stays the solid button. */
.mob-fabbar button.ghost {
  background: #fff; border: 1px solid #dccbe8; color: #4d1469; box-shadow: none;
}
@media (hover: hover) { .mob-fabbar button.ghost:hover { background: #f7f0fb; } }

/* The bill's footer in the calendar bar's look (Giselle, 2026-10-25: "THe chips
   here, anyway to make it nicer?"): white chips with a thin lavender outline
   and purple ink, all one height; the pencil a round icon; Void white with a
   soft red outline; Order Form slimmer - still amber while not yet sent (its
   title is set only then), white like the rest once it is. */
.cart-actions .doc-actions > *, .cart-actions .order-form-btn { height: 36px; min-height: 36px; padding: 0 16px; border-radius: 999px; font-size: .84rem; font-weight: 600; box-shadow: none; display: inline-flex; align-items: center; gap: 7px; text-decoration: none; }
.cart-actions .doc-actions > .ghost:not(.danger) { background: #fff; border: 1px solid #dccbe8; color: #4d1469; }
.cart-actions .doc-actions > .ghost.danger { background: #fff; border: 1px solid #f0c9c5; color: #b42318; }
.cart-actions .doc-actions > .no-fill.no-outline { width: 36px; min-width: 36px; padding: 0; justify-content: center; background: #fff; border: 1px solid #dccbe8; color: #4d1469; }
.cart-actions .order-form-btn { background: #fff !important; border: 1px solid #dccbe8 !important; color: #4d1469 !important; }
.cart-actions .order-form-btn[title] { background: #fff8ec !important; border-color: #f1d39a !important; color: #7a4b00 !important; }

/* A note opened on its own (2026-10-25): the same width-aware layout as the
   note cards - on a phone one column, labels above their answers. */
.note-view { container-type: inline-size; }

/* The case note on a phone (Giselle, 2026-10-25: "This also so messy. If
   already have case notes, pls show it at the top of the page"): a customer's
   past notes come first, under their name, when there are any; the photo /
   gallery / letter buttons share one row; Patient details puts each label
   beside its line, half the height it was. */
@media (max-width: 1000px) {
  .cn-split:has(.cn-history .cn-item) .cn-history { order: 0; border-top: 0; margin-top: 0; padding-top: 0; border-bottom: 1px solid var(--line); padding-bottom: 12px; margin-bottom: 4px; }
}
@media (max-width: 620px) {
  .cn-sheet3 { grid-template-columns: 104px minmax(0, 1fr); align-items: center; column-gap: 10px; }
  .cn-toptools .cn-toprow { gap: 6px; }
  .cn-toptools .cn-toprow > button { flex: 1 1 calc(33.3% - 6px); min-width: 0; font-size: .76rem; padding: 6px 6px; line-height: 1.15; white-space: normal; text-align: center; justify-content: center; }
}

/* Public holidays (Giselle, 2026-10-27): a soft rose day, named. A strip over
   a one-day calendar, the name under the date in a week, and a rose dot plus
   the name in the month calendars. */
.ph-banner {
  display: flex; align-items: center; gap: 6px; margin: 0 0 8px; padding: 7px 12px;
  border-radius: 10px; background: #fff1f2; border: 1px solid #fecdd3; color: #9f1239; font-size: .84rem;
}
.ph-banner[hidden] { display: none; }
.ph-dot { width: 8px; height: 8px; border-radius: 50%; background: #e11d48; flex: none; display: inline-block; }
.cal .slot.ph-col, .cal-scroll.ph-day .cal .slot, .mcal-scroll.ph-day .mcal-col { background-color: #fff5f6; }
.hd-ph { display: block; font-size: .64rem; font-weight: 700; color: #be123c; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mc-day.ph { color: #be123c; font-weight: 800; position: relative; }
.mc-day.ph::after { content: ""; position: absolute; left: 50%; bottom: 2px; width: 4px; height: 4px; margin-left: -2px; border-radius: 50%; background: #e11d48; }
.mc-day.ph.sel { color: #fff; }
.mc-day.ph.sel::after { background: #fff; }
.mc-ph { margin: 4px 2px 2px; display: flex; flex-direction: column; gap: 2px; font-size: .74rem; color: #9f1239; }
.mc-ph > div { display: flex; align-items: center; gap: 6px; }
.mc-ph .ph-dot { width: 6px; height: 6px; }
/* Malaysia's public holidays LUNOX does not observe (2026-10-27): amber, so
   the rose of a LUNOX holiday still means "we are off". */
.ph-banner.gz { background: #fffbeb; border-color: #fde68a; color: #92400e; }
.ph-dot.gz { background: #f59e0b; }
.gz-note { color: #b45309; font-size: .78rem; }
.cal .slot.gz-col, .cal-scroll.gz-day .cal .slot, .mcal-scroll.gz-day .mcal-col { background-color: #fffbeb; }
.hd-ph.gz { color: #b45309; }
.mc-day.gz { color: #b45309; font-weight: 800; position: relative; }
.mc-day.gz::after { content: ""; position: absolute; left: 50%; bottom: 2px; width: 4px; height: 4px; margin-left: -2px; border-radius: 50%; background: #f59e0b; }
.mc-day.gz.sel { color: #fff; }
.mc-day.gz.sel::after { background: #fff; }
.mc-ph > div.gz { color: #92400e; }
/* The holiday strip reads as one sentence that wraps, not three columns. */
.ph-banner { display: block; line-height: 1.45; }
.ph-banner[hidden] { display: none; }
.ph-banner .ph-dot { margin-right: 6px; vertical-align: 1px; }
.ph-banner > span:not(.ph-dot) { margin-left: 3px; }
.mcal-col.gz-col { background-color: #fffbeb; }
/* Sharing Custom Mochi padding balance (2026-10-28). */
.pad-share { border-radius: 10px; padding: 8px 11px; margin: 8px 0 0; font-size: .88rem; }
.pad-share.got { background: var(--ok-bg); color: var(--ok); }
.pad-share.gave { background: var(--warn-bg); color: var(--warn); }
.pad-share a { color: inherit; font-weight: 700; }
.pad-share .muted { color: inherit; opacity: .85; margin-top: 2px; }
.pad-share-tag { font-weight: 800; }
.pad-share-modal { display: grid; gap: 10px; }
.pad-lbl { font-size: .78rem; font-weight: 700; color: var(--muted); }
.pad-givers { display: grid; gap: 6px; }
button.pad-giver { text-align: left; background: #fff; color: var(--text); border: 1.5px solid var(--line); border-radius: 12px; padding: 8px 11px; box-shadow: none; font-weight: 400; min-height: 0; }
button.pad-giver.on { border-color: var(--brand); background: var(--brand-tint); }
.pad-giver strong { color: var(--brand); }
.ok-text { color: var(--ok); }
.pad-share-modal .seg { display: inline-flex; border: 1px solid #d8cfdd; border-radius: 999px; overflow: hidden; width: max-content; }
.pad-share-modal .seg button { border: 0; border-radius: 0; background: #fff; color: var(--brand); box-shadow: none; min-height: 0; padding: 5px 13px; font-weight: 700; }
.pad-share-modal .seg button.on { background: var(--brand); color: #fff; }
.pad-table th { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.pad-consent { background: #faf8fc; border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; font-size: .9rem; }
.pad-consent p { margin: 0 0 6px; } .pad-consent p:last-child { margin: 0; }
.pad-sig-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 640px) { .pad-sig-grid { grid-template-columns: 1fr; } }
.pad-sig { position: relative; border: 1.5px dashed #cdb8d4; border-radius: 12px; background: #fcfaff; }
.pad-sig-canvas { display: block; width: 100%; height: 140px; touch-action: none; cursor: crosshair; }
.pad-sig-hint { position: absolute; left: 0; right: 0; top: 56px; text-align: center; color: var(--muted); font-size: .85rem; pointer-events: none; }
.pad-sig-foot { display: flex; justify-content: space-between; align-items: center; padding: 4px 8px 6px 10px; border-top: 1px solid var(--line); font-size: .8rem; color: var(--muted); }
.pad-sig-view { margin: 0; border: 1px solid var(--line); border-radius: 12px; padding: 6px; text-align: center; }
.pad-sig-view img { width: 100%; max-height: 140px; object-fit: contain; }
.pad-sig-view figcaption { font-size: .8rem; color: var(--muted); }
.pad-sec { border-top: 1px solid var(--line); margin: 6px 0 10px; padding-top: 10px; }
.pad-sec-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pad-sec-head h4 { margin: 0; font-size: .95rem; }
.pad-sec-state { font-weight: 700; font-size: .88rem; }
.pad-sec-head button { margin-left: auto; }
.pad-sec-table { width: 100%; margin-top: 6px; font-size: .88rem; }
.pad-sec-table th { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; text-align: left; }
.pad-sec-table th.num, .pad-sec-table td.num { text-align: right; }
.pad-sec-table a { color: var(--brand); font-weight: 600; }
.pad-sec-table tr.undone td:not(:last-child) { text-decoration: line-through; color: var(--muted); }
.warn-text { color: var(--warn); }
/* Padding balance as its own panel (2026-10-28 second pass). */
.pad-panel { background: #faf7fc; border: 1px solid var(--brand-tint-2); border-radius: 14px; padding: 12px 14px; margin: 12px 0 14px; }
.pad-panel-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pad-panel-title { font-weight: 800; color: var(--brand); font-size: .95rem; }
.pad-pill { display: inline-block; border-radius: 999px; padding: 2px 10px; font-weight: 800; font-size: .8rem; white-space: nowrap; }
.pad-pill.got { background: var(--ok-bg); color: var(--ok); }
.pad-pill.gave, .pad-pill.short { background: var(--warn-bg); color: var(--warn); }
.pad-pill.none { background: var(--neutral-bg); color: var(--muted); }
button.pad-take { margin-left: auto; background: var(--brand); color: #fff; border: 0; box-shadow: none; }
.pad-tx-list { display: grid; gap: 8px; margin-top: 10px; }
.pad-tx { display: flex; align-items: center; gap: 12px; background: #fff; border: 1px solid var(--line); border-radius: 12px; padding: 8px 12px; }
.pad-tx .pad-pill { min-width: 64px; text-align: center; }
.pad-tx-main { flex: 1; min-width: 0; font-size: .9rem; }
.pad-tx-main a { color: var(--brand); font-weight: 700; }
.pad-tx-sub { font-size: .78rem; margin-top: 1px; }
.pad-tx-link { color: var(--brand); font-weight: 700; font-size: .85rem; white-space: nowrap; }
.pad-tx.undone { opacity: .55; } .pad-tx.undone .pad-pill { text-decoration: line-through; }
.pad-empty { margin: 8px 0 0; font-size: .85rem; }
@media (max-width: 640px) { .pad-tx { flex-wrap: wrap; } .pad-tx-link { margin-left: 76px; } button.pad-take { margin-left: 0; width: 100%; } }
/* Ticking which extra pads a padding transfer pays for (2026-10-28). */
.pad-pick-list { display: grid; gap: 6px; }
label.pad-pick { display: grid; grid-template-columns: auto 1fr auto auto; align-items: center; gap: 10px;
  background: #fff; border: 1.5px solid var(--line); border-radius: 12px; padding: 8px 12px; cursor: pointer; font-weight: 400; margin: 0; }
label.pad-pick.on { border-color: var(--brand); background: var(--brand-tint); }
label.pad-pick.off { opacity: .55; cursor: not-allowed; }
label.pad-pick input { width: 18px; height: 18px; margin: 0; accent-color: var(--brand); }
.pad-pick-name { font-weight: 700; }
.pad-pick-price { font-weight: 700; color: var(--brand); }
tr.pad-covered td { color: var(--muted); }
tr.pad-covered .pad-pill { margin-left: 8px; font-size: .72rem; }
/* Padding transfer consent: tablet, later, or paper (2026-10-28). */
.pad-consent-missing { background: var(--bad-bg); color: var(--bad); border-radius: 10px; padding: 8px 11px; font-weight: 700; margin: 0; font-size: .88rem; }
.pad-consent-missing.small { display: inline-block; padding: 2px 9px; font-size: .76rem; margin-top: 4px; border-radius: 999px; }
button.pad-save-pending { background: var(--warn-bg); color: var(--warn); border: 1.5px solid #f3d29a; box-shadow: none; }
.pad-person { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; background: #fff; display: grid; gap: 6px; }
.pad-person.missing { border-color: #f4b8b3; background: #fffafa; }
.pad-person-head { display: flex; gap: 8px; align-items: baseline; }
.pad-person img { width: 100%; max-height: 130px; object-fit: contain; }
.pad-ok { color: var(--ok); font-weight: 700; font-size: .82rem; }
.pad-missing { color: var(--bad); font-weight: 800; font-size: .88rem; }
.pad-paper-mark { font-size: 2.2rem; text-align: center; padding: 18px 0; }
.pad-forms { border: 1px dashed #cdb8d4; border-radius: 12px; padding: 10px 12px; }
.pad-forms-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
/* Both sign on the spot (2026-10-28): tablet, or the printed form photographed. */
.pad-rule { background: var(--brand-tint); color: var(--brand); border-radius: 10px; padding: 8px 11px; margin: 0; font-weight: 600; font-size: .88rem; }
label.pad-paper-tick { display: flex; align-items: center; gap: 6px; font-size: .85rem; font-weight: 600; color: var(--muted); margin: 6px 2px 0; cursor: pointer; }
label.pad-paper-tick input { width: 17px; height: 17px; margin: 0; accent-color: var(--brand); }
.pad-sig.paper { opacity: .35; pointer-events: none; }
.pad-photo-strip { display: flex; gap: 8px; flex-wrap: wrap; }
.pad-photo { position: relative; width: 96px; height: 120px; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: #fff; }
.pad-photo img { width: 100%; height: 100%; object-fit: cover; }
button.pad-photo-x { position: absolute; top: 4px; right: 4px; min-height: 0; width: 22px; height: 22px; padding: 0; border-radius: 50%; background: #fff; color: var(--bad); border: 1px solid var(--line); box-shadow: none; font-size: .7rem; }
/* Recommended, not bought (2026-10-28). */
.rec-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rec-head select { width: auto; min-height: 34px; }
.rec-items { display: grid; gap: 6px; }
.rec-item { display: grid; grid-template-columns: 92px 1fr; column-gap: 8px; align-items: baseline; }
.rec-kind { font-size: .72rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--brand); }
.rec-what { font-weight: 600; }
.rec-why { grid-column: 2; font-size: .8rem; color: var(--warn); }
/* After-sales tabs (2026-10-28): the PV & Orders tab style, each with its count. */
.as-tabs { margin-bottom: 14px; }
.as-count { display: inline-block; min-width: 20px; margin-left: 7px; padding: 1px 7px; border-radius: 999px;
  background: var(--brand); color: #fff; font-size: .72rem; font-weight: 800; text-align: center; }
.pv-tab:not(.on) .as-count { background: #d9c6df; color: var(--brand); }
@media (max-width: 640px) { .as-tabs { width: 100%; } .as-tabs .pv-tab { padding: 7px 11px; font-size: .84rem; } }
/* Recommended, not bought on a phone: one card per visit, items full width. */
@media (max-width: 640px) {
  .rec-table, .rec-table tbody, .rec-table tr, .rec-table td { display: block; width: 100%; }
  .rec-table tr:first-child { display: none; }
  .rec-table tr { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px; }
  .rec-table td { border: 0; padding: 3px 0; }
  .rec-table td:nth-child(3)::before { content: 'Seen by '; color: var(--muted); }
  .rec-table td:last-child { padding-top: 8px; }
  .rec-table td:last-child button { width: 100%; }
  .rec-item { grid-template-columns: 84px 1fr; }
}
/* After-sales tabs, second pass (2026-10-28: "after sales tabs so messy"):
   one panel per tab, a one-line hint, small section heads, quiet secondary
   text, actions right-aligned and the same size everywhere. */
.as-panel { padding-top: 14px; }
.as-panel-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.as-hint { margin: 0; flex: 1; min-width: 240px; color: var(--muted); font-size: .86rem; }
.as-ctrl { display: flex; gap: 8px; align-items: center; }
.as-ctrl select { width: auto; min-height: 34px; font-size: .85rem; }
.as-sec { margin-top: 14px; }
.as-sec + .as-sec { border-top: 1px solid var(--line); padding-top: 12px; }
.as-sec h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: .95rem; color: var(--brand); }
.as-sec-n { min-width: 20px; padding: 1px 7px; border-radius: 999px; background: var(--brand-tint-2); color: var(--brand);
  font-size: .72rem; font-weight: 800; text-align: center; }
.as-sec-ctrl { justify-content: flex-end; margin: -30px 0 6px; }
.as-sub { color: var(--muted); font-size: .76rem; font-weight: 400; }
.as-when { white-space: nowrap; }
.as-empty { margin: 6px 0 2px; color: var(--muted); font-size: .86rem; }
.as-panel table td.as-act { text-align: right; white-space: nowrap; width: 1%; }
.as-panel table td.as-act button { min-height: 32px; }
.as-place { display: flex; gap: 6px; justify-content: flex-end; align-items: center; flex-wrap: wrap; }
.as-place select { width: auto; max-width: 220px; min-height: 32px; font-size: .82rem; }
a.as-case { font-weight: 700; color: var(--brand); text-decoration: none; white-space: nowrap; }
a.as-case:hover { text-decoration: underline; }
@media (max-width: 640px) {
  .as-sec-ctrl { margin: 0 0 6px; justify-content: flex-start; }
  .as-panel table td.as-act { text-align: left; width: auto; }
  .as-place { justify-content: flex-start; }
}
/* "To place" (2026-10-28, "so messy"): the order picker on top, the three
   buttons in one row under it, inside a fixed-width cell that never spills. */
.as-panel table td.as-place-cell { width: 360px; white-space: normal; }
.as-place { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.as-place select { width: 100%; max-width: none; }
.as-place-btns { display: flex; gap: 6px; justify-content: flex-end; }
.as-place-btns button { flex: 1; white-space: nowrap; padding-left: 8px; padding-right: 8px; }
.as-why { margin-top: 3px; color: var(--muted); font-size: .74rem; font-style: italic; }
@media (max-width: 640px) {
  .as-place-table, .as-place-table tbody, .as-place-table tr, .as-place-table td { display: block; width: 100%; }
  .as-place-table tr:first-child { display: none; }
  .as-place-table tr { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px; }
  .as-panel table.as-place-table td { border: 0; padding: 2px 0; width: 100%; }
  .as-panel table.as-place-table td.as-place-cell { padding-top: 8px; }
}
/* After-sales follow-ups kept in the row (2026-10-28): the latest call in
   the row, a box under it with the earlier calls and what happened now. */
.as-seg { display: inline-flex; gap: 4px; padding: 4px; margin: 6px 0 10px; border-radius: 999px; background: var(--brand-tint-2); }
.as-seg button { border: 0; background: transparent; color: var(--brand); font-weight: 700; font-size: .84rem;
  padding: 6px 14px; border-radius: 999px; min-height: 0; box-shadow: none; }
.as-seg button.on { background: #fff; box-shadow: 0 1px 3px rgba(60, 20, 80, .15); }
.as-seg-n { margin-left: 6px; font-size: .72rem; opacity: .75; }
.db-table td { vertical-align: top; }
.db-tag { display: inline-block; margin-bottom: 4px; padding: 2px 9px; border-radius: 999px; font-size: .74rem; font-weight: 700; }
.db-tag.nothing { background: #fdecea; color: var(--bad); }
td.as-fu { width: 260px; }
td.as-fu > button { margin-top: 4px; }
.as-fu-last { font-size: .84rem; }
.as-fu-last.end strong { color: var(--muted); }
.as-fu-note { color: var(--muted); font-size: .78rem; font-style: italic; margin-top: 2px; }
.as-fu-next { font-size: .76rem; color: var(--brand); font-weight: 700; margin-top: 2px; }
.as-fu-next.due { color: var(--bad); }
tr.as-fu-row > td { background: var(--brand-tint-2); border-top: 0; }
.as-fu-box { padding: 4px 2px; }
.as-fu-hist { margin: 0 0 10px; padding-left: 18px; font-size: .82rem; }
.as-fu-hist li { margin: 3px 0; }
.as-fu-hist .as-when { margin-right: 8px; color: var(--muted); }
.as-x { border: 0; background: transparent; color: var(--muted); min-height: 0; padding: 0 6px; box-shadow: none; cursor: pointer; }
.as-fu-q { font-weight: 700; font-size: .84rem; color: var(--brand); margin-bottom: 6px; }
.as-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.as-chip { border: 1px solid var(--line); background: #fff; color: var(--ink, #222); border-radius: 999px; padding: 5px 12px;
  font-size: .82rem; font-weight: 600; min-height: 0; box-shadow: none; }
.as-chip.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.as-fu-form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.as-fu-form input[type=text] { flex: 1; min-width: 220px; }
.as-fu-date { display: flex; flex-direction: column; gap: 2px; }
.as-fu-date input { width: auto; }
@media (max-width: 640px) {
  .db-table, .db-table tbody, .db-table tr, .db-table td { display: block; width: 100%; }
  .db-table tr:first-child { display: none; }
  .db-table tr { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; margin-bottom: 10px; }
  .db-table tr.as-fu-row { margin-top: -14px; border-top: 0; border-radius: 0 0 12px 12px; background: var(--brand-tint-2); }
  .as-panel table.db-table td { border: 0; padding: 3px 0; }
  td.as-fu { width: auto; padding-top: 8px !important; }
  td.as-fu > button { width: 100%; }
  .as-seg { display: flex; width: 100%; }
  .as-seg button { flex: 1; padding: 6px 6px; }
}
@media (max-width: 640px) {
  .as-panel-head .as-ctrl { width: 100%; }
  .as-panel-head .as-ctrl select { flex: 1; min-width: 0; }
  .as-seg button { white-space: nowrap; font-size: .8rem; }
}
/* A status chip inside a page title (Mochi order: "IVZP… — NAME [Collected]")
   inherited the title's see-through gradient text and showed as an empty
   green pill (Giselle, 2026-10-28: "what is the blank green chip?"). */
.content h1 .badge { -webkit-text-fill-color: currentColor; }
/* WhatsApp / Call switch in the follow-up box. */
.as-via { display: inline-flex; gap: 6px; margin-left: 10px; vertical-align: middle; }
.as-chip.small { padding: 3px 10px; font-size: .78rem; }
@media (max-width: 640px) {
  .as-seg { border-radius: 14px; }
  .as-seg button { white-space: normal; line-height: 1.2; font-size: .76rem; border-radius: 10px; min-width: 0; }
  .as-seg-n { display: block; margin: 2px 0 0; }
}
/* Tracking → To send: the courier picker under the number, two actions. */
.as-courier { width: auto; min-height: 28px; font-size: .78rem; margin-top: 4px; padding: 2px 8px; }
.as-send { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: nowrap; }
@media (max-width: 640px) { .as-send { flex-wrap: wrap; } }
@media (max-width: 640px) { .as-send { justify-content: flex-start; } }
/* Unpaid: a little room between the amount and the follow-up column. */
.db-table td.num, .db-table th.num { padding-right: 28px; }
.as-panel-head div.as-hint .as-seg { margin: 0; }
/* Re-assessment tab (2026-10-29): green normal Mochi adjustment, yellow
   standard re-assessment, red serious - the colour runs down the row's edge. */
.re-nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.re-nav > strong { min-width: 130px; text-align: center; color: var(--brand); }
.re-nav .as-seg { margin: 0 0 0 10px; }
.re-legend { display: flex; gap: 6px; flex-wrap: wrap; }
.re-level { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: .72rem; font-weight: 700; margin-top: 3px; }
.re-level.green { background: #e5f4e9; color: #166534; }
.re-level.yellow { background: #fdf3d6; color: #8a5a00; }
.re-level.red { background: #fdeaea; color: #b91c1c; }
.re-table td { vertical-align: top; }
.re-row > td:first-child { border-left: 5px solid transparent; }
.re-row.green > td:first-child { border-left-color: #22a35a; }
.re-row.yellow > td:first-child { border-left-color: #e0a800; }
.re-row.red > td:first-child { border-left-color: #dc2626; }
.re-row.red { background: #fff6f6; }
.re-row.closed { opacity: .55; }
td.re-tick { width: 34px; }
td.re-tick input { width: 20px; height: 20px; cursor: pointer; accent-color: var(--brand); }
.re-dots { display: inline-flex; gap: 6px; margin-top: 4px; }
.re-dot { width: 18px; height: 18px; min-height: 0; padding: 0; border-radius: 50%; border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--line); cursor: pointer; opacity: .45; }
.re-dot.green { background: #22a35a; } .re-dot.yellow { background: #e0a800; } .re-dot.red { background: #dc2626; }
.re-dot.on { opacity: 1; box-shadow: 0 0 0 2px var(--brand); }
td.re-notes { max-width: 320px; }
td.re-notes > button { margin-top: 4px; }
tr.re-box > td { background: var(--brand-tint-2); }
tr.re-box textarea { flex: 1; min-width: 240px; }
.re-carried { margin: 10px 0 16px; padding: 8px 10px; border: 1px solid #f5c2c2; border-radius: 12px; background: #fffafa; }
.re-carried h3 { margin: 0 0 6px; font-size: .9rem; color: #b91c1c; }
@media (max-width: 640px) {
  .re-table, .re-table tbody, .re-table tr, .re-table td { display: block; width: 100%; }
  .re-table tr:first-child { display: none; }
  .re-table tr.re-row { border: 1px solid var(--line); border-radius: 12px; padding: 8px 12px; margin-bottom: 10px; }
  .re-row > td:first-child { border-left: 0; }
  .re-row.green { border-left: 5px solid #22a35a !important; } .re-row.yellow { border-left: 5px solid #e0a800 !important; }
  .re-row.red { border-left: 5px solid #dc2626 !important; }
  .as-panel table.re-table td { border: 0; padding: 3px 0; max-width: none; }
  .re-nav .as-seg { margin: 6px 0 0; }
}
/* Public holidays LUNOX observes, each in its own look (Giselle, 2026-11-01:
   "Got better colour? ... u put some design that's related to the PH").
   A banner with the day's mark, its colours as a soft wash, and the closed
   calendar under a faint diagonal hatch instead of flat pink. */
.ph-t-festive   { --ph-a: #6d28d9; --ph-b: #db2777; --ph-ink: #4c1d95; --ph-tint: #faf5ff; }
.ph-t-xmas      { --ph-a: #15803d; --ph-b: #dc2626; --ph-ink: #14532d; --ph-tint: #f2faf4; }
.ph-t-cny       { --ph-a: #c81e1e; --ph-b: #d4a017; --ph-ink: #7f1d1d; --ph-tint: #fff8ec; }
.ph-t-raya      { --ph-a: #047857; --ph-b: #c9a227; --ph-ink: #064e3b; --ph-tint: #f0fbf5; }
.ph-t-deepavali { --ph-a: #ea580c; --ph-b: #7c3aed; --ph-ink: #7c2d12; --ph-tint: #fff7ed; }
.ph-t-merdeka   { --ph-a: #1e3a8a; --ph-b: #dc2626; --ph-ink: #1e3a8a; --ph-tint: #f4f6ff; }
.ph-t-wesak     { --ph-a: #db2777; --ph-b: #f59e0b; --ph-ink: #831843; --ph-tint: #fff5f9; }
.ph-t-thaipusam { --ph-a: #0e7490; --ph-b: #ca8a04; --ph-ink: #164e63; --ph-tint: #f0fbfd; }
.ph-t-newyear   { --ph-a: #4338ca; --ph-b: #d97706; --ph-ink: #312e81; --ph-tint: #f5f5ff; }
.ph-t-labour    { --ph-a: #b45309; --ph-b: #1d4ed8; --ph-ink: #78350f; --ph-tint: #fffaf0; }

.ph-banner[class*="ph-t-"] {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-radius: 14px; border: 0;
  color: var(--ph-ink);
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--ph-a) 14%, #fff), color-mix(in srgb, var(--ph-b) 12%, #fff));
  box-shadow: inset 4px 0 0 var(--ph-a), 0 1px 3px rgba(0, 0, 0, .05);
}
.ph-banner .ph-icon { font-size: 1.6rem; line-height: 1; flex: none; }
.ph-banner .ph-text { display: flex; flex-direction: column; margin: 0; }
.ph-banner .ph-text strong { font-size: 1rem; color: var(--ph-a); letter-spacing: .01em; }
.ph-banner .ph-sub { font-size: .76rem; opacity: .8; margin: 0; }
/* The closed day: the theme's own wash with a faint hatch, so it reads "off"
   at a glance without shouting. */
.cal-scroll.ph-day[class*="ph-t-"] .cal .slot, .cal .slot.ph-col[class*="ph-t-"] {
  background-color: var(--ph-tint);
  background-image: repeating-linear-gradient(135deg, transparent 0 10px,
    color-mix(in srgb, var(--ph-a) 6%, transparent) 10px 11px);
}
.hd-ph[class*="ph-t-"] { color: var(--ph-a); }
/* "nobody rostered" on the coloured branch heads: readable white, not faded. */
.br-hd .hd-nobody, .br-hd .muted { color: rgba(255, 255, 255, .82); font-weight: 600; }
.ph-banner .ph-deco { margin-left: auto; display: flex; gap: 14px; align-items: center; opacity: .32; pointer-events: none; }
.ph-banner .ph-deco > span { font-size: 1.05rem; }
.ph-banner .ph-deco > span:nth-child(odd) { transform: translateY(-4px) rotate(-12deg); }
.ph-banner .ph-deco > span:nth-child(even) { transform: translateY(4px) rotate(10deg); font-size: .85rem; }
@media (max-width: 640px) { .ph-banner .ph-deco > span:nth-child(n+3) { display: none; } }
.ph-banner > span.ph-deco { margin-left: auto; padding-right: 8px; }

/* Firm latex (2026-11-06): the latex family, a deeper honey so it reads as
   the firmer of the two at a glance. */
.stack .m-firm_latex { background-color: var(--mat-firm-latex); }
.st-chip.mat-firm_latex { background: var(--mat-firm-latex); }
.st-chip.mat-firm_latex.on { color: #5e4a10; background: var(--mat-firm-latex); }
.st-chip.mat-firm_latex:not(.on) { background: color-mix(in srgb, var(--mat-firm-latex) 26%, #fff); }
@media (hover: hover) { .st-chip.mat-firm_latex:not(.on):hover { background: color-mix(in srgb, var(--mat-firm-latex) 55%, #fff); color: #4a4150; } }

/* Mount Austin, Johor (2026-11-07): the fourth clinic's colour. */
.cal .hd.today.br-ma { background: #4338ca; }
.br-tag.br-ma { background: #4338ca; }
.br-chip.on.br-ma { background: #4338ca; border-color: #4338ca; }
.br-plain.br-ma { color: #4338ca; }
.cal-branches .hd.br-ma { background: #4338ca; }
/* Phone calendar while the next day loads (2026-11-08): the old grid dims
   and the heading already names the new day. */
.mcal-loading .mcal-scroll { opacity: .4; transition: opacity .15s; pointer-events: none; }
.mcal-loading .mcal-title .muted { font-style: italic; }

/* CS channel: who the sale is under (2026-11-11). */
.cs-sale { background: var(--brand-tint, #f6ecf8); border-radius: 12px; padding: 12px 14px 14px; margin: 12px 0 8px; }
.cs-sale-q { font-size: .82rem; font-weight: 700; color: var(--brand, #4a0a5e); margin-bottom: 8px; }
.cs-need { font-weight: 400; color: #ba7517; }
.cs-types { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
button.cs-type { display: flex; align-items: center; padding: 10px 14px; border-radius: 10px; border: 1.5px solid #dcc9e3; background: #fff; color: var(--text, #222); cursor: pointer; text-align: left; min-height: 0; box-shadow: none; }
button.cs-type.on { border-color: var(--brand, #4a0a5e); background: var(--brand, #4a0a5e); color: #fff; }
.cs-goes { margin-top: 12px; padding: 9px 12px; background: #fff; border-radius: 8px; font-size: .9rem; }
.shop-note div { margin-top: 4px; }
.shop-note .shop-warn { color: #a3370f; font-weight: 600; }

/* Booked from CS follow-up (2026-11-11). */
button.apx-fu { display: flex; align-items: center; justify-content: flex-start; gap: 12px; width: 100%; padding: 12px 14px; border-radius: 12px; border: 1.5px solid #e2d7e8; background: #fff; color: var(--text, #222); text-align: left; cursor: pointer; min-height: 0; box-shadow: none; }
button.apx-fu.on { border-color: var(--brand, #4a0a5e); background: var(--brand-tint, #f6ecf8); }
.apx-fu-box { flex: 0 0 22px; height: 22px; border-radius: 6px; border: 1.5px solid #b9a6c4; display: flex; align-items: center; justify-content: center; font-weight: 700; color: #fff; }
button.apx-fu.on .apx-fu-box { background: var(--brand, #4a0a5e); border-color: var(--brand, #4a0a5e); }
.apx-fu-sub { display: block; font-size: .8rem; color: #7a6a80; font-weight: 400; }
.cs-hint { margin-top: 10px; font-size: .82rem; line-height: 1.5; color: #6b5a72; }
.cs-hint strong { color: var(--brand, #4a0a5e); font-weight: 600; }
.cs-tick { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: .9rem; font-weight: 600; color: var(--text, #222); cursor: pointer; }
.cs-tick input { width: 18px; height: 18px; margin: 0; flex: none; }
/* Each choice says which sales belong to it, as its second line. */
button.cs-type { flex-direction: column; align-items: flex-start; justify-content: center; gap: 3px; }
.cs-type-sub { font-size: .78rem; font-weight: 400; color: #7a6a80; line-height: 1.35; }
button.cs-type.on .cs-type-sub { color: #eadcf0; }
/* Appointment confirmation preview (2026-11-11). */
.apd-copy-pre { margin: 12px 0 0; padding: 12px 14px; background: #f6f1f8; border-radius: 10px; font: inherit; font-size: .92rem; line-height: 1.5; white-space: pre-wrap; }
.modal.apd .apd-copyslot { display: contents; }
/* Long confirmation text scrolls inside its own box, so Copy / Open WhatsApp stay in view (2026-11-11). */
.apd-copy-pre { max-height: calc(100vh - 300px); overflow-y: auto; }
textarea.apd-copy-pre { display: block; width: 100%; box-sizing: border-box; border: 1px solid transparent; resize: vertical; color: inherit; }
textarea.apd-copy-pre:focus { outline: none; border-color: #c9add9; background: #fff; }
/* Case note photos: upright, with a turn button (2026-11-13). */
.note-att-fig { position: relative; display: inline-block; }
.note-att-turn { position: absolute; right: 6px; bottom: 6px; width: 30px; height: 30px; min-height: 0; padding: 0; border-radius: 50%;
  border: none; background: rgba(255,255,255,.92); color: var(--brand, #4a0a5e); font-size: 16px; line-height: 30px; cursor: pointer;
  box-shadow: 0 1px 4px rgba(0,0,0,.25); }
/* Bill pop-up totals: three tiles (2026-11-13). */
.inv-pop-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin: 4px 0 6px; }
.inv-pop-tile { border: 1px solid var(--line); border-radius: 12px; padding: 10px 14px; background: #fff; display: flex; flex-direction: column; gap: 2px; }
.inv-pop-tile-l { font-size: .78rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted, #7a6a80); }
.inv-pop-tile strong { font-size: 1.15rem; }
.inv-pop-tile.settled { background: #eef8f0; border-color: #cfe9d5; }
.inv-pop-tile.settled strong { color: #1e7a3c; }
.inv-pop-tile.due { background: #fdf0f0; border-color: #f2cfcf; }
.inv-pop-tile.due strong { color: var(--bad, #b42318); }
/* Stacked up and down, in a narrow column so each amount stays near its label (Giselle, 2026-11-13). */
.inv-pop-tiles { grid-template-columns: 1fr; max-width: 340px; gap: 6px; }
.inv-pop-tile { flex-direction: row; align-items: center; justify-content: space-between; padding: 8px 14px; }
.inv-pop-tile strong { font-size: 1.05rem; }
/* Payment rows in the same narrow column as the totals, amount close by (Giselle, 2026-11-13). */
.inv-pop-tiles { max-width: 380px; }
.inv-pop-pays .pay-row { max-width: 380px; box-sizing: border-box; }
/* Allocation left, Totals + Payments right (Giselle, 2026-11-13). */
.inv-pop-foot { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; }
.inv-pop-foot-l { flex: 1 1 220px; min-width: 0; }
.inv-pop-foot-r { flex: 0 1 380px; margin-left: auto; min-width: 0; }
.inv-pop-foot-r .inv-pop-tiles, .inv-pop-foot-r .pay-row { max-width: none; }
/* Full bill page: Balance row shaded (2026-11-13). */
tr.tot-bal.due td { background: #fdf0f0; }
tr.tot-bal.settled td { background: #eef8f0; color: #1e7a3c; }
tr.tot-bal td:first-child { border-radius: 10px 0 0 10px; }
tr.tot-bal td:last-child { border-radius: 0 10px 10px 0; }

/* Working, not seeing patients (2026-11-13). In the day picker a pill beside
   Lead?; on the month squares the name is dashed and paler - working, but
   not on the appointment calendar. */
.rd-np {
  margin-left: auto; flex: none; display: inline-flex; align-items: center;
  padding: 3px 9px; border-radius: 999px; border: 1px dashed var(--line);
  background: var(--card); color: var(--muted); font-size: .72rem; font-weight: 700;
  box-shadow: none; cursor: pointer; min-height: 0;
}
.rd-at + .rd-np { margin-left: 8px; }
.rd-np + .rd-lead { margin-left: 6px; }
@media (hover: hover) { .rd-np:hover { border-color: var(--brand-light); color: var(--text); } }
.rd-np.on { border-style: solid; border-color: var(--brand); background: var(--brand-tint); color: var(--brand); }
.rd-np:disabled { opacity: .5; cursor: not-allowed; }
.rc-who.np { background: transparent; border: 1px dashed var(--pc-line, var(--brand-light)); border-left-width: 3px; opacity: .8; font-style: italic; }

/* ⏰ rostered today, not clocked in to Lunites yet (2026-11-13). */
.hd-clock { margin-left: 4px; font-size: .9em; line-height: 1; }
.hd-who:has(.hd-clock) { box-shadow: inset 0 0 0 1.5px #ef9f27; }

/* WOO and the Lunites tags on the day roster (2026-11-13). */
.rd-np.rd-woo.on { border-color: #1d9e75; background: #e1f5ee; color: #085041; }
.rd-tag { font-size: .68rem; font-weight: 700; border-radius: 999px; padding: 1px 8px; white-space: nowrap; }
.rd-tag:empty { display: none; }
.rd-tag.of { background: var(--brand-tint); color: var(--muted); }
.rd-tag.lv { background: #fcebeb; color: #791f1f; }
.rd-tag.tr { background: #eeedfe; color: #3c3489; }
.rd-train { margin: 0 0 8px; padding: 6px 10px; border-radius: 8px; background: #eeedfe; color: #3c3489; font-weight: 700; font-size: .82rem; }

/* Day roster rows lined up (2026-11-13: "Make it tidy"): the name takes the
   spare room, WOO / Not bookable / Lead? sit in fixed columns on the right. */
.rd-row .rd-name { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px; }
.rd-row .rd-name > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rd-row .rd-at { margin-left: 0; white-space: nowrap; }
.rd-row .rd-np, .rd-row .rd-lead, .rd-at + .rd-np, .rd-np + .rd-lead { margin-left: 0; justify-content: center; }
.rd-row .rd-woo { width: 58px; }
.rd-row .rd-np:not(.rd-woo) { width: 112px; }
.rd-row .rd-lead { width: 80px; }

/* Eng / 中文 on the customer copy and reminder (2026-11-13), bottom left. */
.actions .copy-lang { margin-right: auto; display: inline-flex; padding: 3px; gap: 2px; border-radius: 999px; background: var(--brand-tint); }
.copy-lang button { min-height: 0; padding: 6px 14px; border-radius: 999px; border: 0; background: transparent; color: var(--muted); font-weight: 700; box-shadow: none; }
.copy-lang button.on { background: var(--brand); color: #fff; }

/* Group copy: Booked by left blank (2026-11-13). */
.modal input.bad { border-color: #d93025; box-shadow: 0 0 0 3px #fde8e6; }

/* Copy Confirmation / Copy Reminder (2026-11-13): two equal tiles, own row. */
.modal.apd .apd-copy-row { flex: 1 1 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.modal.apd .apd-copy {
  display: flex; align-items: center; gap: 8px; min-height: 0; padding: 9px 10px; text-align: left;
  border-radius: 14px; border: 0; background: #eaf7f0; color: #4a0a5e; box-shadow: none; cursor: pointer;
}
.modal.apd .apd-copy-ic { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: #25a764; color: #fff; }
.modal.apd .apd-copy-ic svg { width: 17px; height: 17px; }
.modal.apd .apd-copy-txt { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.modal.apd .apd-copy-txt strong { font-size: .8rem; font-weight: 800; white-space: nowrap; }
.modal.apd .apd-copy-txt small { font-size: .7rem; color: var(--muted); font-weight: 600; margin-top: 2px; }
@media (hover: hover) { .modal.apd .apd-copy:hover { background: #d6f0e2; } }
/* Reminder in orange, so the two read apart at a glance (Giselle: B, without outline). */
.modal.apd .apd-copy-row .apd-copy:last-child { background: #fff1dc; }
.modal.apd .apd-copy-row .apd-copy:last-child .apd-copy-ic { background: #ef9f27; }
@media (hover: hover) { .modal.apd .apd-copy-row .apd-copy:last-child:hover { background: #ffe5bf; } }

/* Delivery order Copy Template: the two delivery days side by side (2026-11-13). */
.tpl-days { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

/* Delivery order: "Send to customer" menu (2026-11-13), opens upwards. */
.po-menu { position: relative; display: inline-flex; }
.po-menu-caret { font-size: .75em; opacity: .7; margin-left: 2px; }
.po-menu-list { position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 50; min-width: 290px; padding: 6px;
  background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 12px 32px rgba(40, 10, 60, .18);
  display: flex; flex-direction: column; gap: 2px; }
.po-menu-list[hidden] { display: none; }
.po-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; min-height: 0; text-align: left;
  border: 0; border-radius: 10px; background: transparent; color: var(--text); box-shadow: none; cursor: pointer; }
@media (hover: hover) { .po-menu-item:hover { background: var(--brand-tint); } }
.po-menu-ic { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--brand-tint); color: var(--brand); }
.po-menu-ic svg { width: 16px; height: 16px; }
.po-menu-txt { display: flex; flex-direction: column; line-height: 1.25; }
.po-menu-txt strong { font-size: .86rem; }
.po-menu-txt small { font-size: .72rem; color: var(--muted); }
.po-menu-sep { height: 1px; background: var(--line); margin: 4px 6px; }
.actions .po-menu-list .po-menu-item { justify-content: flex-start; text-align: left; margin: 0; }
.actions .po-menu-list .po-menu-txt { align-items: flex-start; }
/* Case note: when CS should follow up (2026-11-13), under Yes / No on the right. */
.cn-cs-col { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: 0 0 auto; }
.cn-when { display: flex; align-items: center; gap: 8px; padding: 4px 6px 4px 10px; border-radius: 10px; }
.cn-when.owed { background: #fff; box-shadow: 0 0 0 1px #ef9f27 inset; }
.cn-when-sel, .cn-when-month, .cn-when-note { min-height: 32px; border: 1px solid #ef9f27; border-radius: 999px; padding: 4px 12px;
  background: #fff; font: inherit; font-size: .82rem; color: #4a0a5e; font-weight: 600; }
.cn-when-sel { width: 118px; flex: 0 0 auto; }
.cn-when .cn-when-month { width: 150px; flex: 0 0 auto; padding: 4px 8px; }
.cn-when .cn-when-note { width: 150px; flex: 0 0 auto; font-weight: 500; }
.rec-when { font-size: .8rem; color: #4a0a5e; margin-top: 2px; }
.rec-when.asap { color: #b42318; font-weight: 700; }

/* CS team rows (2026-11-13: "This messy leh"): names in line with the
   clinicians' (past the tick box), job title small after the name, Lead? in
   the same right-hand column as the clinicians' Lead?. */
.rd-row.rd-cs-row { padding-left: calc(7px + var(--rd-tick, 20px) + 9px); }
.rd-cs-row .rd-name > span:first-child { font-size: .84rem; font-weight: 600; color: #6d6378; }
.rd-cs-row .rd-role { font-size: .7rem; font-weight: 600; color: var(--muted); white-space: nowrap; }

/* Roster month (2026-11-14): WOO / No booking beside the name, and who is
   off or on leave under the day's names. */
.rc-flag { margin-left: 4px; padding: 0 5px; border-radius: 999px; font-size: .62rem; font-weight: 800; font-style: normal;
  background: var(--brand-tint); color: var(--brand); white-space: nowrap; }
.rc-flag.woo { background: #e1f5ee; color: #085041; }
.rc-away { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 3px; padding-top: 3px; border-top: 1px dashed var(--line); }
.rc-away-who { font-size: .64rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
.rc-away-who small { margin-left: 3px; padding: 0 4px; border-radius: 999px; font-size: .9em; font-weight: 700; background: #f1edf4; }
.rc-away-who.lv small { background: #fcebeb; color: #791f1f; }
.rc-wrap:not(.one) .rc-flag { font-size: .55rem; padding: 0 3px; }
.rc-wrap:not(.one) .rc-flag { display: none; }
.rc-away-n { font-size: .58rem; font-weight: 700; color: var(--muted); }
.rc-n + .rc-away { border-top: 0; margin-top: 0; padding-top: 0; }
.rc-wrap:not(.one) .rc-away-who { font-size: .56rem; }

/* Roster (2026-11-14): calendar on the left, "who's away" always on the right. */
.rc-layout { display: grid; grid-template-columns: minmax(0, 1fr) 250px; gap: 14px; align-items: start; }
.rc-side { position: sticky; top: 0; border: 1px solid var(--line); border-radius: 14px; background: var(--card); padding: 12px; max-height: 72vh; overflow: auto; }
.rc-side-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.rc-side-top strong { font-size: .9rem; color: var(--brand); text-align: center; }
.rc-side-title { margin: 8px 0 6px; font-size: .7rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.rc-side-sec { margin-bottom: 10px; }
.rc-side-h { display: flex; justify-content: space-between; align-items: center; font-size: .78rem; font-weight: 800; padding: 4px 8px; border-radius: 8px; }
.rc-side-h span { font-weight: 700; }
.rc-side-h.lv { background: #fcebeb; color: #791f1f; }
.rc-side-h.of { background: var(--brand-tint); color: var(--brand); }
.rc-side ul { list-style: none; margin: 4px 0 0; padding: 0; }
.rc-side li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 6px; padding: 4px 8px; border-bottom: 1px solid #f2ecf5; font-size: .8rem; }
.rc-side-nm { font-weight: 600; }
.rc-side-dept, .rc-side-why { font-size: .68rem; color: var(--muted); }
.rc-side-why { color: #791f1f; }
.rc-side-none { margin: 4px 8px; font-size: .78rem; }
.rc-side-hint { margin: 6px 0 0; font-size: .7rem; }
.rc-layout .rc-wrap:not(.one) { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 8px; }
@media (max-width: 900px) { .rc-layout { grid-template-columns: 1fr; } .rc-side { position: static; max-height: none; } }

/* Public holidays on the roster month (2026-11-14). */
.rc-day.rc-phday { background: #fff4e5; border-color: #f2c48d; }
.rc-ph { display: block; font-size: .62rem; font-weight: 700; color: #a4510b; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-wrap:not(.one) .rc-ph { font-size: .52rem; }

.rc-side-h.cz { background: #eef3f7; color: #34506b; }

/* Off / Leave / Casual at the foot of a roster square, Lunites style (2026-11-14). */
.rc-away-line { font-size: .66rem; line-height: 1.35; color: var(--muted); }
.rc-away-line b { color: #4a0a5e; font-weight: 800; }
.rc-away-line.lv, .rc-away-line.lv b { color: #a32d2d; }
.rc-away-line.cz b { color: #34506b; }
.rc-day .rc-away { margin-top: auto; }

/* Roster: line between ALL and the branches (2026-11-14). */
.rc-sep { width: 1px; align-self: stretch; min-height: 22px; margin: 0 4px; background: var(--line); }

/* Roster: the date sits at the top of every square, like a normal calendar (2026-11-15). */
.rc-day, button.rc-day { justify-content: flex-start; align-content: flex-start; }

/* CS calendar like the Lunites CS Work Schedule (2026-11-15). */
.rc-n-cs { display: flex; align-items: flex-start; justify-content: space-between; gap: 4px; }
.cs-mgrs { display: flex; gap: 3px; flex-wrap: wrap; justify-content: flex-end; }
.cs-mgr { font-size: .62rem; font-weight: 700; color: #6d6378; background: #f3eef6; border-radius: 999px; padding: 0 6px; white-space: nowrap; }
.cs-chip { display: flex; align-items: center; gap: 4px; background: var(--pc-bg); border-left: 3px solid var(--pc-line); }
.cs-chip .cs-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cs-chip .cs-tm { font-size: .82em; font-weight: 600; color: #6d6378; white-space: nowrap; }
.cs-dots { margin-left: auto; display: inline-flex; gap: 3px; flex: none; }
.cs-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; box-sizing: border-box; border: 1.5px solid; background: transparent; vertical-align: middle; }
.cs-dot.a { border-color: #f0a63a; } .cs-dot.a.on { background: #f0a63a; }
.cs-dot.b { border-color: #5b4bb7; } .cs-dot.b.on { background: #5b4bb7; }
.cs-crown { font-size: .85em; margin-right: 2px; }
.rc-day.rc-phday2 { background: #eef2f6; border-color: #c4cfdb; }
.rc-ph.ph2 { color: #4f6378; }
.cs-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 10px 4px 2px; font-size: .74rem; color: #6d6378; align-items: center; }
.cs-key { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: middle; }
.cs-key.ph { background: #fff4e5; border: 1px solid #f2c48d; } .cs-key.ph2 { background: #eef2f6; border: 1px solid #c4cfdb; }
.cs-chip .cs-nm { flex: 0 0 auto; max-width: 68%; }
.cs-chip .cs-tm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Invoice register Mode details (2026-11-15). */
.inv-mode-d { margin: 1px 0 0 22px; font-size: .74rem; font-weight: 600; color: var(--muted); white-space: normal; }
.inv-mode-amt { margin-left: 6px; font-weight: 600; color: var(--muted); font-size: .76rem; }
@media (min-width: 561px) {
  .inv-row { grid-template-columns: 30px 112px 138px minmax(196px, .8fr) 132px minmax(180px, 1.6fr) 132px; }
  .inv-row.no-acts { grid-template-columns: 30px 112px 138px minmax(196px, .8fr) 132px minmax(180px, 1.6fr); }
}
