/* Genesys V3 · the v2 surface
 *
 * 18 September 2026. The first review said the interface is boring. It is not
 * crude: app.css already carries a token system, a dark theme, a grouped
 * sidebar, a command palette and a contrast pass that was measured rather than
 * eyeballed. What it does not carry is hierarchy. One type size does most of
 * the work, 2 surfaces do all of it, there is no elevation ladder, no motion,
 * and no density setting, so a patient's name, a ward name and a footnote all
 * arrive at the same weight and nothing is ever seen to change.
 *
 * This file is that hierarchy, layered over app.css rather than folded into it.
 * A layer can be removed in 1 line if any of it is wrong on a real screen, and
 * folding it in afterwards is a mechanical job. It should be folded in once it
 * has been used for a week.
 *
 * ---------------------------------------------------------------------------
 * THE CONSTRAINT THAT SHAPED MOST OF WHAT IS BELOW
 *
 * app.css was contrast-solved on 17 September against WCAG AA, and it was
 * solved to the edge: --faint measures 4.55 : 1 on --paper against a
 * requirement of 4.50. So ANY background darker than --paper fails for --faint,
 * --warn, --good, --bad and --brand at once. Measured, not assumed:
 *
 *   well #EDF0F5   faint 4.32   brand 4.46   good 4.38   warn 4.31   bad 4.48
 *   well #F1F4F8   faint 4.47   brand 4.62   good 4.54   warn 4.46   bad 4.64
 *   well #F4F6F9   faint 4.55   brand 4.71   good 4.62   warn 4.55   bad 4.73
 *
 * So in the light theme no surface here is ever darker than --paper. Depth
 * comes from elevation and hairlines rather than from grey fills, which is what
 * this direction wanted anyway. The dark theme has headroom and uses it.
 *
 * The alternative was to re-solve 5 ink tokens 1 day after they were solved,
 * and change the colour of a clinical warning tag twice in a week. That is
 * recorded in docs/PENDING.md instead.
 * ---------------------------------------------------------------------------
 *
 * Nothing here changes a workflow, a role, a label or anything the database
 * refuses. It changes weight, spacing, depth, motion and state.
 */

/* ---------- 1. TOKENS THIS FILE ADDS ---------------------------------
   Nothing measured in app.css is redefined. These are new names only. */
:root{
  /* Wells and floating things. In light, --sunk is exactly --paper: see above. */
  --sunk:#F4F6F9; --hover:#F7F9FB; --raised:#FFFFFF;
  /* Body ink, between --ink and --muted. 9.38 : 1 on paper, 10.16 on surface. */
  --ink2:#33425A;
  --rulestrong:#C9D2DE;
  /* A deeper step of the measured --brand, for text on --brandwash: 6.60 : 1. */
  --brandhi:#0A4FB8;

  --e1:0 1px 2px rgba(20,32,43,.05);
  --e2:0 2px 4px rgba(20,32,43,.05),0 10px 26px rgba(20,32,43,.07);
  --e3:0 8px 16px rgba(20,32,43,.09),0 28px 60px rgba(20,32,43,.14);

  --t-fast:120ms; --t:170ms; --t-slow:280ms;
  --ease:cubic-bezier(.2,.7,.3,1);

  --r-sm:7px; --r-lg:16px;

  /* Density. One attribute on <html> or <body> moves every row in the product. */
  --row-y:13px; --pad:18px; --gap:14px;
}
[data-density="compact"]{ --row-y:8px; --pad:13px; --gap:10px; }

[data-theme="dark"]{
  /* --hover is DARKER than the card in this theme, not lighter: --faint on a
     lighter hover measured 4.20 : 1, below AA. On #0D1823 it is 4.62. */
  --sunk:#0B131E; --hover:#0D1823; --raised:#17222F;
  --ink2:#C2D0E0;
  --rulestrong:#31425A;
  --brandhi:#8CB6FF;
  --e1:0 1px 2px rgba(0,0,0,.5);
  --e2:0 2px 4px rgba(0,0,0,.4),0 10px 26px rgba(0,0,0,.45);
  --e3:0 8px 16px rgba(0,0,0,.5),0 28px 60px rgba(0,0,0,.6);
}

/* ---------- 2. TYPE --------------------------------------------------
   5 steps with intent, and figures that line up. The font files loaded by
   index.html stop at 700, so nothing here asks for a weight that would be
   synthesised by the browser. */
body{ font-size:15px; line-height:1.55; letter-spacing:-.005em; }
h1{ font-size:29px; font-weight:700; letter-spacing:-.03em; line-height:1.14 }
h2{ font-size:21px; font-weight:700; letter-spacing:-.026em; line-height:1.18 }
h3{ font-size:16px; font-weight:600; letter-spacing:-.02em }
.head h1{ font-size:27px }
.head p{ color:var(--muted); font-size:13.5px; margin-top:4px }
.nm{ font-weight:600; font-size:14.5px; letter-spacing:-.012em }
.sub{ font-size:12.5px; color:var(--muted) }
.hint{ font-size:12.5px; color:var(--faint) }
/* Every figure in the product is tabular, so a column of money lines up and a
   temperature does not jump about as it changes. */
.mono,.num,input[type="number"],td .mono,.kv span:last-child{ font-variant-numeric:tabular-nums }

/* ---------- 3. SURFACES AND DEPTH ------------------------------------ */
.card{
  background:var(--surface); border-radius:12px; border:0;
  box-shadow:var(--e1);
  transition:box-shadow var(--t) var(--ease);
}
.card.pad{ padding:var(--pad) }
.pop,.modal,.palette{ border-radius:var(--r-lg); box-shadow:var(--e3) }
.pbanner{ border-radius:12px; box-shadow:var(--e1); border:0; padding:16px var(--pad) }
.pbanner .pname{ font-size:19px; font-weight:700; letter-spacing:-.025em }

/* Rows: one hairline, a hover that is felt rather than seen, and a hit area
   that a thumb can find on a tablet in a ward. */
.row{ padding:var(--row-y) var(--pad); border-bottom:1px solid var(--rule2);
  transition:background var(--t-fast) var(--ease) }
.row:last-child{ border-bottom:0 }
.row:hover{ background:var(--hover) }

/* ---------- 4. CONTROLS ---------------------------------------------- */
.btn{
  border-radius:var(--r-sm); padding:9px 15px; font-size:13.5px; font-weight:600;
  letter-spacing:-.008em; box-shadow:0 1px 2px rgba(20,32,43,.14);
  transition:transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
             box-shadow var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}
.btn:hover{ box-shadow:0 2px 10px color-mix(in srgb,var(--brand) 32%,transparent) }
.btn:active{ transform:translateY(1px) }
.btn.sm{ padding:6px 11px; font-size:12.5px; border-radius:6px }
.btn.quiet{ box-shadow:inset 0 0 0 1px var(--rule) }
.btn.quiet:hover{ background:var(--hover); box-shadow:inset 0 0 0 1px var(--rulestrong) }
.btn[disabled],.btn[aria-disabled="true"]{ box-shadow:none }

/* The tag was set in IBM Plex Mono, which is why a ward full of them read as
   a terminal. The mono face stays where it belongs, which is figures. */
.tag{ font-family:"Inter Tight",system-ui,sans-serif; border-radius:999px; font-weight:600;
  letter-spacing:-.005em; padding:3px 9px; font-size:11.5px }
.tag .mono,.tag.mono{ font-family:"IBM Plex Mono",ui-monospace,monospace }

/* The field, which is where a prompt box used to be. The focus ring is the
   whole of the feedback and it has to be unmistakable at arm's length. */
.fld{
  border-radius:var(--r-sm); border:0; box-shadow:inset 0 0 0 1px var(--rule);
  padding:10px 12px; font-size:15px; background:var(--surface);
  transition:box-shadow var(--t) var(--ease);
}
.fld:focus{ outline:0; box-shadow:inset 0 0 0 2px var(--brand) }
.fld.bad{ box-shadow:inset 0 0 0 2px var(--bad) }
/* The field label was mono, uppercase and letter-spaced, which is a label on
   a machine rather than on a form somebody fills in at a bedside. */
.lbl{ font-family:"Inter Tight",system-ui,sans-serif; font-size:12.5px; font-weight:600;
  color:var(--ink2); letter-spacing:0; text-transform:none }

/* ---------- 5. THE SHELL --------------------------------------------- */
.topbar{ box-shadow:var(--e1); border-bottom:1px solid var(--rule) }
.side{ background:var(--surface) }
.side nav button{
  border-radius:var(--r-sm); font-size:13.5px; font-weight:500; position:relative;
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.side nav button:hover{ background:var(--hover) }
/* The current screen is marked on the rail as well as tinted, so it is findable
   without relying on a tint that a bright ward window washes out. */
.side nav button[aria-current="true"]{ background:var(--brandwash); color:var(--brandhi); font-weight:600 }
.side nav button[aria-current="true"]::before{
  content:""; position:absolute; left:-10px; top:50%; translate:0 -50%;
  width:3px; height:18px; border-radius:0 3px 3px 0; background:var(--brand);
}
.side .grp button{ letter-spacing:.1em; font-size:10.5px; text-transform:uppercase; color:var(--faint) }
.side .count{ font-variant-numeric:tabular-nums; border-radius:999px; font-weight:600 }
.jump{
  border-radius:var(--r-sm); background:var(--sunk); box-shadow:inset 0 0 0 1px transparent;
  transition:box-shadow var(--t) var(--ease), background var(--t) var(--ease);
}
.jump:hover{ background:var(--surface); box-shadow:inset 0 0 0 1px var(--rulestrong) }

/* The strips that say what the link is doing. They are information, so they
   read as information rather than as an error. */
.strip{ font-size:13px }
.strip .stripin{ padding-top:9px; padding-bottom:9px }

/* The priority list at the top of the home screen. Drawn from the alert feed,
   which until 18 September was visible only inside the bell's popover. The rail
   on the left carries the severity, so the row is readable as urgent without
   reading it, and the word is on the tag as well because colour alone is never
   the carrier. */
.needs .need{ width:100%; text-align:left; gap:12px; align-items:flex-start; background:none }
.needs .need:hover{ background:var(--hover) }
.needs .need .sev{ width:3px; align-self:stretch; border-radius:3px; background:var(--rulestrong); flex:none }
.needs .need.crit .sev{ background:var(--bad) }
.needs .need.warn .sev{ background:var(--warn) }
.needs .need .nm{ display:block }

/* ---------- 6. THE 4 STATES EVERY SCREEN MUST DRAW -------------------- */
/* Empty. A sentence in the middle of a card is not an empty state: it does not
   say what would put something there. */
.empty{ padding:40px 20px; text-align:center; color:var(--muted); font-size:13.5px }
.empty b{ display:block; color:var(--ink); font-family:Archivo,system-ui,sans-serif;
  font-size:15px; font-weight:600; letter-spacing:-.02em; margin-bottom:5px }
.empty .art{
  width:70px; height:70px; margin:0 auto 14px; border-radius:var(--r-lg);
  background:var(--sunk); display:grid; place-items:center; color:var(--faint);
}

/* Loading. A skeleton says the shape of what is coming. "Loading" says nothing,
   and reads identically to a screen that finished and found nothing. */
.skel{
  background:linear-gradient(90deg,var(--sunk) 25%,var(--rule2) 37%,var(--sunk) 63%);
  background-size:400% 100%; animation:gv3skel 1.4s ease infinite;
  border-radius:6px; height:12px; display:block;
}
.skel + .skel{ margin-top:8px }
@keyframes gv3skel{ 0%{background-position:100% 50%} 100%{background-position:0 50%} }

/* ---------- 7. MOTION -------------------------------------------------
   5 movements, all under 300ms, all on the same curve, and none of them on
   anything a clinician has to read. */
.card,.row,.btn,.tag,.fld,.jump{ will-change:auto }
@keyframes gv3rise{ from{opacity:0;transform:translateY(6px)} to{opacity:1;transform:none} }
main > *{ animation:gv3rise var(--t-slow) var(--ease) both }
.pop,.modal{ animation:gv3rise var(--t) var(--ease) both }
@media(prefers-reduced-motion:reduce){
  main > *,.pop,.modal{ animation:none }
  .skel{ animation:none }
}

/* ---------- 8. FACES --------------------------------------------------
   8 tints picked from the name by web/polish.js, all measured against both
   themes. 20 patients stop being 20 identical blue circles, and a face becomes
   something the eye can find in a list. */
.avatar{ font-weight:700; letter-spacing:-.01em }
.avatar[data-tint="0"]{ background:#E8F0FE; color:#134BAB }
.avatar[data-tint="1"]{ background:#E4F4F0; color:#0A6055 }
.avatar[data-tint="2"]{ background:#FAEFE2; color:#8A5110 }
.avatar[data-tint="3"]{ background:#F1ECFC; color:#4F33A3 }
.avatar[data-tint="4"]{ background:#FCEBEF; color:#9B2B45 }
.avatar[data-tint="5"]{ background:#E9F2E6; color:#375F28 }
.avatar[data-tint="6"]{ background:#FBEFE8; color:#914526 }
.avatar[data-tint="7"]{ background:#E7F0F6; color:#1B4E6B }
[data-theme="dark"] .avatar[data-tint="0"]{ background:#152A47; color:#8CB6FF }
[data-theme="dark"] .avatar[data-tint="1"]{ background:#0E2C28; color:#4FD8C0 }
[data-theme="dark"] .avatar[data-tint="2"]{ background:#2E2314; color:#F0BA63 }
[data-theme="dark"] .avatar[data-tint="3"]{ background:#221A3C; color:#B49BFF }
[data-theme="dark"] .avatar[data-tint="4"]{ background:#33161F; color:#FF8FA6 }
[data-theme="dark"] .avatar[data-tint="5"]{ background:#182A14; color:#8FCE7C }
[data-theme="dark"] .avatar[data-tint="6"]{ background:#2F1B12; color:#F0A277 }
[data-theme="dark"] .avatar[data-tint="7"]{ background:#12283A; color:#7FBCE0 }

/* ---------- 9. THE SIGN IN PAGE ---------------------------------------
   The one screen that must never fail, and the one an investor sees first.
   app.css puts a light panel behind the WebGL rings and fades it into --paper
   at the bottom. This makes that panel dark in both themes, which is what gives
   the page its weight, and web/signin3d.js now always lights the rings for a
   dark panel rather than following the interface theme.

   Everything below degrades to a plain dark panel and a working form: if the
   module fails, if WebGL is absent, or if this file never loads, the form is
   still the form. */
.mid{ min-height:100dvh }
.scene{
  background:
    radial-gradient(120% 90% at 74% 14%, #1B5FD8 0%, transparent 56%),
    radial-gradient(110% 80% at 10% 92%, #0A2E6B 0%, transparent 60%),
    linear-gradient(160deg,#0B1B36 0%,#07122A 58%,#050D1E 100%);
}
/* app.css fades the bottom of the scene into --paper so the rings sit on the
   page. The panel is dark now, so the fade goes to the panel's own base. */
.scene::after{ background:linear-gradient(to top, #050D1E 10%, transparent) }
.scene .tagline h2{
  font-family:Archivo,system-ui,sans-serif; font-weight:700;
  font-size:clamp(25px,3.1vw,34px); letter-spacing:-.035em; line-height:1.1; color:#fff;
}
.scene .tagline p{ color:rgba(234,242,254,.74); font-size:15px }
/* The wordmark on the dark panel, if a screen ever puts one there, is the
   inverted one the dark theme already uses. */
.scene .wordmark{ filter:invert(1) hue-rotate(180deg) brightness(1.4) }

.formside{ background:var(--surface) }
.signin{ max-width:380px }
.signin h1{ font-size:25px; letter-spacing:-.03em }
.signin .fld{ width:100% }
.signin .lbl{ display:block; margin-bottom:6px }
/* The password field carries a reveal, because a password typed wrong 3 times
   on a ward tablet with wet gloves locks an account for 15 minutes. */
.signin .pwwrap{ position:relative; display:block }
.signin .pwwrap .fld{ padding-right:64px }
.signin .pwwrap .rev{
  position:absolute; right:8px; top:50%; translate:0 -50%;
  font-size:12px; font-weight:600; color:var(--muted); padding:5px 7px; border-radius:6px;
}
.signin .pwwrap .rev:hover{ color:var(--brand); background:var(--sunk) }
/* display:flex would beat the browser's own [hidden] rule, so it is said here
   explicitly. Without this the caps lock warning is on the screen permanently,
   which is how a warning stops being read. */
.signin .caps{ display:flex; align-items:center; gap:6px; margin-top:7px;
  font-size:12.5px; color:var(--warn) }
.signin .caps[hidden]{ display:none }
.signin .netnote{ margin-top:14px; font-size:13px }
.signin .netnote[hidden]{ display:none }

/* ---------- 10. PHONES AND WALLS --------------------------------------
   A nurse carries a phone. Rows get taller rather than smaller, because a
   thumb is 9mm wide and a row that is comfortable on a mouse is a miss on a
   ward round. */
@media(max-width:640px){
  :root{ --row-y:15px; --pad:15px }
  body{ font-size:15.5px }
  .head h1{ font-size:23px }
  .btn{ padding:11px 16px }
  .btn.sm{ padding:8px 12px }
  .card{ border-radius:12px }
  .row{ gap:10px }
}
/* A board on a corridor wall is read at 3 metres, not at 60 centimetres. */
@media(min-width:1800px){
  :root{ --content:1560px }
}

/* ---------- 11. THE RAIL, AND THE HEAD OF EVERY SCREEN ----------------
 * 18 September 2026, second pass. The first pass was restraint: 2px of corner
 * radius, half a point of type, shadows instead of borders. All of it correct,
 * and added together a person who uses the product every day could not see it.
 * A change nobody notices is not a change.
 *
 * So the shell carries the brand now. The rail is the same deep navy as the
 * sign in panel, the content sits white against it, and the head of every
 * screen is a band rather than a bare line of text. That is the difference you
 * see from the doorway, and it touches no workflow, no label and no role.
 *
 * Every colour on the rail was measured against it rather than chosen:
 *
 *   rail #0C1A33      nav item #C8D6E8  11.76 : 1    group label #8AA0BC  6.47
 *                     active  #FFFFFF   17.34        count       #9FB3CC  8.09
 *   active fill is white at 14%, which composites to #2E3A50:
 *                     nav item on it    7.75         active on it        11.43
 */
.side{
  background:linear-gradient(180deg,#0D1C37 0%,#0A1730 60%,#08132A 100%);
  border-right:0;
  box-shadow:inset -1px 0 0 rgba(255,255,255,.06);
}
[data-theme="dark"] .side{
  background:linear-gradient(180deg,#0A1220 0%,#070E1A 100%);
  box-shadow:inset -1px 0 0 rgba(255,255,255,.05);
}
.side nav button{ color:#C8D6E8 }
.side nav button svg{ opacity:.72 }
.side nav button:hover{ background:rgba(255,255,255,.08); color:#fff }
.side nav button:hover svg{ opacity:1 }
.side nav button[aria-current="true"]{
  background:rgba(255,255,255,.14); color:#fff; font-weight:600;
}
.side nav button[aria-current="true"] svg{ opacity:1 }
.side nav button[aria-current="true"]::before{ background:#5B96FF }
.side nav .count{ color:#9FB3CC }
.side nav button[aria-current="true"] .count{ color:#fff }
.side .grp{ color:#8AA0BC }
.side .grp button{ color:#8AA0BC }
.side .grp button:hover{ color:#fff }
.side .foot{ border-top:1px solid rgba(255,255,255,.1) }
.side .foot .who{ color:#8AA0BC }
/* The search box on the rail: a well in the navy rather than a card on paper. */
.jump{
  background:rgba(255,255,255,.07); color:#C8D6E8;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.09);
}
.jump:hover{ background:rgba(255,255,255,.13); color:#fff; box-shadow:inset 0 0 0 1px rgba(255,255,255,.2) }
.jump kbd{ background:rgba(255,255,255,.1); color:#9FB3CC; border-color:transparent }
.side .navscrim{ background:rgba(8,19,42,.5) }

/* The head of a screen becomes a band: the title, what it is about, and the
   actions, on their own surface rather than floating on the page. */
main > .head{
  background:var(--surface); border-radius:14px; box-shadow:var(--e1);
  padding:20px var(--pad); margin-bottom:var(--gap);
  display:flex; align-items:center; gap:16px; flex-wrap:wrap;
}
main > .head h1{ font-size:27px; margin:0 }
main > .head p{ margin-top:5px }
main > .head .spacer{ flex:1 }

/* Faces are 38px in a list. A face the eye can find is the whole reason for
   giving it a colour. */
.row > .avatar{ width:38px; height:38px; font-size:13px }
.pbanner .avatar.lg{ width:58px; height:58px; font-size:19px }

/* The top bar pill that says how many things need this person. It is the ward
   tab's shape on purpose: 2 pills side by side, one for the ward and one for
   you, and neither of them is a bell nobody opens. */
.needtab{
  display:flex; align-items:center; gap:8px; padding:6px 12px; border-radius:999px;
  border:1px solid transparent; font-size:12.5px; font-weight:600; letter-spacing:-.01em;
  background:color-mix(in srgb,var(--bad) 13%,transparent);
  border-color:color-mix(in srgb,var(--bad) 40%,transparent); color:var(--bad);
  transition:background var(--t-fast) var(--ease);
}
.needtab:hover{ background:color-mix(in srgb,var(--bad) 20%,transparent) }
.needtab .pip{ width:9px; height:9px; border-radius:99px; background:currentColor; flex:none }
.needtab.amber{
  background:color-mix(in srgb,var(--warn) 14%,transparent);
  border-color:color-mix(in srgb,var(--warn) 40%,transparent); color:var(--warn);
}
.needtab.amber:hover{ background:color-mix(in srgb,var(--warn) 21%,transparent) }
@media(max-width:1150px){ .needtab .say{ display:none } }

/* ---------- 12. WARM ------------------------------------------------
 * 18 September 2026, third pass, and the brief for it was 1 word: homely.
 *
 * The product was built on a cool blue-grey paper, #F4F6F9, which is what
 * every piece of clinical software in the world is built on and is why they all
 * feel like a form somebody makes you fill in. This makes the paper warm and
 * leaves the clinical colours exactly where the contrast pass put them.
 *
 * THE PALETTE WAS RE-SOLVED RATHER THAN RE-PICKED. docs/PENDING.md recorded
 * that app.css had no contrast headroom left and that re-solving it was a
 * deliberate job of its own. This is that job, done the same way: hue and
 * saturation held, lightness walked until every pair clears 4.5 : 1, and the
 * result written down. Only 2 ink tokens had to move at all, and both by a
 * hair.
 *
 *   paper #FAF8F4   surface #FFFFFF   surface2 #FCFAF6
 *   well  #F5F2EA   hover   #FDFCFA
 *
 *            paper surface surface2 well  hover  own wash
 *   faint    4.77  5.06    4.86     4.52  4.94         was #637282, now #627080
 *   muted    5.14  5.46    5.23     4.88  5.32         unchanged
 *   brand    4.80  5.10    4.89     4.56  4.97   4.52  unchanged
 *   good     4.78  5.07    4.87     4.53  4.95   4.58  unchanged
 *   warn     4.79  5.08    4.88     4.54  4.96   4.66  was #A36100, now #A05F00
 *   bad      4.82  5.12    4.91     4.57  4.99   4.51  unchanged
 *
 * White on the 3 filled colours: brand 5.10, bad 5.12, good 5.07.
 *
 * The dark theme is NOT warmed in this pass. Warming a dark theme means
 * re-solving all of its pairs too, and doing that in the same hour as this
 * would be 2 palettes changed at once with 1 set of eyes on them.
 */
/* 18 SEPTEMBER 2026, LATER THE SAME DAY. THIS SELECTOR IS THE WHOLE OF ONE FIX.
 *
 * This block was written as a plain `:root`. `:root` and `[data-theme="dark"]`
 * have the SAME specificity, 1 pseudo-class against 1 attribute, so the later
 * declaration wins, and this block is later in this file than the dark block at
 * the top of it and later again than app.css. So every token named here
 * overrode the dark theme's own value.
 *
 * Measured in a browser rather than argued about: with data-theme="dark" set,
 * --paper came back #FAF8F4 and --surface #FFFFFF while --ink stayed #EAF2FA.
 * That is near white text on a white card, 1.13 : 1. The dark theme has been
 * unreadable since the warm palette shipped.
 *
 * `:root:not([data-theme="dark"])` is 2 simple selectors against 1, so it still
 * beats app.css in the light theme, and it does not match at all in the dark
 * one. The dark theme goes back to the values app.css measured for it.
 *
 * The radii moved OUT of this block, up to the shared :root, because a corner
 * is not a colour and the 2 themes should not round differently.
 */
:root{ --r:14px; --r-sm:9px; --r-lg:20px; }

:root:not([data-theme="dark"]){
  --paper:#FAF8F4; --surface:#FFFFFF; --surface2:#FCFAF6;
  --sunk:#F5F2EA; --hover:#FDFCFA; --raised:#FFFFFF;
  --faint:#627080;
  --warn:#A05F00;
  /* Warm rules, so a hairline on warm paper is warm rather than blue. */
  --rule:#E7E2D9; --rule2:#F0EBE2; --rulestrong:#D5CEC2;
  /* A shadow the colour of the paper's own shade rather than of a blue sky. */
  --e1:0 1px 2px rgba(60,48,34,.06);
  --e2:0 2px 4px rgba(60,48,34,.05),0 10px 26px rgba(60,48,34,.08);
  --e3:0 8px 16px rgba(60,48,34,.10),0 28px 60px rgba(60,48,34,.16);

  /* THE SECOND ACCENT. It is furniture and welcome, never state, and nothing
     clinical is ever this colour. It was terracotta for a few hours on
     18 September and is a deep steel blue now, which is a hue of the product's
     own family rather than a second family beside it: 8.45 : 1 on paper, 8.96
     on white, 7.75 on its own wash, and white on it is 8.96.

     It is deliberately NOT --brand. Blue means "you can press this" everywhere
     in this product, so the accent is darker and duller than the action blue,
     and it is never used on anything pressable. */
  --warm:#1E4B7A; --warmwash:#E9EFF7; --warmline:#D3DEEC;
}

/* The same accent solved for the dark theme, because scoping the block above to
   the light theme left --warm, --warmwash and --warmline undefined in dark, and
   an undefined custom property is not a fallback, it is nothing: the welcome
   band, the empty states and the round would have drawn with no colour at all.
   A steel blue has to be LIGHT on a dark ground to be the same idea, so this is
   the same hue walked the other way rather than the light value reused.

   Measured against this theme's own grounds:
     --warm on --warmwash  7.28     --warm on --paper    8.98
     --warm on --surface   8.37     --warm on --sunk     8.90
     #08111C on --warm     9.05  (which is why a filled chip flips to dark text)

   This is NOT the dark theme warmed. That is a separate job, on the list, and it
   means re-solving every pair in app.css rather than these 3. */
[data-theme="dark"]{
  --warm:#8FB6E8; --warmwash:#16263C; --warmline:#2A4468;
}
.card{ border-radius:16px }
.btn{ border-radius:10px }
.btn.sm{ border-radius:8px }
.fld{ border-radius:10px }
main > .head{ border-radius:18px }
.pbanner{ border-radius:18px }

/* ---------- 13. THE WELCOME ------------------------------------------
 * The home screen opens with a greeting, and until now that greeting was a
 * line of text the same size as a ward name. It is the first thing anybody
 * sees every morning, so it gets a band of its own.
 *
 * `data-screen` on the root element is set by web/polish.js from the shell's
 * own state, so a screen can be styled without the shell being rewritten.
 */
[data-screen="home"] main > .head{
  background:
    radial-gradient(72% 130% at 90% 0%, color-mix(in srgb,var(--warm) 30%,#fff) 0%, transparent 60%),
    linear-gradient(118deg, #FFFFFF 0%, #F3F7FD 38%, #E2ECF8 100%);
  box-shadow:var(--e1), inset 0 0 0 1px var(--warmline);
  padding:34px var(--pad) 32px;
  position:relative; overflow:hidden;
}
[data-screen="home"] main > .head h1{
  font-size:clamp(28px,3.4vw,38px); letter-spacing:-.037em; line-height:1.08;
}
[data-screen="home"] main > .head p{ font-size:14.5px; color:var(--ink2) }
/* A quiet sun over the corner of the band. It is drawn from the warm accent
   and nothing else: no illustration library, no stock picture of a doctor. */
[data-screen="home"] main > .head::after{
  content:""; position:absolute; right:-40px; top:-60px; width:220px; height:220px;
  border-radius:50%; pointer-events:none;
  background:radial-gradient(circle at 34% 34%, color-mix(in srgb,var(--warm) 22%,transparent) 0%, transparent 62%);
}

/* The welcome band holds the greeting on the left and a shortcut on the right,
   and the 2 keep apart when the band is narrow rather than the button landing
   under the greeting. */
[data-screen="home"] main > .head{ align-items:flex-end; gap:20px }
[data-screen="home"] main > .head .who{ min-width:0 }
[data-screen="home"] main > .head .who p{ margin-top:8px }
.btn.shortcut{
  display:inline-flex; align-items:center; gap:9px;
  padding:12px 18px; font-size:14.5px; border-radius:12px; flex:none;
  box-shadow:0 2px 10px color-mix(in srgb,var(--brand) 30%,transparent);
}
.btn.shortcut svg{ opacity:.9 }
@media(max-width:760px){
  [data-screen="home"] main > .head{ align-items:flex-start }
  [data-screen="home"] main > .head .spacer{ display:none }
  .btn.shortcut{ width:100% ; justify-content:center }
}

/* An empty state gets a shape rather than only a sentence, unless the screen
   already drew its own mark. */
.empty:not(:has(.art))::before{
  content:""; display:block; width:54px; height:54px; margin:0 auto 14px;
  border-radius:18px; background:var(--warmwash);
  box-shadow:inset 0 0 0 1px var(--warmline);
}
.empty .art{ background:var(--warmwash); box-shadow:inset 0 0 0 1px var(--warmline); color:var(--warm) }

/* The welcome band's gradient stops are literal colours rather than tokens,
   because a band is a picture and not a surface. That means they do not follow
   the theme on their own, and in dark the band was white with near white text
   on it. The same 3 steps, walked down instead of up. --ink on the lightest of
   them is 13.22 : 1 and --ink2 is 9.53. */
[data-theme="dark"] [data-screen="home"] main > .head{
  background:
    radial-gradient(72% 130% at 90% 0%, color-mix(in srgb,var(--warm) 22%,transparent) 0%, transparent 60%),
    linear-gradient(118deg, #101A26 0%, #0E1B2C 38%, #132840 100%);
}

/* The eyebrow over a panel. It was in the accent for a few hours and it is
   back to a neutral: a heading set in a strong blue reads as something you can
   press, and in this product blue means exactly that. */
.card .mode, main .mode{
  color:var(--faint); font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-size:10.5px; letter-spacing:.14em; text-transform:uppercase;
}

/* Tiles on the home screen: warmer, rounder, and the number is the loudest
   thing in them. */
.tile{ border-radius:14px; transition:transform var(--t) var(--ease), box-shadow var(--t) var(--ease) }
.tile:hover{ transform:translateY(-2px); box-shadow:var(--e2) }
.tnum{ letter-spacing:-.04em }

/* The strip that says what the link is doing sits on warm paper now. */
.strip{ border-bottom:1px solid var(--rule) }

/* A face gets a soft ring, so a list of people reads as people. */
.avatar{ box-shadow:0 0 0 2px var(--surface), 0 0 0 3px var(--rule2) }
.side .avatar, .topbar .avatar{ box-shadow:none }

/* ===========================================================================
   THE WARD ROUND
   18 September 2026. round.js mounts over the application rather than into a
   screen, because a round is a mode and not a page: while it is running it owns
   the screen, 1 bed at a time.

   z-index 160 is chosen and not arbitrary. app.css has topbar 50, pop 60,
   navscrim 140, side drawer 150, scrim 200, toasts 300. The round has to sit
   above the navigation and BELOW the modal backdrop, because the round note is
   a modal opened from inside the round, and below the toasts, because a refusal
   during a round is the one message that must be readable.

   Everything here is scoped to .roundhost except .roundgo, which is the button
   on the ward board, so none of it can reach another screen.
   =========================================================================== */

.roundhost{
  position:fixed; inset:0; z-index:160;
  background:var(--paper);
  overflow:auto;
  animation:roundin 180ms var(--ease);
}
@keyframes roundin{ from{ opacity:0; transform:translateY(8px) } to{ opacity:1; transform:none } }

.roundwrap{
  max-width:940px; margin:0 auto; padding:20px 16px 90px;
  display:flex; flex-direction:column; gap:16px; min-height:100%;
}
.roundwrap:focus{ outline:none }

/* --- The head ----------------------------------------------------------- */
.roundtop{
  display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap;
  padding-bottom:14px; border-bottom:1px solid var(--rule);
}
.roundtitle{ flex:1 1 220px; min-width:0 }
.roundtitle h2{ font-size:22px; letter-spacing:-.02em; margin:2px 0 }
.roundtitle .sub.mono{ letter-spacing:.09em; text-transform:uppercase; font-size:10.5px }
.roundcount{ text-align:right; line-height:1.15 }
.roundcountn{
  display:block; font-family:"IBM Plex Mono", monospace;
  font-size:30px; font-weight:600; letter-spacing:-.04em; color:var(--warm);
}
.roundacts{ display:flex; gap:8px; align-items:center; margin-left:auto }
.roundshut{ margin:0 }

/* --- The strip of beds --------------------------------------------------
   Every bed on the ward, in the order they are walked, with its state on its
   face. This is the thing that answers "where am I" without reading anything.
   ------------------------------------------------------------------------ */
.roundbar{
  display:flex; gap:6px; flex-wrap:wrap;
  padding:12px; border-radius:var(--r-lg);
  background:var(--sunk); border:1px solid var(--rule2);
}
.roundchip{
  min-width:38px; height:34px; padding:0 9px;
  border-radius:9px; border:1px solid var(--rule);
  background:var(--surface); color:var(--muted);
  font-family:"IBM Plex Mono", monospace; font-size:12.5px; font-weight:600;
  display:inline-flex; align-items:center; justify-content:center;
  transition:transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.roundchip:hover{ transform:translateY(-1px) }
.roundchip.is-seen{ background:var(--goodwash); border-color:transparent; color:var(--good) }
.roundchip.is-skipped{ background:var(--warnwash); border-color:transparent; color:var(--warn) }
.roundchip.is-at{
  background:var(--warm); border-color:var(--warm); color:#fff;
  box-shadow:0 0 0 3px var(--warmwash);
}
[data-theme="dark"] .roundchip.is-at{ color:#08111C }

/* --- The bed ------------------------------------------------------------ */
.roundbody{ flex:1 }
.roundcard{
  background:var(--surface); border:1px solid var(--rule);
  border-radius:var(--r-lg); padding:22px 22px 20px;
  box-shadow:var(--e2);
}
.roundcard.is-done{ background:var(--surface2) }
.roundcardtop{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:14px }
.roundbed{
  font-family:"IBM Plex Mono", monospace; font-size:12px; font-weight:600;
  letter-spacing:.06em; text-transform:uppercase;
  background:var(--warmwash); color:var(--warm);
  border-radius:99px; padding:4px 11px;
}
.roundconcern{
  font-size:11px; font-family:"IBM Plex Mono", monospace;
  padding:3px 9px; border-radius:99px; border:1px solid currentColor;
}
.roundmark{ font-size:11.5px; font-weight:600 }

.roundname{ font-size:26px; letter-spacing:-.025em; margin:0 }
.roundmeta{ font-size:13px; color:var(--muted); margin:3px 0 18px }

.roundwhy{
  background:var(--sunk); border-radius:var(--r);
  padding:13px 15px; margin-bottom:14px;
}
.roundwhy p:last-child{ font-size:14.5px; line-height:1.5 }

.roundfacts{ display:grid; grid-template-columns:1fr 1fr; gap:12px }
.roundfacts > div{
  border:1px solid var(--rule2); border-radius:var(--r);
  padding:11px 13px;
}
.roundfacts p:last-child{ font-size:15px; font-weight:600 }

.roundhost .lab{
  font-size:10.5px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--faint); font-weight:600; margin-bottom:4px; display:block;
}
.roundnote{
  margin-top:13px; font-size:13.5px; line-height:1.5;
  border-left:3px solid var(--rule); padding-left:12px;
}
.roundhost .t-ok{ color:var(--good) }
.roundhost .t-warn{ color:var(--warn) }
.roundhost .t-bad{ color:var(--bad) }
.roundhost .t-none{ color:var(--faint) }

/* --- The foot -----------------------------------------------------------
   Fixed, because the round is walked with a thumb on a tablet held in one hand
   and the 2 buttons that matter must not move as the card above them changes
   height from one patient to the next.
   ------------------------------------------------------------------------ */
.roundfoot{
  position:fixed; left:0; right:0; bottom:0; z-index:1;
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background:var(--surface); border-top:1px solid var(--rule);
  box-shadow:0 -2px 18px rgba(20,32,43,.06);
}
[data-theme="dark"] .roundfoot{ box-shadow:0 -2px 18px rgba(0,0,0,.45) }

@media (max-width:640px){
  .roundwrap{ padding:16px 12px 120px }
  .roundname{ font-size:22px }
  .roundcount{ text-align:left }
  .roundacts{ margin-left:0; width:100% }
  .roundfacts{ grid-template-columns:1fr }
  .roundfoot{ justify-content:stretch }
  .roundfoot .spacer{ display:none }
  .roundfoot .btn{ flex:1 1 auto }
}

/* --- The button on the ward board ---------------------------------------
   It was a .btn.quiet, which is this product's secondary styling, and the
   person using it said it was not really visible. On the ward board, walking
   the round is the primary thing a doctor or a nurse comes to the screen for,
   and it was drawn quieter than Admit.

   It is --brand and not the second accent. The accent is furniture, never
   state, and never on anything pressable: that rule is set at the top of this
   file and the first pass broke it on the running state. */
.roundgo{
  display:inline-flex; align-items:center; gap:9px; white-space:nowrap;
  padding:11px 17px; font-size:14px; border-radius:11px;
  box-shadow:0 2px 10px color-mix(in srgb,var(--brand) 30%,transparent);
}
.roundgo svg{ opacity:.95 }
.roundgo.is-live{ padding-right:9px }
.roundgoq{
  font-size:11.5px; font-weight:700; letter-spacing:.02em; text-transform:uppercase;
  background:rgba(255,255,255,.22); border-radius:99px; padding:3px 9px;
}
[data-theme="dark"] .roundgoq{ background:rgba(0,0,0,.18) }
.roundpulse{
  width:8px; height:8px; border-radius:99px; background:currentColor;
  animation:roundlive 1.8s infinite; flex:none;
}
@keyframes roundlive{ 0%,100%{ opacity:1 } 50%{ opacity:.35 } }

/* On a narrow ward card the button drops to its own line at full width rather
   than squeezing the bed count off the end. */
@media (max-width:620px){
  .roundgo{ width:100%; justify-content:center }
}

/* ===========================================================================
   A WARNING THAT INTERRUPTS ONCE
   18 September 2026. See web/warn.js for why a sentence under a card is not a
   warning. This sits in the corner, takes no focus, and closes on its own
   button or on Escape.

   z-index 250 puts it above the ward round (160) and the modal backdrop (200),
   because a unit running unobserved outranks whatever is on the screen, and
   below the toasts (300), which are the reply to something just pressed.
   =========================================================================== */

.warnhost{
  position:fixed; right:16px; bottom:16px; z-index:250;
  display:flex; flex-direction:column; gap:10px;
  width:min(380px, calc(100vw - 32px));
  pointer-events:none;
}
.warncard{
  pointer-events:auto; position:relative;
  background:var(--surface); border:1px solid var(--bad);
  border-left-width:4px;
  border-radius:var(--r); padding:14px 40px 12px 14px;
  box-shadow:var(--e3);
  display:grid; grid-template-columns:auto 1fr; gap:4px 11px;
  animation:warnin 200ms var(--ease);
}
@keyframes warnin{ from{ opacity:0; transform:translateY(10px) } to{ opacity:1; transform:none } }
.warncard.is-going{ opacity:0; transform:translateY(6px); transition:opacity 150ms var(--ease), transform 150ms var(--ease) }
.warnmark{ color:var(--bad); display:flex; padding-top:1px }
.warncard p{ margin:0; font-size:13.5px; line-height:1.5; color:var(--ink) }
.warnacts{ grid-column:2; display:flex; gap:7px; margin-top:10px }
.warnx{
  position:absolute; top:8px; right:8px; width:26px; height:26px;
  display:grid; place-items:center; border-radius:7px; color:var(--muted);
}
.warnx:hover{ background:var(--hover); color:var(--ink) }

@media (max-width:640px){
  .warnhost{ left:12px; right:12px; bottom:12px; width:auto }
}
