/* =========================================================================
   app.css — the screens: the registry, the generated encounter form, admin.

   Everything here dresses markup that `renderer.js` produced from the dictionary, so the
   selectors are structural (`.grp`, `.field`, `.row-card`, `.rec-item`) and never name a data
   group. A stylesheet that had to know what NCDQANS looks like would be the same architecture
   mistake as a renderer that did.

   The vocabulary is OLD's — cards, pills, tables, notes, empty states, the teal/coral pairing
   for "suggested" versus "required". Screens OLD never had (the generated form, the A.3 panel,
   the Konsulta admin cards, the import and cipher-key cards) are drawn in that same language so
   they read as though they had always belonged to it.
   ========================================================================= */

/* ================================================================ REGISTRY */
.search-card .row{align-items:flex-end}
.search-card .field{min-width:260px;flex:1}
.new-pin{margin-top:14px;padding-top:14px;border-top:1px dashed var(--line)}
.new-pin .field{min-width:200px}
.results{overflow:auto;-webkit-overflow-scrolling:touch}
.results table{min-width:840px}

/* ---- the patient list (P22) ------------------------------------------------
   OLD's registry row, ported: a teal monogram tile, the name split across two columns with a
   quiet second line under each, the two derived columns a front desk actually uses (last exam,
   next appointment), and the two verbs as pills on the right. Everything here is the theme's
   own vocabulary — `.avatar`, `.pill`, `.btn.xs`, the table's own hairlines — so the list and
   the chart it opens read as one screen. */
.section-h{display:flex;align-items:center;gap:10px;margin:24px 0 12px;flex-wrap:wrap}
.section-h h2{font-size:var(--fs-input);font-weight:800;letter-spacing:-.01em}
.section-h .count{background:var(--line-2);color:var(--ink-2);border-radius:var(--pill);
  padding:1px 10px;font-size:var(--fs-xs);font-weight:800}
.section-h .spacer{flex:1}

.cell-person{display:flex;align-items:center;gap:10px;min-width:0}
.cell-person>span:not(.avatar){min-width:0}
.cell-person .nm{display:block;font-weight:800;line-height:1.25;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* OLD put the member's BARANGAY here. YAKAP3's registry has no address column at all — the Rev.14
   dictionary carries none and `PATIENT_COLS` has none — so this line carries the one registry
   fact that belongs under a name at a Konsulta desk: which effectivity year they are enlisted
   for. Inventing an address to fill OLD's slot would have been the one thing worse than an
   empty one (D-113). */
.cell-person .sub{display:block;font-size:var(--fs-xs);color:var(--ink-3);font-weight:700;line-height:1.3;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.results td .tiny{color:var(--ink-3);font-weight:700;margin-top:2px}
.results td.tact .row{gap:8px;flex-wrap:nowrap;justify-content:flex-end;align-items:center}
/* P22 — the rows arrive in sequence rather than as a slab, at OLD's curve and a third of its
   length: a registry is a list somebody is scanning, and a long entrance on a list is a list
   that cannot be scanned. Only the first eight are staggered; after that the delay is flat, so
   a hundred-row answer does not spend two seconds assembling itself. */
.results tbody tr{animation:riseIn .3s var(--ease-out) both}
.results tbody tr:nth-child(2){animation-delay:.03s}
.results tbody tr:nth-child(3){animation-delay:.06s}
.results tbody tr:nth-child(4){animation-delay:.09s}
.results tbody tr:nth-child(5){animation-delay:.12s}
.results tbody tr:nth-child(6){animation-delay:.15s}
.results tbody tr:nth-child(7){animation-delay:.18s}
.results tbody tr:nth-child(n+8){animation-delay:.21s}

/* P16 moved the patient's encounter list out of the registry and onto the chart's Encounters
   tab, where it is drawn as `.chart-item`/`.enc-row` (chart.css). The `.timeline`/`.tl-*` rules
   that dressed the old list were left behind styling markup nothing emits any more, and P17
   removed them: dead CSS is not free — it is the next reader's wrong answer. */

/* ================================================================ ENCOUNTER */
/* P25: the room owns the viewport, so the page it lives on is full-bleed. The reading-width cap
   this used to carry belonged to a form on a page with a sidebar beside it; there is no sidebar
   while a room is open, and the drawer sets its own width. */
.enc{max-width:none;margin:0}
.enc-h{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:20px}
.enc-title h1{font-size:var(--fs-2xl);font-weight:800;letter-spacing:-.02em}
.enc-meta{display:flex;gap:8px;flex-wrap:wrap;align-items:center;color:var(--ink-2);
  font-size:var(--fs-xs);font-weight:700;margin-top:5px}
.enc-actions{margin-left:auto;display:flex;gap:9px;flex-wrap:wrap}
/* "Join call" is a real link (a new tab, `rel=noopener`) wearing the button's clothes. The reset
   that takes an anchor's underline and visited colour off it lives with `.btn` in theme.css since
   P17 — it was scoped to this one row, and `.media-thumb` and the admin screens' anchors needed
   it too. */
/* P16 moved the group navigator INTO the right rail, where V3-PLAN §Phase 16 puts the DD-group
   timeline — so the room is now the form and one rail, not a form between two of them. */
.enc-body{display:grid;grid-template-columns:minmax(0,1fr) 348px;gap:20px;align-items:start}

/* ---- the DD-group timeline: a rail, in the sidebar's idiom but on a light surface ---- */
/* Forty-three groups is longer than any display, so the sections collapse and the rail scrolls
   inside itself rather than running off the bottom of a sticky column. */
.enc-nav{display:flex;flex-direction:column;gap:2px;margin-bottom:14px;
  padding:12px 10px;border-radius:var(--r);background:var(--surface);
  border:1px solid var(--line);box-shadow:var(--shadow),var(--hi-soft)}
.nav-h{font-size:var(--fs-2xs);font-weight:900;letter-spacing:var(--tracking-caps);text-transform:uppercase;
  color:var(--ink-3);padding:2px 8px 8px}
.rail-sec{display:flex;flex-direction:column;gap:2px;padding-bottom:4px}
.rail-sec + .rail-sec{border-top:1px dashed var(--line-2);padding-top:5px}
.rail-h{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:0;background:none;
  cursor:pointer;border-radius:9px;text-align:left;
  font:900 var(--fs-2xs)/1.3 var(--font-sans);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--ink-3)}
.rail-h:hover{background:var(--line-2);color:var(--ink-2)}
.rail-h:focus-visible{outline:2px solid var(--teal-500);outline-offset:1px}
/* One symbol, rotated. Two different glyphs (`▸`/`▾`) have two different advance widths, so
   the label beside them shifted sideways on every toggle. P17. */
.rail-caret{flex:none;display:inline-flex;color:var(--ink-3);transition:transform .16s ease}
.rail-caret .i{width:12px;height:12px}
.rail-caret.open{transform:rotate(90deg)}
.rail-h:hover .rail-caret{color:var(--ink-2)}
/* The back-to-chart chevron points the other way; same symbol, no second asset. */
.btn .i.flip{transform:rotate(180deg)}
.rail-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rail-h .pill{flex:none;padding:1px 7px;font-size:var(--fs-2xs);font-family:var(--font-mono)}
.nav-item{display:flex;align-items:center;gap:8px;padding:6px 10px;border:0;background:none;cursor:pointer;
  border-radius:9px;font:800 var(--fs-xs)/1.35 var(--font-sans);color:var(--ink-2);text-align:left;width:100%;
  transition:background-color .16s,color .16s}
.nav-item:hover:not(:disabled){background:var(--teal-50);color:var(--teal-ink)}
.nav-item:focus-visible{outline:2px solid var(--teal-500);outline-offset:1px}
/* A group the rules did not open is SHOWN, greyed: "the engine decided you do not need this"
   is an answer, and a rail that hid it could not give it. */
.nav-item.is-closed{opacity:.5;cursor:default}
.nav-item:disabled{cursor:default}
.nav-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);
  font-size:var(--fs-2xs);letter-spacing:.01em}
.nav-state{flex:none;font:700 var(--fs-2xs)/1 var(--font-sans);letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-3)}
.nav-item .pill{flex:none;padding:1px 7px;font-size:var(--fs-2xs)}

/* ---- P25: the documentation DRAWER (D-117) ----------------------------------------------
   v1's own geometry and timings, ported off `truemed-yakap/public/css/encounter.css`: a
   full-viewport scrim that dims and blurs the room, and a panel that slides in from the right
   edge on `transform .38s cubic-bezier(.22,1,.36,1)` — which is this app's `--ease-out`, so the
   drawer moves on the same curve as every other entrance here. `translateX(102%)` rather than
   100% is v1's too: the two extra percent take the shadow off-canvas with the panel.

   Two things v1 did NOT have are added rather than ported (see D-117): the panel is
   `visibility:hidden` while parked — so it is out of the accessibility tree and cannot widen
   the page, which v1 had to fix with a global `overflow-x:hidden` — and Escape and the scrim
   both dismiss, refusing while there are unsaved edits.

   The panel is deliberately OUTSIDE `.room`, so the light form tokens apply to it: the dark
   palette stays scoped where D-114 put it, and a documentation sheet is a reading-and-typing
   surface. The room keeps painting behind the dim, which is what makes this never read as a
   page change. */
.enc-doc-scrim{position:fixed;inset:0;z-index:var(--z-sheet);background:var(--scrim);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  opacity:0;pointer-events:none;transition:opacity .3s}
.enc-doc-scrim.show{opacity:1;pointer-events:auto}
.enc-doc{position:fixed;top:0;right:0;bottom:0;width:min(1120px,100%);
  z-index:calc(var(--z-sheet) + 10);display:flex;flex-direction:column;
  background:var(--bg);border-left:1px solid var(--line);
  box-shadow:-30px 0 70px rgba(0,0,0,.45);
  transform:translateX(102%);visibility:hidden;will-change:transform;
  transition:transform .38s var(--ease-out),visibility 0s linear .38s}
.enc-doc.open{transform:none;visibility:visible;
  transition:transform .38s var(--ease-out),visibility 0s}
.enc-doc>.enc-h{margin:0;padding:13px 22px;gap:14px;
  border-bottom:1px solid var(--line);background:var(--surface);
  box-shadow:0 4px 18px rgba(23,28,38,.06)}
.enc-doc>.enc-body{flex:1;min-height:0;overflow:auto;padding:20px 22px 44px;
  -webkit-overflow-scrolling:touch}
.enc-doc .enc-findings{position:sticky;top:0;max-height:none}
.enc-doc .enc-nav{margin-bottom:0}
.enc-nav-park:empty{display:none}
.enc-nav-park{margin-bottom:14px}

.enc-form{min-width:0}
/* The rail is three hosts that repaint on their own schedules — the group timeline, the engine's
   findings, the media panel. This is the middle one; it had no rule at all until P17. */
.enc-findings{position:sticky;top:18px;max-height:calc(100vh - 42px);overflow:auto;-webkit-overflow-scrolling:touch}
.enc-findings-list{display:block;min-width:0}
/* The unsaved mark on "Save draft": the app's own dot pill, not a bullet in the label. */
.btn .save-dirty{font-size:var(--fs-2xs);padding:1px 8px;gap:5px;margin-left:2px}
.btn .save-dirty .pdot{width:5px;height:5px}

/* ---- generated group sections: OLD's card, with the dictionary's own name on the header ---- */
.grp{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow),var(--hi-soft);margin-bottom:18px;overflow:hidden}
.grp-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:13px 18px;
  border-bottom:1px solid var(--line-2);
  background:linear-gradient(180deg,#fff,var(--neutral-50));position:sticky;top:0;z-index:2}
/* P25: the heading is now the group's CURATED title, which is a phrase, so it is set in the
   UI face. The dictionary's own identifier moved to `.grp-dd` beside it — still on screen,
   still in mono, because an evaluator has to be able to get from the words back to the DD row.
   A group nobody curated has no `.grp-dd` and its `h2` is the identifier, as before. */
.grp-h h2{font-size:var(--fs);font-weight:900;letter-spacing:-.01em;color:var(--ink)}
.grp-h>.i{width:17px;height:17px;flex:none;color:var(--teal-600)}
.grp-dd{flex:none;font-size:var(--fs-2xs);font-weight:800;letter-spacing:.03em;color:var(--ink-3);
  padding:2px 8px;border-radius:var(--pill);background:var(--surface-2);border:1px solid var(--line-2)}
.grp-h .pill{flex:none}
.grp-blurb{margin:0;padding:12px 18px 0;font-size:var(--fs-xs);font-weight:600;line-height:1.5;
  color:var(--ink-2);max-width:86ch}
.grp-b{padding:18px}
.grp-b>.empty{padding:22px}
.grp-b>.btn{margin-top:4px}
/* a group-level finding sits between the header and the body, full-bleed */
.grp>.field-warn,.grp>.field-err{margin:0;border-radius:0;border-left:0;border-right:0;padding:11px 18px}

/* ---- P25: a group's fields, in the curated sections ---- */
/* With no curation there is exactly one unnamed section holding every parameter in the
   dictionary's own order, which is what this form was before — so these rules cost an
   uncurated group nothing but a wrapper. */
.grp-fields{display:flex;flex-direction:column;gap:18px}
.form-sec{display:flex;flex-direction:column;gap:10px}
.form-sec-t{font-size:var(--fs-2xs);font-weight:900;letter-spacing:var(--tracking-caps);text-transform:uppercase;
  color:var(--teal-ink)}
.form-sec-n{font-size:var(--fs-xs);font-weight:650;color:var(--ink-3);line-height:1.5;
  margin-top:-6px;max-width:86ch}

/* ---- a row of a repeating group ---- */
.row-card{border:1px solid var(--line);border-radius:var(--r-sm);padding:14px;margin-bottom:12px;
  background:var(--surface-2)}
.row-card:last-of-type{margin-bottom:14px}
.row-head{display:flex;align-items:center;gap:10px;margin-bottom:12px}
.row-n{font:900 var(--fs-2xs)/1 var(--font-mono);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--ink-3);
  background:var(--line-2);border-radius:var(--pill);padding:4px 9px}
.row-head .btn{margin-left:auto}

/* ---- a field, its marks and the engine's words about it ---- */
.field-label{font-weight:700}
/* P25: the label row carries the friendly name on the left and the DD's parameter name on the
   right, so the two never fight for the same start position. */
.field>label{display:flex;align-items:baseline;gap:6px;flex-wrap:wrap}
.field>label .req,.field>label .sug{margin-left:0}
.field-dd{margin-left:auto;font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:700;
  letter-spacing:.01em;color:var(--ink-3);opacity:.85}
.field-hint{font-size:var(--fs-xs);font-weight:600;color:var(--ink-3);line-height:1.5;margin-top:5px}
.field-warn,.field-err{font-size:var(--fs-xs);font-weight:700;border-radius:var(--r-sm);
  padding:8px 11px;margin-top:6px;line-height:1.45;display:block}
.field-warn{color:var(--warning-dark);background:var(--warning-light);border:1px solid var(--warn-line)}
.field-err{color:var(--error-dark);background:var(--error-light);border:1px solid var(--danger-line)}
/* P25's humanizer: the plain sentence is what a clinician reads, and PhilHealth's own wording
   collapses underneath it. The citation is never removed — it is what an evaluator checks, and
   it is the same string Phase 6's 422 body carries. */
.finding-plain{display:block;font-weight:800}
.finding-cite{margin-top:6px}
.finding-cite>summary{cursor:pointer;list-style:none;font-size:var(--fs-2xs);font-weight:900;
  letter-spacing:var(--tracking-caps);text-transform:uppercase;opacity:.72}
.finding-cite>summary::-webkit-details-marker{display:none}
.finding-cite>summary::before{content:"▸ ";display:inline-block;transition:transform .16s ease}
.finding-cite[open]>summary::before{content:"▾ "}
.finding-cite>summary:hover{opacity:1}
.finding-cite-b{margin-top:5px;font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:600;
  line-height:1.55;opacity:.92;overflow-wrap:anywhere}
/* a field the dictionary pairs with the one above it (a value and its unit, a code and its text) */
.field.paired{padding-left:13px;border-left:2px solid var(--teal-100)}

/* who wrote the row, and when */
.stamps{grid-column:1/-1;display:flex;gap:16px;flex-wrap:wrap;padding-top:11px;margin-top:2px;
  border-top:1px dashed var(--line);font-size:var(--fs-2xs);color:var(--ink-3)}
.stamp{display:inline-flex;gap:6px;align-items:baseline}
.stamp-k{font-weight:900;text-transform:uppercase;letter-spacing:var(--tracking-caps);font-size:var(--fs-2xs);color:var(--ink-3)}
.stamp-v{font-weight:700;color:var(--ink-2)}

/* ---- library combobox: OLD's typeahead, on a light surface ---- */
.combo{position:relative}
.combo-bar{display:flex;align-items:center;gap:7px}
.combo-input{flex:1;min-width:0}
.combo-code{font-size:var(--fs-2xs);font-weight:800;color:var(--teal-700);background:var(--teal-50);
  border:1px solid var(--teal-100);border-radius:6px;padding:4px 8px;min-width:28px;text-align:center;flex:none}
.combo-clear{border:1px solid var(--line);background:var(--surface);border-radius:8px;width:28px;height:28px;
  cursor:pointer;color:var(--ink-3);font-size:var(--fs-md);line-height:1;flex:none;padding:0;transition:.15s}
.combo-clear:hover{border-color:var(--danger-line);color:var(--danger);background:var(--danger-soft)}
/* P21 — OLD's dropdown entrance (`admin.css` `@keyframes ddIn`), on the control YAKAP3 actually
   has. A menu that appears instantly under a moving cursor reads as a mis-click; 220ms of drop
   is what says "this came from the box you are typing in". */
.combo-menu{position:absolute;z-index:var(--z-menu);left:0;right:0;top:calc(100% + 5px);max-height:270px;overflow:auto;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow-lg);padding:4px;-webkit-overflow-scrolling:touch;
  animation:menuIn .22s var(--ease-out)}
@keyframes menuIn{from{opacity:0;transform:translateY(-6px) scale(.98)}to{opacity:1;transform:none}}
.combo-opt{display:flex;gap:10px;width:100%;text-align:left;border:0;background:none;cursor:pointer;
  padding:9px 11px;border-radius:var(--r-sm);font:650 var(--fs-sm)/1.35 var(--font-sans);color:var(--ink);
  transition:background-color var(--t-state),color var(--t-state),transform var(--t-press)}
/* OLD's `dd-item` nudge: the row under the pointer leans toward the reader by 2px. */
.combo-opt:hover{background:var(--teal-50);color:var(--teal-ink);transform:translateX(2px)}
.combo-opt .mono{color:var(--teal-700);font-weight:800;min-width:40px;flex:none}
.combo-desc{flex:1;min-width:0}
.combo-empty{padding:12px 13px;font-size:var(--fs-xs);font-weight:700;color:var(--ink-3)}
/* A badge the library layer attached to a row — "Konsulta" for a PhilHealth benefit drug,
   "Stocked" for one this clinic holds (P14). Coral because it is a fact about the row, not a
   state of the control. */
.combo-badge{flex:none;align-self:center;font-size:var(--fs-2xs);font-weight:800;letter-spacing:.02em;
  color:var(--coral-700);background:var(--coral-50);border:1px solid var(--coral-100);
  border-radius:var(--pill);padding:2px 8px;white-space:nowrap}
.combo-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:7px}
.chip-code{display:inline-flex;align-items:center;gap:7px;background:var(--teal-50);color:var(--teal-700);
  border:1px solid var(--teal-100);border-radius:var(--pill);padding:3px 5px 3px 10px;font-size:var(--fs-xs);font-weight:800}
.chip-desc{font-weight:650;color:var(--ink-2);max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chip-x{border:0;background:none;cursor:pointer;color:var(--teal-700);font-size:var(--fs-sm);line-height:1;padding:0 3px}
.chip-x:hover{color:var(--danger)}

/* ---- the A.3 recommended-services panel ----------------------------------
   A card in its own right, teal because the whole panel is a suggestion: nothing in it can
   block a finalize, and its rows are answered rather than filled in. */
.panel.recommend{margin:0 18px 18px;border:1px solid var(--teal-line);border-radius:var(--r);
  background:linear-gradient(180deg,var(--teal-50),rgba(255,255,255,.92));padding:16px;
  box-shadow:var(--hi-soft)}
.grp-h + .panel.recommend{margin-top:18px}
.panel-h{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:7px}
.panel-h h3{font-size:var(--fs-md);font-weight:900;color:var(--teal-ink)}
.panel-note{margin:0 0 14px;font-size:var(--fs-sm);font-weight:650;color:var(--teal-ink);line-height:1.5;max-width:72ch}
.panel.recommend > .field-warn{margin:0 0 12px}
.rec-list{display:flex;flex-direction:column;gap:9px}
.rec-item{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-sm);padding:12px 14px;
  box-shadow:var(--hi-soft)}
.rec-title{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.rec-code{background:var(--teal-50);color:var(--teal-700);border:1px solid var(--teal-100);border-radius:6px;
  padding:2px 8px;font-weight:800;font-size:var(--fs-2xs);flex:none}
.rec-name{font-weight:800;font-size:var(--fs-sm);line-height:1.35}
.rec-answers{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:11px}
.rec-answers .field{margin-bottom:0}
.rec-extra{margin-top:11px;padding-top:11px;border-top:1px dashed var(--line-2)}
/* A-15: services the guide names that no PhilHealth library codes — shown, never guessed at. */
.rec-unmapped{margin-top:14px;padding:11px 13px;border-radius:var(--r-sm);background:var(--warning-light);
  border:1px solid var(--warn-line);color:var(--warning-dark);font-size:var(--fs-xs);font-weight:700;line-height:1.5}
.rec-unmapped .field-help{color:var(--warning-dark);opacity:.85}

/* ---- the NCD questionnaire ---- */
.ncdq-section{font-size:var(--fs-2xs);font-weight:900;letter-spacing:var(--tracking-caps);text-transform:uppercase;
  color:var(--teal-ink);margin:20px 0 10px;padding-top:14px;border-top:1px solid var(--line-2)}
.ncdq-section:first-child{margin-top:0;padding-top:0;border-top:0}
.ncdq-q{display:grid;grid-template-columns:minmax(0,1fr) 190px;gap:10px 16px;align-items:start;
  padding:11px 0;border-bottom:1px solid var(--line-2)}
.ncdq-q:last-child{border-bottom:0}
/* The question and its answer are pinned to the first row, so a finding rendered BETWEEN them
   (the renderer emits ask → warning → answer) drops to a row of its own instead of shunting the
   answer control into the question's column. */
.ncdq-ask{display:flex;gap:10px;align-items:baseline;grid-column:1;grid-row:1}
.ncdq-n{font-weight:900;color:var(--teal-700);font-size:var(--fs-2xs);font-family:var(--font-mono);flex:none;
  min-width:22px;text-align:right}
.ncdq-text{font-size:var(--fs-sm);font-weight:650;line-height:1.5}
.ncdq-a{margin-bottom:0;grid-column:2;grid-row:1}
.ncdq-q .field-warn,.ncdq-q .field-err,.ncdq-extra{grid-column:1/-1}
.ncdq-extra{padding-left:32px;border-left:2px solid var(--teal-100);margin-left:2px}

/* ---- the findings rail: the engine's own words, never paraphrased ---- */
.findings{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow),var(--hi-soft);margin-bottom:14px;overflow:hidden}
.findings-h{display:flex;align-items:center;gap:9px;padding:12px 14px;border-bottom:1px solid var(--line-2)}
.findings-h h3{font-size:var(--fs-2xs);font-weight:900;letter-spacing:var(--tracking-caps);text-transform:uppercase}
.findings-h .pill{margin-left:auto}
.findings.errors{border-color:var(--danger-line)}
.findings.errors .findings-h{background:var(--error-light);color:var(--error-dark)}
.findings.warnings{border-color:var(--warn-line)}
.findings.warnings .findings-h{background:var(--warning-light);color:var(--warning-dark)}
.finding{padding:10px 14px;border-bottom:1px solid var(--line-2)}
.finding:last-child{border-bottom:0}
.finding-jump{display:block;width:100%;text-align:left;border:0;background:none;cursor:pointer;padding:0;font:inherit}
.finding-jump:focus-visible{outline:2px solid var(--teal-500);outline-offset:2px;border-radius:4px}
.finding-where{display:block;font-size:var(--fs-2xs);font-weight:800;color:var(--teal-700);font-family:var(--font-mono);
  letter-spacing:.02em}
.finding-msg{display:block;font-size:var(--fs-xs);font-weight:650;line-height:1.45;color:var(--ink);margin-top:3px}
.finding-jump:hover .finding-msg{text-decoration:underline}
.finding-rule{display:inline-block;margin-top:5px;font-size:var(--fs-2xs);font-weight:800;color:var(--ink-3);
  font-family:var(--font-mono);background:var(--line-2);border-radius:var(--pill);padding:1px 8px}
.finding .btn{margin-top:8px}

/* ---- the media panel: the §V.D.5 signature and the §V.D.2 photos (P13, D-100/D-101) ---- */
/* It sits under the findings in the same rail, in the findings card's idiom — the same surface,
   the same header type — because it is the other thing a clinician does at the end of a visit,
   not a different kind of screen. */
.media-panel{display:block;min-width:0}
.enc-media{margin-top:14px}
.media-consent{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:12px}
.media-consent .btn{margin-left:auto}
.media-consent .media-meta{width:100%;margin:0}
.media-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow),var(--hi-soft);margin-bottom:14px;padding:14px}
.media-h{display:flex;align-items:center;gap:9px;margin-bottom:11px}
.media-h h3{font-size:var(--fs-2xs);font-weight:900;letter-spacing:var(--tracking-caps);text-transform:uppercase}
.media-h .pill{margin-left:auto}
.media-empty{font-size:var(--fs-xs);font-weight:650;line-height:1.45;color:var(--ink-3);margin-bottom:11px}
.media-meta{font-size:var(--fs-2xs);font-weight:700;color:var(--ink-3);line-height:1.45;margin:8px 0 11px}
/* The stored signature is a trimmed PNG on a transparent background: it needs a light ground of
   its own, or dark ink on a dark surface disappears. */
.media-sig-img{display:block;width:100%;max-height:120px;object-fit:contain;object-position:left center;
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r-sm);padding:6px}
.media-strip{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:11px}
/* Every other clickable row in this app answers to the pointer and to the keyboard; the photo
   thumbnails were the one that did not. P17. */
.media-thumb{display:block;width:72px;text-decoration:none;color:var(--ink-3);border-radius:var(--r-sm);
  transition:transform .14s,color .16s}
.media-thumb:hover{transform:translateY(-1px);color:var(--teal-ink)}
.media-thumb:hover img{border-color:var(--teal-500)}
.media-thumb:focus-visible{outline:2px solid var(--teal-500);outline-offset:2px}
.media-thumb img{display:block;width:72px;height:72px;object-fit:cover;border-radius:var(--r-sm);
  border:1px solid var(--line);transition:border-color .16s}
.media-thumb-at{display:block;font-size:var(--fs-2xs);font-weight:800;margin-top:3px;font-family:var(--font-mono)}
/* The file input is the label: it opens the camera on a phone and the picker everywhere else.
   The input inside it is the focusable thing, so the LABEL has to show the focus — otherwise a
   keyboard user tabs onto an invisible control and the page looks inert. P17. */
.media-add{display:inline-flex;cursor:pointer}
.media-add:focus-within{outline:2px solid var(--teal-500);outline-offset:2px;border-radius:var(--pill)}
.media-add input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}

/* ---- the pad itself: OLD's 3:1 strip, with a signature rule drawn under it ---- */
.sigpad{margin-top:4px}
.sigpad .note{margin-bottom:12px;font-size:var(--fs-xs);line-height:1.5}
/* Two mutually exclusive options: the vocabulary's chip pair, not a third bespoke toggle. The
   buttons carry `aria-pressed`, so the `.on` state is the visual half of what they already say. */
.sigpad-modes{display:flex;gap:8px;margin-bottom:12px;flex-wrap:wrap}
/* A 3:1 strip that takes the full page width is three times taller than a signature wants to be.
   The pad keeps the rail's own measure wherever the rail happens to be. P17; `media.js` re-fits
   the bitmap to whatever this resolves to. */
.sigpad-box{position:relative;border:1px solid var(--line);border-radius:var(--r-sm);
  background:var(--surface-2);overflow:hidden;margin-bottom:10px;touch-action:none;max-width:520px}
.sigpad-box canvas{display:block;width:100%;touch-action:none;cursor:crosshair;position:relative;z-index:2}
.sigpad-base{position:absolute;left:12%;right:12%;bottom:22%;border-bottom:1px dashed var(--line);
  pointer-events:none;z-index:1}
/* P21 — OLD's `sigpad-hint`: the words "Sign here" sitting on the rule, fading out under the
   first stroke rather than being cleared by a repaint. It is the one place in this app where an
   instruction is allowed to disappear on its own, because the thing that removes it is the
   person doing what it asked. `media.js` adds `.inked` on pointerdown and takes it off on Clear. */
.sigpad-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-size:var(--fs);font-weight:800;color:var(--neutral-300);letter-spacing:.02em;pointer-events:none;
  transition:opacity .18s ease;z-index:1}
.sigpad-box.inked .sigpad-hint{opacity:0}
.sigpad-foot{display:flex;align-items:center;gap:8px}

/* ================================================================ ADMIN */
.user-list{display:flex;flex-direction:column}
.user-row{display:flex;align-items:center;gap:14px;padding:13px 18px;border-bottom:1px solid var(--line-2)}
.user-row:last-child{border-bottom:0}
.user-row.inactive{opacity:.55}
.user-who{flex:1;min-width:0}
.user-name{display:block;font-weight:800;font-size:var(--fs-md)}
.user-meta{display:block;font-size:var(--fs-xs);font-weight:650;color:var(--ink-3);margin-top:1px}
.user-row select.ctl{width:auto;min-width:136px}

/* ---- the Formulary (P14) -------------------------------------------------
   The same row shape as the account list: what it is on the left, the clinic's own two cells
   in the middle, the stock flag and the way out on the right. */
.formulary-list{display:flex;flex-direction:column}
.formulary-row{display:flex;align-items:center;gap:12px;padding:12px 18px;border-bottom:1px solid var(--line-2)}
.formulary-row:last-child{border-bottom:0}
.formulary-who{flex:1 1 34%;min-width:0}
.formulary-name{display:block;font-weight:800;font-size:var(--fs-md)}
.formulary-meta{display:block;font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);margin-top:1px}
.formulary-row{transition:background-color var(--t-state) ease}
.formulary-row:hover{background:var(--surface-2)}
.formulary-row input.ctl{flex:1 1 150px;min-width:0;width:auto}
.formulary-stocked{display:inline-flex;align-items:center;gap:7px;flex:none;padding:4px 8px;
  border-radius:var(--r-sm);font-size:var(--fs-xs);font-weight:750;color:var(--ink-2);cursor:pointer;
  transition:background-color .16s,color .16s}
.formulary-stocked:hover{background:var(--teal-50);color:var(--teal-ink)}
.formulary-stocked:focus-within{outline:2px solid var(--teal-500);outline-offset:1px}
.formulary-row.stocked .formulary-name{color:var(--teal-ink)}

/* ---- the pharmacy queue (P15) --------------------------------------------
   Read-only, so the same row shape with nothing to type in: what was prescribed on the left,
   why it left the shelf and where it stands on the right. */
.formulary-queue{display:flex;flex-direction:column}
/* Panel-level guidance is a `.note`, not a field's helper text — but this one is inside a card
   header's flow rather than beside a control, so it keeps the quieter treatment and gains the
   card's own padding. */
.formulary-queue > .field-help{padding:12px 18px 0;max-width:82ch}
.formulary-queue-row{display:flex;align-items:center;gap:12px;padding:12px 18px;
  border-bottom:1px solid var(--line-2)}
.formulary-queue-row:last-child{border-bottom:0}
.formulary-queue-row .formulary-meta > span + span{margin-left:8px}
.formulary-queue-row .queue-qty{font-family:var(--font-mono);font-variant-numeric:tabular-nums}

/* ---- the clinic diary (P18) ----------------------------------------------
   The same row grammar as the two lists above — when on the left, who in the middle, state and
   the ways forward on the right — plus a week strip and one booking sheet. No new colours: the
   status chips are the app's existing `.pill` tones, so "cancelled" is the same coral a refusal
   wears everywhere else. */
.appt-bar{align-items:flex-end;gap:10px;flex-wrap:wrap}
.appt-bar input.appt-date{width:auto;min-width:170px;font-family:var(--font-mono)}
.appt-week{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:7px;margin-top:14px}
.appt-day{display:flex;flex-direction:column;align-items:center;gap:3px;padding:9px 4px;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-sm);background:var(--surface);color:var(--ink-2);
  font-family:inherit;transition:border-color .16s,background-color .16s,color .16s}
.appt-day:hover{border-color:var(--teal-line);background:var(--teal-50);color:var(--teal-ink)}
.appt-day:focus-visible{outline:2px solid var(--teal-500);outline-offset:2px}
.appt-day.on{border-color:var(--brand-line);background:var(--brand-soft);color:var(--brand-ink)}
/* Today is marked even when it is not the day being read — a diary you have paged away from
   should still say where "now" is. */
.appt-day.now .appt-day-n{text-decoration:underline;text-underline-offset:3px}
.appt-day-n{font-size:var(--fs-xs);font-weight:800;white-space:nowrap}
.appt-day-c{font-family:var(--font-mono);font-variant-numeric:tabular-nums;font-size:var(--fs-md);font-weight:800}
.appt-day-c.zero{color:var(--ink-3);font-weight:650}

.appt-list{display:flex;flex-direction:column}
.appt-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 18px;
  border-bottom:1px solid var(--line-2);transition:background-color var(--t-state) ease}
.appt-row:last-child{border-bottom:0}
.appt-row:hover{background:var(--surface-2)}
/* P21 — the day changing. Paging the diary or the dashboard from Tuesday to Wednesday replaces
   every row in the list, and without this the swap is a hard cut that reads as a glitch rather
   than as a different day. `listIn` is a 180ms fade with NO stagger and NO delay: the rows are
   already in the document when it starts, so nothing here can be mistaken for loading, and a
   busy clinic day of thirty appointments does not take a second to become readable. */
.appt-list>.appt-row,.home-list>.home-row{animation:listIn .18s ease both}
@keyframes listIn{from{opacity:0}to{opacity:1}}
/* A cancelled or missed row stays on the day — cancel is a status, never a delete (D-109) —
   and recedes rather than disappearing. */
.appt-row.s-cancelled,.appt-row.s-no-show{opacity:.6}
.appt-row.s-cancelled .appt-time{text-decoration:line-through}
.appt-when{flex:none;display:flex;flex-direction:column;min-width:96px}
.appt-time{font-size:var(--fs);font-weight:800;font-variant-numeric:tabular-nums}
.appt-dur{font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);white-space:nowrap}
.appt-who{flex:1 1 220px;min-width:0}
.appt-name{display:block;font-weight:800;font-size:var(--fs-md)}
.appt-meta{display:block;font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);margin-top:1px}
.appt-meta > span + span{margin-left:8px}
.appt-note{display:block;font-size:var(--fs-xs);font-weight:650;color:var(--ink-2);margin-top:3px}
.appt-chips{flex:none;display:flex;align-items:center;gap:6px}
.appt-tele{display:inline-flex;align-items:center;gap:5px}
.appt-tele .i{width:12px;height:12px}
.appt-actions{flex:none;display:flex;align-items:center;gap:6px;margin-left:auto;flex-wrap:wrap}
.appt-hint{flex-basis:100%;font-size:var(--fs-xs);font-weight:650;color:var(--teal-ink)}

/* The booking sheet. The element wears `.modal` too, so the dialog's own `.mh`/`.mb`/`.mf`
   parts come from theme.css rather than being redrawn here — this block adds only the two
   things a form this tall needs. The SCRIM is local because this dialog belongs to the page
   rather than to the shell's single `#modal`. */
.appt-scrim{position:fixed;inset:0;z-index:var(--z-modal);display:flex;align-items:center;
  justify-content:center;padding:20px;background:var(--scrim);
  -webkit-backdrop-filter:var(--glass-blur);backdrop-filter:var(--glass-blur)}
.appt-scrim[hidden]{display:none}
/* The same arrival the shell's own dialog has (theme.css) — this sheet is `.modal` and it should
   not be the one dialog in the app that snaps into place. It has no matching EXIT: this element
   is emptied synchronously by `appointments.js`, and an exit animation would mean holding a
   dismissed booking form in the document on a timer. The chart drawer is closed the same way and
   for the same reason (D-112). */
.appt-scrim:not([hidden]){animation:scrimFade var(--t-scrim) ease}
.appt-scrim:not([hidden]) .appt-modal{animation:modalIn .4s var(--ease-pop)}
.appt-modal{max-height:88vh;overflow:auto;margin:0}
.appt-form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 12px;margin-top:12px}
.appt-form .field{margin-bottom:0}
/* The grid's tallest cell carries two lines of helper text; without this the Notes label lands
   on top of it. */
.appt-form + .field{margin-top:16px}
.appt-modal textarea.ctl{min-height:72px;resize:vertical}
.appt-err{margin-top:12px;padding:10px 12px;border-radius:var(--r-sm);font-size:var(--fs-sm);font-weight:700;
  color:var(--danger);background:var(--danger-soft);border:1px solid var(--danger-line)}
.appt-err[hidden]{display:none}

/* ---- the week-grid calendar (P24) ----------------------------------------
   v1's calendar, ported onto v3's tokens and v3's four real appointment types.

   The whole geometry is TWO custom properties and a lot of percentages. `--cal-hour` is how tall
   an hour is; `--cal-hours` and `--cal-cols` are written onto `.cal` inline by `appointments.js`
   because they are counts, not styles — how many hours the window covers and how many days are
   on screen. Everything else a block needs — its top, its height, its lane — arrives as a
   percentage of the column, so the grid rescales with the viewport and `blockStyle()` stays a
   pure function with no pixel in it (D-115).

   `--cal-hour:88px` is v1's scale exactly: its `--slot` was 44px for a HALF hour. */
.cal-title{min-width:0}
.cal-range{margin:3px 0 0;color:var(--ink-2);font-size:var(--fs-sm);font-weight:700}
.topbar .seg.cal-view{flex:none}
.cal-filters{margin-bottom:0}
.cal-filters .field{min-width:auto}
.cal-nav-field{min-width:0}
.cal-nav{display:flex;align-items:center;gap:6px}
/* One chevron symbol, pointed twice. The sprite has a single `i-chevron` and a calendar needs
   both directions, which is a transform rather than a second piece of artwork. */
.cal-nav .btn.back .i{transform:rotate(180deg)}
.cal-toggle{display:inline-flex;align-items:center;gap:7px;margin-top:12px;
  font-size:var(--fs-xs);font-weight:750;color:var(--ink-2);cursor:pointer}
.cal-toggle input{width:14px;height:14px;margin:0;accent-color:var(--teal-600)}

.callegend{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:0}
.callegend .lg-item{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-xs);font-weight:800;
  color:var(--ink-2)}
.callegend .sw{width:14px;height:14px;border-radius:5px;border:1px solid;flex:none}

.calcard{padding:0;overflow:hidden;margin-top:16px}
/* The grid scrolls INSIDE its card, in both directions. That is the whole answer to "no
   horizontal page scroll": seven columns have a floor width, and when the viewport is narrower
   than the floor it is this box that scrolls, never the document. */
.calwrap{overflow:auto;-webkit-overflow-scrolling:touch;max-height:min(74vh,780px)}
.cal{--cal-hour:88px;--cal-cols:7;--cal-hours:9;min-width:calc(58px + var(--cal-cols) * 104px)}
.calhead,.calbody{display:grid;grid-template-columns:58px repeat(var(--cal-cols),minmax(0,1fr))}
.calhead{position:sticky;top:0;z-index:5;background:var(--surface);
  border-bottom:1px solid var(--line);box-shadow:0 1px 0 var(--line)}
.calh{padding:10px 8px;text-align:center;border-left:1px solid var(--line-2);min-width:0;
  border-top:0;border-right:0;border-bottom:0;background:none;font-family:inherit;color:inherit;
  cursor:pointer;transition:background-color var(--t-state) ease}
.calh.gut{border-left:0;cursor:default}
.calh:hover{background:var(--teal-50)}
.calh:focus-visible{outline:2px solid var(--teal-500);outline-offset:-2px}
.calh .dw{display:block;font-size:var(--fs-2xs);font-weight:900;letter-spacing:.1em;color:var(--ink-3)}
.calh .dn{display:block;font-size:var(--fs-xl);font-weight:900;letter-spacing:-.02em;line-height:1.2;margin-top:2px}
.calh .dc{display:block;font-size:var(--fs-2xs);font-weight:800;color:var(--ink-3);margin-top:1px}
.calh .dc.zero{color:var(--neutral-400)}
/* Today is coral: the header cell washed, all three of its lines recoloured. v1 drew no ring
   around the date number and neither does this — the colour IS the mark. */
.calh.istoday{background:var(--coral-50)}
.calh.istoday .dw,.calh.istoday .dn,.calh.istoday .dc{color:var(--coral-600)}
.calh.istoday:hover{background:var(--coral-100)}
/* The day the LIST underneath is showing, which in week view is not necessarily today. */
.calh.ison{box-shadow:inset 0 -3px 0 var(--brand)}

.calgut{background:var(--surface)}
.calgut .ct{height:var(--cal-hour);font-family:var(--font-mono);font-size:var(--fs-2xs);font-weight:700;
  color:var(--ink-3);text-align:right;padding:2px 8px 0 4px;white-space:nowrap;
  transform:translateY(-1px)}
.calcol{position:relative;border-left:1px solid var(--line-2);min-width:0;
  height:calc(var(--cal-hour) * var(--cal-hours))}
.calcol.istoday{background:linear-gradient(180deg,rgba(229,96,59,.045),rgba(229,96,59,.02))}
/* An hour rule is also the empty slot you click to book that hour, so it is a real button. */
.cslot{display:block;width:100%;height:var(--cal-hour);padding:0;border:0;border-bottom:1px dashed var(--line);
  background:none;cursor:pointer;transition:background-color .12s ease}
.cslot:last-of-type{border-bottom:0}
.cslot:hover{background:var(--teal-50);box-shadow:inset 0 0 0 1px var(--teal-line)}
.cslot:focus-visible{outline:2px solid var(--teal-500);outline-offset:-2px}

.achip{position:absolute;z-index:2;display:flex;flex-direction:column;overflow:hidden;text-align:left;
  min-height:20px;border-radius:9px;border:1px solid;padding:3px 7px;cursor:pointer;
  font-family:inherit;font-size:var(--fs-xs);font-weight:800;line-height:1.25;box-shadow:var(--shadow);
  transition:transform var(--t-press) ease,box-shadow var(--t-state) ease}
.achip:hover{transform:translateY(-1px);box-shadow:var(--shadow-md);z-index:4}
.achip:focus-visible{outline:2px solid var(--teal-500);outline-offset:1px}
.achip.picked{z-index:6;box-shadow:0 0 0 2px var(--brand),var(--shadow-md)}
.achip .at{font-size:var(--fs-2xs);font-weight:900;letter-spacing:.03em;opacity:.85;
  font-family:var(--font-mono)}
.achip .an{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.achip .ar{font-size:var(--fs-2xs);font-weight:700;opacity:.8;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
/* v1 stacked the day view's third line under the other two, which worked because its span was
   always 540 minutes and its blocks were therefore always tall enough. This window is derived,
   so a 20-minute visit in a ten-hour day is 29px — and three stacked lines in 29px is three
   clipped lines. A day column is the full width of the card instead, so the same three facts go
   side by side and every one of them is readable. */
/* Under-30-minute blocks in the week grid: one row, so nothing is clipped mid-letter. */
.achip.slim{flex-direction:row;align-items:baseline;gap:6px;padding:2px 8px}
.achip.slim .at{flex:none}
.achip.slim .an{flex:1;min-width:0}
.mode-day .achip{flex-direction:row;align-items:baseline;gap:10px;padding:4px 9px}
/* …and the DAY grid needs the slim padding too (D-121). `.achip.slim` above and
   `.mode-day .achip` here have the SAME specificity (0,2,0), so the later rule wins and a slim
   block in day view silently got its 4px padding back: 15px of line box into 12px of content
   box, and the bottom 3px — exactly the descender zone — was sliced off every name on the
   grid. `Rogelio Delos Santos Panganiban` lost both tails of its `g`s. This is D-095's
   "cascade order beats good intentions" a second time, so the rule that re-tightens it is
   scoped to the day grid rather than reordered: `.mode-day .achip.slim` is (0,3,0) and cannot
   be undone by a later sibling of equal weight. Only the VERTICAL padding is taken back —
   day view keeps its roomier 9px sides. */
.mode-day .achip.slim{padding-top:2px;padding-bottom:2px}
.mode-day .achip .at{flex:none}
.mode-day .achip .an{flex:none;max-width:46%}
.mode-day .achip .ar{flex:1;min-width:0;opacity:.75}
/* The four tones, one per REAL type. `TYPE_TONES` in appointments.js is the other half of this
   table and `tests/appointments.test.mjs` refuses to let the two drift: a type with no rule
   here, or a rule with no legend entry, fails the suite. */
.achip.t-coral{background:linear-gradient(180deg,#FFE7DE,var(--coral-50));border-color:var(--brand-line);
  color:var(--coral-700)}
.achip.t-teal{background:linear-gradient(180deg,#DCF5F2,var(--teal-50));border-color:var(--teal-line);
  color:var(--teal-700)}
.achip.t-violet{background:linear-gradient(180deg,#EBE6FB,#F5F2FE);border-color:var(--violet-line);
  color:var(--violet-ink)}
.achip.t-blue{background:linear-gradient(180deg,#DCEAFE,var(--info-light));border-color:var(--info-line);
  color:var(--info-dark)}
.callegend .sw.t-coral{background:var(--coral-50);border-color:var(--brand-line)}
.callegend .sw.t-teal{background:var(--teal-50);border-color:var(--teal-line)}
.callegend .sw.t-violet{background:var(--violet-soft);border-color:var(--violet-line)}
.callegend .sw.t-blue{background:var(--info-light);border-color:var(--info-line)}
.callegend .sw.now{background:var(--error);border-color:var(--error);height:3px;border-radius:2px;
  align-self:center}
/* Cancel is a status, never a delete (D-109) — so a cancelled block RECEDES rather than
   disappearing, and says so twice: it is dimmed, its border goes dashed, and the name it holds
   is struck through. Exactly what the day row underneath has always done. */
.achip.off{opacity:.55;border-style:dashed;filter:grayscale(.45)}
.achip.off .an{text-decoration:line-through}
.achip.done{opacity:.72}
.achip.live{box-shadow:0 0 0 2px rgba(234,179,8,.55),var(--shadow)}

/* The "Now" rule: 2px of the app's error red across today's column, with a knob overhanging the
   left edge. It has NO transition — it moves once a minute and a rule that slid would draw the
   eye to a clock nobody asked about. */
.nowline{position:absolute;left:0;right:0;height:2px;background:var(--error);z-index:3;
  pointer-events:none}
.nowline span{position:absolute;left:-3px;top:-3px;width:8px;height:8px;border-radius:50%;
  background:var(--error);box-shadow:0 0 0 3px rgba(239,68,68,.18)}

/* What a block opens (D-115): one card, one tap from the chart and one from the booking. It is
   positioned inside the day column it belongs to, so it scrolls with the grid rather than
   floating over a page that has moved underneath it. */
.cal-pop{position:absolute;left:0;z-index:8;width:252px;max-width:calc(100vw - 48px);
  padding:12px 13px;border-radius:var(--r);border:1px solid var(--line);background:var(--surface);
  box-shadow:var(--shadow-lg);animation:popIn .22s var(--ease-out) both}
@keyframes popIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
/* The last columns open leftwards, so a Saturday appointment's card is not half off the grid. */
.cal-pop.right{left:auto;right:0}
.cal-pop-h{display:flex;align-items:flex-start;gap:8px;font-size:var(--fs-md);font-weight:800}
.cal-pop-h strong{flex:1;min-width:0}
.cal-pop-meta{display:flex;flex-wrap:wrap;gap:4px 10px;margin-top:5px;font-size:var(--fs-2xs);
  font-weight:700;color:var(--ink-3)}
.cal-pop-chips{display:flex;gap:6px;margin-top:8px}
.cal-pop-note{margin-top:8px;font-size:var(--fs-xs);font-weight:650;color:var(--ink-2)}
.cal-pop-tele{display:flex;align-items:flex-start;gap:6px;margin-top:8px;font-size:var(--fs-2xs);
  font-weight:700;color:var(--teal-ink)}
.cal-pop-tele .i{width:13px;height:13px;flex:none;margin-top:1px}
.cal-pop-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}

.cal-empty{padding:16px 18px;font-size:var(--fs-sm);font-weight:650;color:var(--ink-3);
  border-top:1px solid var(--line-2)}
/* The phone's note, and the phone's note only. It is in the document at every width and shown
   by the media query at the bottom of this file — the fallback is a LAYOUT decision, and
   appointments.js does not get to have an opinion about the viewport (D-115). */
.cal-mobile{display:none;margin:0;border-radius:0;border:0;border-top:1px solid var(--line-2)}

/* Which of the two readings is on screen. Both subtrees are always in the document, so the
   toggle is a class on the page rather than a rebuild — and the media query below can therefore
   override it without appointments.js knowing.

   DAY is the grid one column wide (v1's own day view) with P18's list of rows underneath it,
   unchanged: the grid says where the day's shape is, the list is where you act on it. WEEK is
   the seven-column grid, and the list is put away — you are reading the week. */
.appt.mode-week .appt-day-card{display:none}

/* ---- the home dashboard (P19) --------------------------------------------
   v1's organisation — greeting, four counters, the day, alerts — on this app's tokens. No new
   colours and no new shapes: a counter is a `.card` with a big number in it, a day row is the
   same when/who/state/forward grammar the diary and the pharmacy queue already use, and an
   alert borrows the semantic tints a finding wears everywhere else.

   The one thing this screen adds is a DEFINITION under every counter. A dashboard number that
   two people read differently is worse than no number, so "Completed" carries the sentence
   that says finalized-today rather than dated-today, in the card, at all times. */
.home-hi{margin-bottom:22px}
.home-hi h1{font-size:var(--fs-3xl);font-weight:800;letter-spacing:-.02em;line-height:1.15}
.home-sub{margin:6px 0 0;font-size:var(--fs-md);font-weight:700;color:var(--ink-2);
  display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.home-dot{color:var(--ink-3)}
.home-facility{color:var(--teal-ink)}
/* Production runs with `HCI_NAME` empty until PhilHealth registers the clinic. The blank is
   SHOWN, in the muted voice a placeholder deserves, rather than filled with a plausible name. */
.home-facility.unset{color:var(--ink-3);font-weight:650;font-style:italic;cursor:help}

.home-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:20px}
/* P21 — the counters arrive one after another rather than as a slab. The stagger is deliberately
   the SHORTEST in the app (40ms a card, 120ms end to end) and belongs to the cards themselves,
   not to the view entrance: these four boxes are painted when `/api/home` answers, so a delay
   here is a delay on a NUMBER somebody is waiting for. The row's own entrance is switched off so
   the movement is not applied twice to the same pixels. */
#view.view-enter>.page>.home-stats{animation:none}
.home-stats>.home-stat{animation:riseIn .34s var(--ease-out) both}
.home-stats>.home-stat:nth-child(2){animation-delay:.04s}
.home-stats>.home-stat:nth-child(3){animation-delay:.08s}
.home-stats>.home-stat:nth-child(4){animation-delay:.12s}
.home-stat{display:flex;flex-direction:column;align-items:flex-start;gap:2px;text-align:left;
  padding:16px 18px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  box-shadow:var(--shadow);font-family:inherit;color:inherit;min-width:0}
.home-stat.is-link{cursor:pointer;transition:border-color .16s,box-shadow .16s,transform .16s}
.home-stat.is-link:hover{border-color:var(--brand-line);box-shadow:var(--shadow-md);transform:translateY(-1px)}
.home-stat.is-link:focus-visible{outline:2px solid var(--teal-500);outline-offset:2px}
.home-stat-n{font-size:var(--fs-4xl);font-weight:900;line-height:1.05;letter-spacing:-.03em;color:var(--brand-ink);
  font-variant-numeric:tabular-nums}
/* A zero is a fact, not a failure — it recedes rather than shouting in brand coral. */
.home-stat-n.zero{color:var(--ink-3)}
.home-stat-k{font-size:var(--fs-sm);font-weight:900;letter-spacing:.02em;color:var(--ink);margin-top:2px}
.home-stat-d{font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);line-height:1.45;margin-top:4px}
.home-stat-f{font-size:var(--fs-2xs);font-weight:800;color:var(--teal-ink);margin-top:6px;
  font-variant-numeric:tabular-nums}

.home-bar{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;justify-content:space-between;
  border-bottom:1px solid var(--line-2)}
.home-nav{align-items:flex-end}
.home-bar input.home-date{width:auto;min-width:170px;font-family:var(--font-mono)}
.home-filters select.ctl{width:auto;min-width:170px}

.home-list{display:flex;flex-direction:column}
.home-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:12px 18px;
  border-bottom:1px solid var(--line-2);transition:background-color var(--t-state) ease}
.home-row:last-child{border-bottom:0}
.home-row:hover{background:var(--surface-2)}
.home-row.s-cancelled,.home-row.s-no-show{opacity:.6}
.home-row.s-cancelled .home-time{text-decoration:line-through}
/* The patient who is standing at the desk is the one this screen exists to surface. */
.home-row.s-arrived{background:var(--teal-50)}
.home-when{flex:none;display:flex;flex-direction:column;min-width:88px}
.home-time{font-size:var(--fs);font-weight:800;font-variant-numeric:tabular-nums}
.home-dur{font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);white-space:nowrap}
.home-who{flex:1 1 200px;min-width:0}
.home-name{display:block;font-weight:800;font-size:var(--fs-md)}
.home-meta{display:block;font-size:var(--fs-2xs);font-weight:650;color:var(--ink-3);margin-top:1px}
.home-meta > span + span{margin-left:8px}
.home-chips{flex:none;display:flex;align-items:center;gap:6px}
.home-type .i{width:12px;height:12px}
.home-with{flex:none;min-width:120px;font-size:var(--fs-xs);font-weight:700;color:var(--ink-2)}
.home-actions{flex:none;display:flex;align-items:center;gap:6px;margin-left:auto}
.home-list>.empty .btn{margin-top:12px}

.home-alert-list{display:flex;flex-direction:column}
.home-alert{display:flex;gap:12px;align-items:flex-start;padding:13px 18px;
  border-bottom:1px solid var(--line-2)}
.home-alert:last-child{border-bottom:0}
.home-alert-i{flex:none;width:30px;height:30px;display:grid;place-items:center;border-radius:9px;
  background:var(--line-2);color:var(--ink-2)}
.home-alert-i .i{width:15px;height:15px}
.home-alert.sev-ok .home-alert-i{background:var(--success-light);color:var(--success-dark)}
.home-alert.sev-info .home-alert-i{background:var(--teal-50);color:var(--teal-700)}
.home-alert.sev-warn .home-alert-i{background:var(--warning-light);color:var(--warning-dark)}
.home-alert.sev-danger .home-alert-i{background:var(--error-light);color:var(--error-dark)}
.home-alert-b{flex:1;min-width:0}
.home-alert-msg{display:block;font-size:var(--fs-sm);font-weight:700;line-height:1.45;color:var(--ink)}
.home-alert-items{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:7px;font-size:var(--fs-xs)}
.home-alert-more{font-size:var(--fs-xs);font-weight:700;color:var(--ink-3)}

/* A grid row is this app's table: the columns are declared by the screen, and the head row is
   the same grid with the table-header treatment, so the two can never drift apart. */
.audit-rows,.ml-rows,.tx-rows,.imp-rows,.gen-encounters,.backup-rows{display:flex;flex-direction:column}
.audit-row,.ml-row,.tx-row,.imp-row,.gen-row,.backup-row{
  border-bottom:1px solid var(--line-2);font-size:var(--fs-xs);font-weight:650;align-items:center;
  display:grid;gap:10px}
.audit-row:last-child,.ml-row:last-child,.tx-row:last-child,.imp-row:last-child,.gen-row:last-child,.backup-row:last-child{border-bottom:0}
.ml-row.head,.tx-row.head,.imp-row.head,.gen-row.head,.backup-row.head{
  font-size:var(--fs-2xs);font-weight:800;text-transform:uppercase;letter-spacing:var(--tracking-caps);color:var(--ink-3);
  border-bottom:1px solid var(--line);background:none}
.gen-row,.imp-row{cursor:pointer;transition:background-color .16s ease}
.gen-row:hover,.imp-row:hover{background:var(--surface-2)}
.gen-row.head,.imp-row.head{cursor:default}
.gen-row.head:hover,.imp-row.head:hover{background:none}

.audit-row{grid-template-columns:150px 160px 140px 150px minmax(0,1fr);padding:10px 18px}
.audit-ts{color:var(--ink-3);font-variant-numeric:tabular-nums}
.audit-who{font-weight:800}
.audit-target,.audit-detail{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis}

.ml-row{grid-template-columns:130px minmax(0,1fr) 60px 110px 110px 80px 76px;padding:9px 4px}
.ml-rows{margin-top:14px}
.ml-row.known{background:var(--success-light)}

.gen-patients{display:flex;flex-wrap:wrap;gap:7px;margin-top:14px}
.gen-encounters{margin-top:14px}
.gen-row{grid-template-columns:20px 96px 100px minmax(0,1fr);padding:9px 4px}
/* The backups list (P11's endpoints, drawn in P17): date, time, why, rows, size, download. It
   joins the grid-row family above rather than borrowing the generator's `.gen-row` — two lists
   that mean different things do not share a class just because they look alike (the lesson the
   pharmacy queue's borrowed `.formulary-*` row taught). */
.backup-row{grid-template-columns:104px 62px 92px minmax(0,1fr) 78px 100px;padding:10px 18px}
.backup-row:not(.head){transition:background-color var(--t-state) ease}
.backup-row:not(.head):hover{background:var(--surface-2)}
.backup-counts{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;cursor:help}
.backup-row .btn{justify-self:end}
.backup-rows>.empty{padding:26px 18px}
.gen-label{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis}
.gen-bar{margin-top:16px;display:flex;gap:10px;flex-wrap:wrap;align-items:center}

/* The 422 list. Coral, because a refusal by the Data Dictionary is the app's own voice —
   and every sentence in it is a rule's, quoted rather than paraphrased. */
.gen-errors,.imp-problems,.tx-deficiencies{display:flex;flex-direction:column;gap:7px;margin-top:14px}
.gen-errors-h{font-size:var(--fs-sm);font-weight:900;color:var(--coral-700);display:flex;align-items:center;gap:7px}
.gen-err{display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;gap:9px;align-items:baseline;
  padding:9px 12px;border-radius:var(--r-sm);border:1px solid var(--brand-line);
  background:linear-gradient(180deg,var(--coral-50),rgba(255,255,255,.9))}
.gen-err-at{color:var(--coral-700);font-size:var(--fs-2xs);font-weight:800;font-family:var(--font-mono)}
.gen-err-msg{font-size:var(--fs-xs);font-weight:650;color:var(--ink);line-height:1.45}
.gen-err-entry{font-size:var(--fs-2xs);color:var(--ink-3);font-weight:800;font-family:var(--font-mono)}

/* Nine columns and five per-row actions: the transmittal log keeps its natural width and
   scrolls sideways inside its card, exactly as OLD's wide admin tables do, rather than wrapping
   its buttons onto a second line and turning every row into a ragged block. */
.card:has(> .tx-rows){overflow:auto;-webkit-overflow-scrolling:touch}
.tx-rows{min-width:1230px}
/* The tranche column is 66px and not 54px because of its HEADER, not its data (D-121): the cell
   holds `1` or `2`, but the word `Tranche` measures 63px and cannot break, so at 54px it hung
   9px out of its column into the 10px gap — one pixel short of touching `File`. The data column
   is generous either way and the card scrolls, so the twelve pixels cost nothing. */
.tx-row{grid-template-columns:180px 66px minmax(0,1fr) 128px 100px 84px 104px 118px auto;padding:10px 18px}
.tx-row > .row.tight{flex-wrap:nowrap;justify-content:flex-end}
.tx-file{color:var(--ink-2);overflow:hidden;text-overflow:ellipsis}
.tx-txn{color:var(--ink-2);font-size:var(--fs-xs);font-family:var(--font-mono)}
.tx-row-wrap{display:flex;flex-direction:column;border-bottom:1px solid var(--line-2)}
.tx-row-wrap > .tx-row{border-bottom:0}
/* PhilHealth's own deficiency list, quoted. Coral for the same reason the 422 list is. */
.tx-deficiencies{padding:0 18px 14px;margin-top:0}

.imp-rows{margin-top:14px}
.imp-row{grid-template-columns:20px 130px minmax(0,1fr) 180px auto;padding:9px 4px}
.imp-row.done{grid-template-columns:130px minmax(0,1fr);cursor:default}
.imp-row .audit-detail{grid-column:1/-1}
.imp-problems{margin-top:14px}

/* the PhilHealth API state line, and what a lookup answered */
.pka-state{display:flex;flex-wrap:wrap;align-items:center;gap:9px}
.pka-state .field-help{flex:1 1 100%;margin-top:2px}
.pka-result{display:flex;align-items:center;gap:9px;margin-top:12px;min-height:22px;font-size:var(--fs-sm);font-weight:700;
  color:var(--ink-2)}
.card.pad .form-grid + .btn{margin-top:14px}

/* ================================================================ RESPONSIVE */
@media (max-width:1180px){
  /* Four counters at 1180px are 230px each and their definitions wrap to four lines. Two by
     two keeps the sentence under each number readable, which is the only reason it is there. */
  .home-stats{grid-template-columns:repeat(2,minmax(0,1fr))}
  .enc-body{grid-template-columns:minmax(0,1fr)}
  .enc-nav{margin-bottom:16px}
  .rail-sec{flex-direction:row;flex-wrap:wrap;align-items:center;gap:6px}
  .rail-h{width:100%}
  .nav-item{width:auto;border:1px solid var(--line);border-radius:var(--pill);padding:6px 12px}
  .enc-findings{position:static;max-height:none}
  .audit-row,.ml-row,.tx-row,.imp-row{grid-template-columns:1fr 1fr;gap:4px 10px}
  .gen-err{grid-template-columns:1fr}
}
@media (max-width:880px){
  /* The registry table already scrolls inside its card, so it keeps natural column widths
     rather than being squeezed into the viewport and wrapping every name onto three lines. */
  .results table{table-layout:auto}
  .results table td{overflow:visible}
  .enc-h{gap:12px}
  .enc-title h1{font-size:var(--fs-xl)}
  .enc-actions{margin-left:0;width:100%}
  .enc-actions .btn{flex:1 1 auto}
  /* P25 — the drawer becomes a full-height sheet. It still arrives from the right rather than
     from the bottom, exactly as v1's does on a phone: a bottom sheet would put the form's
     header where a thumb rests, and the seamless rule binds the desktop anyway. */
  .enc-doc{width:100%;border-left:0}
  .enc-doc>.enc-h{padding:11px 14px}
  .enc-doc>.enc-body{padding:14px 14px 44px}
  /* Forty-three pills would push the first question off a phone screen: the rail keeps a couple
     of rows and scrolls for the rest. Collapsing a section is the other half of the answer. */
  .enc-nav{max-height:196px;overflow:auto;-webkit-overflow-scrolling:touch}
  .grp-h{position:static;padding:12px 14px}
  .grp-b{padding:14px}
  .panel.recommend{margin:0 14px 14px;padding:14px}
  .rec-answers{grid-template-columns:1fr}
  .user-row{flex-wrap:wrap}
  /* …and the identity needs a floor to wrap ABOVE (D-121). `.user-who` is `flex:1;min-width:0`,
     so it is the only item in the row that can absorb a shortfall — and the ADMINISTRATOR'S OWN
     row is the one that carries a disabled role control instead of a select and one fewer
     button, so it is the one row that still fits on a single line and hands the whole deficit to
     the name. At 430px that collapsed `.user-who` to 37px while `Administrator` is a 93px word
     that cannot break, and `overflow` is visible here, so the name PAINTED STRAIGHT THROUGH the
     role box: the screen read `Administradmin`. A floor rather than a hidden overflow, because
     the row already wraps at this width — with 12ch it drops the controls to the next line
     exactly as every other row already does, and nothing is truncated or hidden. Above 880px the
     row does not wrap and this does not apply. */
  .user-who{min-width:12ch}
  .audit-row,.tx-row{padding-left:14px;padding-right:14px}
  .tx-deficiencies{padding-left:14px;padding-right:14px}
  /* P14's editable row and P15's read-only one look identical and used to wrap at 820px — a
     breakpoint that existed nowhere else in the app, and that the queue row only half shared.
     One rule, at the width the shell itself collapses. P17. */
  .formulary-row,.formulary-queue-row{flex-wrap:wrap}
  .formulary-row .formulary-who,.formulary-queue-row .formulary-who{flex-basis:100%}
  .formulary-row input.ctl{flex:1 1 46%}
  /* Seven day-cells across a phone leaves 45px each: the strip scrolls sideways rather than
     wrapping the week onto two lines, which would stop reading as a week. P18. */
  .appt-week{grid-template-columns:repeat(7,74px);overflow-x:auto;-webkit-overflow-scrolling:touch}
  .appt-row{padding-left:14px;padding-right:14px}
  /* P24 — the week grid does not survive a phone, so on a phone there is no week grid. Seven
     columns across 375px is 45px each, which is a name rendered as two characters and an
     ellipsis; the DAY list this diary shipped with is already the right shape for a thumb, so
     week mode falls back to it and the note inside the (hidden) grid card explains the swap.
     The whole fallback is here rather than in appointments.js on purpose: the layout is what
     knows the width (D-115). */
  .appt.mode-week .calwrap{display:none}
  .appt.mode-week .appt-day-card{display:block}
  .appt.mode-week .cal-mobile{display:flex}
  .cal-filters{gap:10px}
  .cal-filters .field,.cal-filters select.ctl,.cal-filters input.appt-date{min-width:0;flex:1 1 auto}
  .cal-filters .spacer{display:none}
  .callegend{flex-basis:100%;gap:10px}
  .topbar .seg.cal-view{width:auto}
  /* P19. One counter per line on a phone — a 34px number beside a three-line definition in a
     160px column reads as neither. The day row wraps its action onto its own line, as the
     diary's does, rather than squeezing a button against the bezel. */
  .home-hi h1{font-size:var(--fs-2xl)}
  /* P26 — the greeting's second line wraps on a phone, and the `·` between the date and the
     facility then hangs at the end of the first line pointing at nothing. A separator only
     separates while the things it sits between are side by side. */
  .home-sub .home-dot{display:none}
  .home-sub{gap:2px 8px}
  .home-stats{grid-template-columns:1fr;gap:12px}
  .home-stat{padding:14px 16px}
  .home-stat-n{font-size:var(--fs-3xl)}
  .home-row{padding-left:14px;padding-right:14px}
  .home-with{min-width:0;flex-basis:100%}
  .home-actions{margin-left:0;flex-basis:100%}
  .home-alert{padding-left:14px;padding-right:14px}
  .home-bar{gap:10px}
  .home-filters select.ctl,.home-bar input.home-date{min-width:0;flex:1 1 auto}
  .appt-actions{margin-left:0;flex-basis:100%}
  .appt-form{grid-template-columns:1fr}
  .backup-row{grid-template-columns:1fr 1fr;gap:4px 10px;padding-left:14px;padding-right:14px}
  /* The rail's media panel is now the page's width, and its fixed cells need to breathe. */
  .media-strip{gap:10px}
  .media-thumb,.media-thumb img{width:84px}
  .media-thumb img{height:84px}
  .media-sig-img{max-height:160px}
}
@media (max-width:700px){
  .ncdq-q{grid-template-columns:1fr}
  .ncdq-ask,.ncdq-a{grid-column:1;grid-row:auto}
  .ncdq-extra{padding-left:0;border-left:0}
}
@media (max-width:560px){
  /* P26 — the formulary's two editable cells were 46% each on a phone, which is 150px for a
     sig line like "1 tablet twice daily with meals": every row ended in an ellipsis and the
     thing being edited could not be read. One per line, full width. */
  .formulary-row input.ctl{flex:1 1 100%}
}

/* ---------------------------------------------------------------- touch targets (P26) */

/* theme.css sets the 44px floor for every button on a coarse pointer; these are the controls
   THIS file owns, raised here rather than there because a rule in the earlier stylesheet loses
   to the definition in this one at equal specificity — which is how the typeahead's clear cross
   stayed 28px through the first pass of this phase. */
@media (pointer:coarse){
  /* The library typeahead: the cross that empties a picked code, and the rows of the menu. */
  .combo-clear{width:44px;height:44px;font-size:var(--fs-lg)}
  .combo-opt{min-height:44px}
  /* The DD-group rail — forty-three rows a clinician taps to open a group, plus the section
     headers that collapse them. */
  .nav-item,.rail-h{min-height:44px}
  /* The diary's week strip, and a chip that removes a picked library code. */
  .appt-day{min-height:44px}
  .chip-x{min-width:32px;min-height:32px}
}

/* ================================================================ PRINT */
/* An Annex-H slip prints from `slips.js`, which replaces the whole view first and brings its own
   @page — nothing here can reach it. This block is for the OTHER print: a clinician pressing
   Cmd+P on the encounter room itself. Three things in the room are positioned for a screen and
   make no sense on paper — a sticky findings rail clipped to one viewport height, a sticky group
   header that repeats across page breaks, and a gradient band that colour-adjust strips anyway.
   None of these selectors exists in a slip's DOM. P17. */
@media print{
  /* The entrances this file owns are cancelled by theme.css's ONE universal print rule (D-116),
     which is why the list that used to be here is gone: an enumeration per stylesheet meant
     every new animation had to remember to join one, and the day it did not, that element
     printed mid-fade. Nothing about the promise changed — only the number of places that have
     to be kept in step with it. */
  /* P24. A calendar is a screen, not a document: the grid scrolls inside a box and would print
     as one viewport of it with everything below the fold missing, which is worse than nothing.
     So paper gets the DAY LIST — the same rows, in order, on a page that can be as long as the
     day is. Only `display` changes hands; no geometry is asserted, which is the rule every app
     print block here is held to. */
  .appt .calcard,.appt.mode-week .calcard{display:none}
  .appt.mode-week .appt-day-card{display:block}
  .appt .seg,.appt .cal-filters,.appt .cal-toggle{display:none}
  /* P25. On screen the documentation is a fixed overlay; on paper a fixed element prints as one
     clipped page with everything below it missing. So an OPEN drawer joins the flow and prints
     whole, and a closed one prints nothing at all. Only `display` and `position` change hands —
     no geometry is asserted, which is the rule every app print block here is held to. */
  .enc-doc-scrim{display:none !important}
  .enc-doc{display:none !important}
  .enc-doc.open{display:flex !important;position:static;width:auto;border-left:0;box-shadow:none}
  /* …and the ROOM behind it does not print (D-120). P25 made the drawer an overlay over a stage
     that keeps painting, which is right on screen and wrong on paper: Cmd+P with the
     documentation open emitted the stage first — the patient card, the tool grid, the elapsed
     clock, the `<video>` frame — and the documentation after it, so the record a clinician meant
     to print arrived on page two behind a screenshot of a consulting room. The documentation
     prints alone. `data-enc-view` is the attribute `setView` already writes on the shell, so
     this reads the room's own state rather than asserting a second one (and needs no `:has()`,
     which this app uses nowhere). Only `display` changes hands. */
  .enc[data-enc-view="documentation"] .enc-stage-host{display:none !important}
  .enc-body{display:block}
  .enc-nav,.enc-media{display:none !important}
  .enc-findings{position:static;max-height:none;overflow:visible}
  .grp-h{position:static;background:none;border-bottom:1px solid #999}
  .grp,.row-card,.rec-item{break-inside:avoid;box-shadow:none}
  .combo-menu{display:none !important}
}
