/* SmartKitchenDocs — manager console.
 *
 * Visual direction (2026-09-28, "Clean"): a white page, a quiet top bar with
 * the destinations as plain words, content in one centred column, and
 * tinted flat tiles instead of outlined boxes for the numbers that matter.
 * Labels are sentence case throughout. It supersedes "Outline" (white panes
 * edged in grey, from earlier the same day) and lives in the CLEAN block at
 * the end of this file, which is where every rule that makes it lives.
 *
 * Previous direction (2026-09-18, "Nellie"): warm paper, soft shape, real
 * depth. Replaces the 2026-08-26 "Industry" direction, which drew every
 * component as a square, transparent, hairline-bordered wireframe object.
 *
 * WHY IT CHANGED. That direction set --radius:0 and --shadow:none and
 * attributed the flatness to prototype/, but the prototype specifies
 * --radius-sm/md/lg 2/4/7 and a three-step shadow scale. The build was
 * squarer than its own design target. And because surfaces were separated
 * by hairline border alone (--panel2 measured 1.09:1 against the page),
 * nothing on screen outranked anything else — which is what read as busy.
 *
 * So: fills now separate surfaces, elevation marks what floats, and most
 * borders are redundant. Do NOT reintroduce a border on something that
 * already has a fill and a shadow.
 *
 * Semantic colour is still reserved for outcomes. Steps are NOT green: a
 * procedure that has not been performed yet should not read as already
 * done — see the .step rules. Status owns warm colour, category owns cool;
 * both are always spelled out in words, never carried by shape alone.
 */
@font-face{font-family:"Fraunces";src:url("assets/fonts/Fraunces-var.woff2") format("woff2-variations");font-weight:600 700;font-display:swap}
@font-face{font-family:"Instrument Sans";src:url("assets/fonts/InstrumentSans-var.woff2") format("woff2-variations");font-weight:400 600;font-display:swap}
@font-face{font-family:"Courier Prime";src:url("assets/fonts/CourierPrime-400.woff2") format("woff2");font-weight:400;font-display:swap}
@font-face{font-family:"Courier Prime";src:url("assets/fonts/CourierPrime-700.woff2") format("woff2");font-weight:700;font-display:swap}

:root{
  /* Plain white ground (see CLEAN at the end of this file). The warm paper
     set (#fbf7f0 / #f2eadd / #2a231e) is what read as beige: every fill,
     hairline and grey leaned brown. These lean slate. --panel2/--panel3 and
     the lines are translucent on purpose, so an inset well or a hairline
     takes on whatever surface it sits on rather than painting an opaque
     patch over it.

     The contrast figures elsewhere in this file were measured on the colour
     wash that used to sit under the panes, which was darker than white at
     every point; on white each one is equal or higher. */
  --bg:#ffffff; --panel:#ffffff; --panel2:rgba(100,116,139,.08); --panel3:rgba(100,116,139,.05);
  --ink:#101828; --dim:#475467; --warn:#8f5a0d; --bad:#8f2230;
  --line:rgba(16,24,40,.08); --line2:rgba(16,24,40,.2); --faint:#667085;

  /* The outline. Every pane, tile and secondary control is solid white
     edged in this grey, on a ground that is white too, so this line is the
     only thing separating a surface from the page. --outline-hover is the
     same edge under the pointer. Neither is a text-field boundary: that is
     --control-line below, which has a contrast floor these do not meet
     (#d0d5dd is 1.47:1 on white, #98a2b3 2.58:1). */
  --outline:#e4e7ec;
  --outline-hover:#d0d5dd;
  /* The same edge, one step firmer, for things you press that have no fill
     of their own: secondary buttons, the Today ask box and its openers. */
  --outline-strong:#d0d5dd;
  --outline-strong-hover:#98a2b3;

  /* Flat tiles (the KPI figures): a fill, no edge. The tone pairs are the
     tinted tiles for overdue and due-today work; their inks measure 6.05:1
     and 5.20:1 on their own fills. Status on the calendar keeps --occ-*. */
  --tile:#f8f9fb; --tile-hover:#f2f4f7;
  --tone-overdue-bg:#fef3f2; --tone-overdue-bg-hover:#fee4e2; --tone-overdue-ink:#b42318;
  --tone-today-bg:#fffaeb;   --tone-today-bg-hover:#fef0c7;   --tone-today-ink:#b54708;

  /* The inset either side of the top bar and the working surface. Both run
     edge to edge (see .wrap below); this is the only margin they keep. */
  --gutter:28px;

  /* ACCENT IS TWO TOKENS, and this is not a style preference.
     --accent on the page ground measures 4.34:1 — it FAILS WCAG 1.4.3 for
     normal text. It is legal as a fill (white on it is 4.63:1) and illegal
     as text. --accent-text is the same hue darkened to 5.50:1.
     Never set colour:var(--accent) on a text node. */
  --accent:#3a72d4;
  --accent-text:#2c60c4;
  --accent-dark:#24509e;
  --accent-ink:#ffffff; --accent-soft:rgba(58,114,212,.12);

  /* The visual boundary of a text field. WCAG 1.4.11 wants 3:1 against
     what surrounds it; #858fa1 measures 3.26:1 on white, which is both the
     page and every pane. A field's fill is the same white as what it sits
     on, so this border IS the edge -- do not lighten it to --outline to
     make the field look softer. */
  --control-line:#858fa1;

  --ok:#2f6b47; --lock:#416180;
  /* PM calendar occurrence status. Status is the ONE warm colour channel;
     it survives print and colour blindness because every one of these is
     also written as a word (STATUS_LABEL in views/shared/task_glyphs.js).
     'upcoming' and 'skipped' deliberately reuse the neutral tokens rather
     than inventing more colour for states that are not calls to action. */
  --occ-overdue-bg:#f9e7e8; --occ-overdue-line:#b4414c; --occ-overdue-ink:#8f2230;
  --occ-today-bg:#fbeeda;   --occ-today-line:#b98a2a;   --occ-today-ink:#8f5a0d;
  --occ-done-bg:#e4f0e8;    --occ-done-line:#4f8a66;    --occ-done-ink:#2f6b47;
  /* Category tint. Drawn entirely from the COOL end so a category can never
     be misread as a status, which owns warm. Category is always rendered
     with its WORD beside the tint — the old shape vocabulary (circle /
     square / funnel / diamond) is gone, because a shape needs a memorised
     legend and a word does not. */
  --cat-cleaning:#0e7490; --cat-inspection:#6941c6;
  --cat-filter:#3538cd;   --cat-calibration:#475467; --cat-other:#667085;
  --cat-cleaning-bg:#dceef2; --cat-inspection-bg:#ebe4f8;
  --cat-filter-bg:#e2e3fa;   --cat-calibration-bg:#e9ebee; --cat-other-bg:#eef0f3;

  /* Shape and depth. One radius scale replaces the eight hardcoded values
     that were scattered through this file (0, 2, 4, 6, 8, 10, 50%, 999px),
     and elevation is kept only for things that float (see below). */
  --radius:10px;
  --radius-sm:8px;
  --radius-lg:16px;
  --radius-chip:999px;
  --radius-pill:999px;
  /* In-flow surfaces are separated by --outline, not by elevation. --shadow
     is a 1px contact shadow that only seats a card on the page; it does not
     lift it. The two in-between steps are `none`: any rule above that still
     names one draws nothing. Never put either of those in a comma list with
     another shadow -- `none` is only valid on its own, and the whole
     declaration would be dropped. --shadow-lg is what floats (the account
     menu, toasts, the phone's More panel) still lift with: neutral grey,
     not blue. */
  --shadow:0 1px 3px rgba(16,24,40,.05);
  --shadow-sm:none;
  --shadow-md:none;
  --shadow-lg:0 4px 12px rgba(16,24,40,.08),0 16px 32px -12px rgba(16,24,40,.16);

  /* The old identity plate. The rules above still name it -- the band, a
     pressed button, the mobile bar's current tab -- but CLEAN overrides all
     three (the top bar is white now, the markers are outlined panes), so
     nothing on screen paints it. Retuning it changes nothing you can see. */
  --ink-deep:#111b2f;

  /* SF Pro, via the system stack: Apple's licence does not allow the font
     files to be served from a website, so Macs and iPhones get it from the
     OS and everything else falls back to its own UI face. */
  --sans:-apple-system,BlinkMacSystemFont,system-ui,"Segoe UI",Roboto,sans-serif;
  --heading:-apple-system,BlinkMacSystemFont,system-ui,"Segoe UI",Roboto,sans-serif;
  /* WAS: "Barlow",ui-monospace,Consolas,monospace — and Barlow is a
     PROPORTIONAL face that this stylesheet loads, so it won the cascade and
     the monospace fallbacks were never reached. Every cited manual page
     rendered proportional. Courier Prime is a real monospace, and reads as
     a printed document rather than as a terminal, which is what a citation
     out of a paper manual should look like. */
  --mono:"Courier Prime",ui-monospace,Consolas,monospace;
}

*{box-sizing:border-box}
html{background:var(--bg)}
body{margin:0;color:var(--ink);font:15px/1.55 var(--sans);background:var(--bg);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
h1,h2,h3,h4,h5,h6{margin:0;font-family:var(--heading);font-weight:600;letter-spacing:-.01em;line-height:1.12}

/* Anything numeric — page numbers, KPI values, codes — gets the mono face
   with tabular figures so columns line up and digits stop jittering. */
.mono,.kpi .v,code{font-family:var(--mono);font-variant-numeric:tabular-nums}

button{font:inherit;cursor:pointer;border:none;white-space:nowrap}
/* Text-entry controls only. `input` unqualified also matches checkboxes and
   radios, and width:100% turned the Onboarding roster's checkboxes into
   1026px-wide bars -- label text flung to the far edge, and the entire bar
   live as a hit target, so a click meant for a manual's title silently
   crossed that manual off. See input[type=checkbox] below. */
select,input:not([type=checkbox]):not([type=radio]),textarea{font:inherit;background:var(--panel2);color:var(--ink);
  border:1px solid var(--control-line);padding:9px 11px;width:100%;
  transition:border-color .18s ease,background .18s ease}
select:hover,input:not([type=checkbox]):not([type=radio]):hover,textarea:hover{border-color:var(--line2)}

/* Checkboxes size to themselves and stay next to their label. flex:none so a
   flex parent cannot stretch them again; 16px is comfortably clickable while
   the <label> wrapper keeps the text itself as the real hit area. */
input[type=checkbox],input[type=radio]{
  width:16px;height:16px;flex:none;margin:0;accent-color:var(--accent);cursor:pointer}
textarea{resize:vertical;min-height:96px}
/* :focus-visible, not :focus — a mouse click on a select should not leave a
   ring behind, but every keyboard path must show one. */
:focus{outline:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
select:focus-visible,input:not([type=checkbox]):not([type=radio]):focus-visible,textarea:focus-visible{
  border-color:var(--accent);outline-offset:1px}
a{color:var(--accent-dark);text-decoration-color:rgba(65,97,128,.35);
  text-underline-offset:3px}
a:hover{text-decoration-color:currentColor}
/* ONE measure for the whole console: the bands and the working surface
   share the same 28px inset and both run to the edge. Nothing is centred in
   a narrower column inside them.

   The prototype does put its content in a centred column while its bands
   run full-bleed -- measured on its own render, its tabs start at x=45 and
   its page title at x=157 -- and this file used to reproduce that
   faithfully. Dylan's call: that offset is how the prototype bundle was
   built, not something he designed, and the empty ground it leaves either
   side of the content is the "white border" he wanted gone. The design
   intent is edge to edge, so the console is edge to edge. */
/* One inset all the way round: 28px at the sides, and the page closes at
   28px too. It used to trail off in 96px of empty ground below the last
   card, which is what made the bottom of every screen read as unfinished
   rather than ended. */
.wrap{max-width:none;margin:0;padding:0 0 28px}
#appScreen>*:not(header):not(nav),
#authScreen>*:not(header){
  max-width:none;margin-left:0;margin-right:0;padding-left:28px;padding-right:28px}

/* The identity band. One dark plate across the top, then everything below
   it is the light working surface -- the console used to carry TWO stacked
   navy bands (this one and the tab bar), which is half of why it read as a
   different product than the design. */
header.top{display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  margin:0;padding:16px 28px;background:var(--ink-deep);color:#fff;border:0;border-radius:0;box-shadow:none}
.brand{font-family:var(--heading);font-size:22px;font-weight:700;
  text-transform:uppercase;letter-spacing:.02em;line-height:1}
/* One flex item, not two. The wordmark used to be a bare text node next to
   a <span>, so the flex `gap` landed BETWEEN "Kitchen" and "Docs" and a
   .34em margin stacked on top of it -- roughly 17px of hole in the middle
   of a single word pair. The name is now one element that spaces itself
   with an ordinary space. */
.brand__name{white-space:nowrap}
.brand__name b{color:#94bce3;font-weight:inherit}
.top .tiny,.top .muted{color:#9fb0c0}
.top .textAction{color:#fff}
.top a{color:#b5d9fd}
/* A chip, not a lozenge: square with a hairline, condensed uppercase --
   the same object the design uses for the signed-in name. */
.pill{font-family:var(--heading);font-size:11px;font-weight:600;padding:5px 11px;
  background:transparent;text-transform:uppercase;
  border:1px solid rgba(255,255,255,.28);color:#fff;letter-spacing:.08em;white-space:nowrap}
.spacer{flex:1}
.who{display:flex;align-items:center;gap:9px}
.who select{width:auto;min-width:170px}

.banner{margin:14px 0;padding:11px 14px;font-size:12.5px;
  color:#704b00;background:#fff8e8;border:1px solid #ebc46f}
.banner b{color:#5c3d00}
.banner.info{color:var(--dim);background:#eef7fc;border-color:#a9d6ef}
.banner.info b{color:var(--ink)}
.banner.bad{color:#8f1d35;background:#fff1f3;border-color:#f3a5b4}
.banner.bad b{color:#6e1026}
/* A standing caveat that never changes and is not actionable. It has to stay
   on screen (the names really are unverified) but it should not out-weigh
   the working surface below it, which is what a full-width banner did. */
.banner.quiet{font-size:11.5px;padding:8px 12px;color:var(--faint);
  background:transparent;border-color:var(--line);margin:12px 0}
.banner.quiet b{color:var(--dim);font-weight:600}

/* Tabs as one segmented control rather than six free-floating buttons: the
   set reads as a single switch, and the active state is a soft fill instead
   of a saturated block competing with the primary action. */
/* Destinations sit on the LIGHT ground, under the dark identity plate, and
   the current one is marked by a 3px ink rule rather than a second dark
   band. Typographic only: no fills, no pills. */
nav.tabs{display:flex;gap:26px;margin:0 0 30px;padding:0 28px;flex-wrap:wrap;
  background:transparent;border:0;border-bottom:1px solid var(--line);
  border-radius:0;width:auto;max-width:none}
nav.tabs button{background:transparent;color:var(--dim);padding:13px 0 12px;
  border:0;border-bottom:3px solid transparent;border-radius:0;font-family:var(--heading);
  font-size:15px;font-weight:600;text-transform:uppercase;letter-spacing:.09em;
  transition:color .18s ease,border-color .18s ease}
nav.tabs button:hover{color:var(--ink);background:transparent}
/* Measured off the prototype's own render: the current destination's rule is
   the ACCENT (#6f90b1 sampled, antialiased from the accent), not the
   identity navy. */
nav.tabs button.on{background:transparent;color:var(--ink);border-color:var(--accent)}
/* Ask is not a fifth destination. It sits at the far right, apart from the
   places you GO, because it is the one thing on this bar you DO. It stays
   inside nav.tabs so the router wires and marks it like any other tab; only
   its position differs.
   (This slot held Settings and a gear until the glyph came out and Logs
   earned a place of its own in the row.) */
nav.tabs .tabs-ask{margin-left:auto;display:inline-flex;align-items:center;gap:7px}
.textAction{background:transparent;border:0;padding:7px 4px;text-decoration:underline;text-underline-offset:3px}
/* A citation that opens the manual. Underlined and in the link colour
   whether it is an <a> (catalog manual) or a <button> (upload, which needs
   the auth header) -- the reader should not be able to tell which kind they
   are about to open, and both do the same thing. */
.citation-link{background:transparent;border:0;padding:0;font:inherit;
  color:var(--accent);text-decoration:underline;text-underline-offset:2px;
  cursor:pointer}
.citation-link:hover{color:var(--ink)}
.askAction{background:transparent;color:#fff;border:1px solid rgba(255,255,255,.34);padding:9px 14px}
.askAction:hover:not(:disabled){background:rgba(255,255,255,.1);border-color:#fff;filter:none}
.pageHeading{display:flex;justify-content:space-between;gap:20px;align-items:flex-end;
  margin:0 0 22px;border-bottom:0;padding-bottom:0}
.pageHeading h1{font-size:34px;line-height:1.05;letter-spacing:-.015em}
.pageHeading p{margin:6px 0 0;color:var(--dim);font-size:13px}
/* The line above the title -- date on the dashboard, context elsewhere.
   Letterspaced small caps, the design's standing eyebrow treatment. */
.eyebrow{font-family:var(--heading);font-size:12px;font-weight:600;
  text-transform:uppercase;letter-spacing:.14em;color:var(--faint);
  margin:0 0 7px;line-height:1}
.settingsLayout{display:grid;grid-template-columns:210px minmax(0,1fr);gap:24px;align-items:start}
.settingsNav{display:grid;border:1px solid var(--line);background:transparent}
.settingsNav button{padding:13px 14px;text-align:left;background:transparent;color:var(--dim);border-left:3px solid transparent}
.settingsNav button.on{color:var(--ink);background:var(--accent-soft);border-left-color:var(--accent);font-weight:600}
/* Sections across the top instead of down the side. A 210px rail plus its
   24px gutter is a whole column of days taken off a month grid, spent on
   two words that fit comfortably in one line above it. */
.settingsLayout--bar{grid-template-columns:minmax(0,1fr);gap:16px}
.settingsLayout--bar .settingsNav{display:flex;border:0;
  border-bottom:1px solid var(--line);gap:4px}
.settingsLayout--bar .settingsNav button{border-left:0;
  border-bottom:3px solid transparent;padding:10px 14px;white-space:nowrap}
.settingsLayout--bar .settingsNav button.on{background:transparent;
  border-bottom-color:var(--accent)}

/* A card is a frame, not a raised tile: hairline border, no fill, no
   shadow, square corners. Everything the console draws sits on the one page
   plane. */
.card{background:transparent;border:1px solid var(--line);
  border-radius:var(--radius);padding:17px 18px;box-shadow:var(--shadow)}
.card+.card{margin-top:14px}
/* Several views reserve a card as a status/error slot (#logStatus,
   #planStatus, #rosterStatus) and leave it empty when there is nothing to
   report. Empty, it still painted a bordered box — on the Log tab that was a
   blank panel sitting above the KPIs. An empty container should occupy no
   space rather than each view having to remember to hide its own. */
.card:empty{display:none}
.card h3{font-size:11px;text-transform:uppercase;letter-spacing:.1em;
  color:var(--faint);margin-bottom:14px;font-weight:600}
/* A panel header inside a card is a label, not a page title: the same
   letterspaced small caps as .card h3, with its one action on the right and
   a hairline under the pair. */
.card>.pageHeading{align-items:center;margin:-2px 0 4px;padding-bottom:13px;
  border-bottom:1px solid var(--line)}
.card>.pageHeading h2{font-size:12px;text-transform:uppercase;letter-spacing:.11em;
  color:var(--faint);font-weight:600;line-height:1.2}
.card>.pageHeading a{font-size:13px;text-decoration:none;white-space:nowrap}
.card>.pageHeading a:hover{text-decoration:underline}
.cols{display:grid;grid-template-columns:minmax(330px,400px) 1fr;gap:18px;align-items:start}
@media(max-width:980px){.cols{grid-template-columns:1fr}}

label.lab{display:block;font-size:12px;color:var(--dim);margin:15px 0 6px;font-weight:500}
label.lab:first-child{margin-top:0}
/* Condensed uppercase, square, hairline-bordered. The primary action is a
   filled slate plate; secondary is the same shape with no fill, so the two
   read as one family at a glance instead of two unrelated controls. */
.btn{background:var(--accent-dark);color:var(--accent-ink);
  border:1px solid var(--accent-dark);padding:10px 16px;
  font-family:var(--heading);font-size:14px;font-weight:600;line-height:1.2;
  text-transform:uppercase;letter-spacing:.07em;
  width:auto;transition:background .18s ease,color .18s ease,border-color .18s ease}
.btn:hover:not(:disabled){background:#2c455d;border-color:#2c455d;filter:none}
.btn:active:not(:disabled){background:var(--ink-deep);border-color:var(--ink-deep)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn.sec{background:transparent;color:var(--ink);border:1px solid var(--control-line)}
.btn.sec:hover:not(:disabled){background:rgba(29,31,32,.06);border-color:var(--ink);filter:none}
.btn.ok{background:var(--ok);border-color:var(--ok);color:#fff}
.btn.ok:hover:not(:disabled){background:#265a3a;border-color:#265a3a}
.btn.bad{background:var(--bad);border-color:var(--bad);color:#fff}
.btn.bad:hover:not(:disabled){background:#751a26;border-color:#751a26}
.btn.block{width:100%}
.row{display:flex;gap:8px}
.row>*{flex:1;min-width:0}

/* A step is an instruction that has NOT been carried out yet. The old rule
   washed every step in success-green, so a procedure the cook had not
   started already looked finished. Neutral card; the accent lives only in
   the numeral, which is what you scan for when you lose your place. */
.step{background:transparent;border:1px solid var(--line);
  padding:13px 14px;margin-bottom:9px}
.step:last-child{margin-bottom:0}
/* .step.ok is the ordinary state and is kept as a no-op so existing markup
   (and the mobile/web parity tests that assert on it) keeps working. There
   is deliberately no .step.hazard variant: a hazard step is a normal,
   actionable step plus a chip — it must never read as disabled or locked
   out. See web/views/ask.js. */
.step .hd{display:flex;gap:11px;align-items:flex-start}
.step .n{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:12px;font-weight:600;color:var(--accent);background:var(--accent-soft);
  border:1px solid rgba(89,128,166,.4);
  min-width:24px;height:22px;display:inline-flex;
  align-items:center;justify-content:center;flex:none;margin-top:1px}
.step .txt{flex:1;line-height:1.55;overflow-wrap:anywhere}
.tag{font-family:var(--heading);font-size:11px;font-weight:600;letter-spacing:.08em;
  padding:3px 8px;border-radius:var(--radius-chip);
  text-transform:uppercase;white-space:nowrap}
.tag.ok{background:#e7f6ee;color:var(--ok)}
.tag.hazard{background:#fff0f2;color:var(--bad);border:1px solid #f3a5b4}
.tag.unverified{background:#fff8e8;color:var(--warn)}
.cites{margin-top:9px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}
/* Only INSIDE a step does the citation row hang under the text column — the
   answer-level citation list at the bottom is not indented under anything. */
.step .cites{margin-left:35px}
.cites:empty{display:none}
.cite{font-size:11px;color:var(--dim);text-decoration:none;
  background:transparent;border:1px solid var(--line);
  padding:3px 8px;transition:color .18s ease,border-color .18s ease}
.cite:hover{color:var(--accent);border-color:rgba(22,105,168,.4)}
.cite .p{font-family:var(--mono);font-variant-numeric:tabular-nums;
  font-size:10.5px;color:var(--faint);margin-left:5px}
.cite:hover .p{color:var(--accent)}
/* The answer-level source list. Labelled, because unlabelled it reads as a
   second copy of the per-step citations rather than "every page consulted". */
.sources{margin-top:14px;padding-top:12px;border-top:1px solid var(--line)}
.sources .lbl{font-size:10px;text-transform:uppercase;letter-spacing:.1em;
  color:var(--faint);font-weight:600;margin-bottom:7px}
.hazardBanner{background:#fff1f3;border:1px solid #f3a5b4;
  padding:9px 11px;margin-top:9px;font-size:12px;color:#8f1d35}
.hazardBanner b{color:#6e1026}
.proBanner{background:#eef7fc;border:1px solid #a9d6ef;
  padding:13px 15px;margin-bottom:14px}
.proBanner h4{margin:0 0 4px;color:var(--accent);font-size:13px;font-weight:600}
.acts{display:flex;gap:7px;margin-top:11px}
.acts button{flex:1;font-size:12px;padding:9px}

.prose{background:var(--panel2);border:1px solid var(--line);
  padding:14px;font-size:13.5px;line-height:1.6}

/* The "nothing here yet" state. It is the first thing a manager sees on the
   Ask tab, so it gets a centered, deliberate treatment rather than a bare
   sentence pinned to the top-left of an empty box. */
.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:9px;min-height:190px;text-align:center;color:var(--faint);padding:24px}
.empty svg{width:26px;height:26px;stroke:currentColor;fill:none;
  stroke-width:1.5;opacity:.5}
.empty .t{color:var(--dim);font-size:13.5px}

table{width:100%;border-collapse:collapse;font-size:12.5px}
th{text-align:left;color:var(--faint);font-family:var(--heading);font-weight:600;
  font-size:11px;text-transform:uppercase;letter-spacing:.1em;padding:8px;
  border-bottom:1px solid var(--line)}
td{padding:9px 8px;border-bottom:1px solid rgba(29,31,32,.09);vertical-align:top}
tr:last-child td{border-bottom:none}
tr:hover td{background:rgba(29,31,32,.04)}
.res{font-family:var(--heading);font-size:11px;font-weight:600;padding:3px 8px;
  border-radius:var(--radius-chip);text-transform:uppercase;letter-spacing:.07em;white-space:nowrap}
.res.self{background:#e7f6ee;color:var(--ok)}
.res.contracted{background:#fff0f2;color:var(--bad)}
.res.open{background:#fff8e8;color:var(--warn)}

/* A KPI reads label-then-figure, top to bottom: the word tells you what you
   are about to look at, the figure answers. The markup order is
   value/label/summary (the render tests pin it), so the three are reordered
   here rather than in every view that emits one.

   The figure is the condensed heading face at display size -- it is the one
   thing on the dashboard meant to be read from across the office -- and it
   takes the status ink of whatever it counts. A tile with nothing to report
   stays in plain ink: only real overdue and real due-today work is allowed
   to spend colour. */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:16px}
.kpi{background:transparent;border:1px solid var(--line);
  padding:15px 16px 16px;display:flex;flex-direction:column}
.kpi .v{order:2;font-family:var(--heading);font-variant-numeric:tabular-nums;
  font-size:44px;font-weight:600;letter-spacing:-.02em;line-height:1;margin:9px 0 0}
.kpi .l{order:1;font-family:var(--heading);font-size:11.5px;color:var(--faint);
  text-transform:uppercase;letter-spacing:.13em;font-weight:600;margin-top:0;line-height:1}
.kpi .s{order:3;font-size:12px;color:var(--dim);margin-top:9px;line-height:1.35}
.kpi[data-tone="overdue"] .v{color:var(--occ-overdue-ink)}
.kpi[data-tone="today"] .v{color:var(--occ-today-ink)}

.muted{color:var(--dim);font-size:12.5px}
.tiny{font-size:11px;color:var(--faint)}
.hide{display:none}
.spin{display:inline-block;width:13px;height:13px;border:2px solid var(--line);
  border-top-color:var(--accent);border-radius:50%;animation:sp .7s linear infinite;vertical-align:-2px}
@keyframes sp{to{transform:rotate(360deg)}}

.selfAsserted{font-size:10.5px;color:var(--faint);font-style:italic}

.centerCard{max-width:420px;margin:64px auto}
.gap{height:8px}

/* Motion here is decoration, never the only signal that something changed —
   the spinner has a static border, tabs change colour as well as position.
   So it is all safe to drop outright. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important}
}

@media(max-width:640px){
  /* 44px floor for every control on a phone (UI brief rule 2). Measured with
     playwright: selects came out at 42px and the Log tab's Refresh at 33px --
     each individually "nearly right", which is how a control ends up missable
     with a gloved thumb. Applied to the shared control rules rather than
     patched per view, so a control added later inherits it. */
  select,input:not([type=checkbox]):not([type=radio]),textarea,
  .btn,.btn.sec,button.btn{min-height:44px}
  select,input:not([type=checkbox]):not([type=radio]){touch-action:manipulation}
  .btn,.btn.sec{touch-action:manipulation}
  .wrap{padding:0 0 36px}
  #appScreen>*:not(header):not(nav),
  #authScreen>*:not(header){padding-left:12px;padding-right:12px}
  header.top,nav.tabs{padding-left:12px;padding-right:12px}
  /* minmax(0,1fr), not 1fr. A bare 1fr track cannot shrink below its
     content's min-content width, and this rule also lands on
     .settingsLayout--bar, undoing the minmax it asks for: the Schedule
     calendar held its column at 463px, and mobile Chrome widened the whole
     layout viewport to 475 to fit it. Every screen reached from Schedule
     rendered clipped at the right edge on a 320px phone, and the viewport
     probe passed it, because it compares against that widened innerWidth. */
  .settingsLayout{grid-template-columns:minmax(0,1fr)}.settingsNav{display:flex;overflow-x:auto}.settingsNav button{white-space:nowrap;border-left:0;border-bottom:3px solid transparent}.settingsNav button.on{border-bottom-color:var(--accent)}
  header.top{align-items:stretch;padding:12px;gap:10px}
  .spacer{display:none}
  .who{width:100%;order:3}
  .who input{min-width:0!important;flex:1}
  .btn.sec{margin-left:auto}
  nav.tabs{width:100%;flex-wrap:wrap;padding:4px}
  nav.tabs button{flex:1 1 30%;min-width:100px;padding:9px 12px}
  .card{padding:14px}
  .row,.acts{flex-direction:column}
  .centerCard{margin:28px auto}
  .step .cites{margin-left:0}
  table{display:block;overflow-x:auto;white-space:nowrap}
}

/* --- PM calendar (plan Task 8) ------------------------------------------
 * Status is the only colour channel. Nothing depends on hue alone (WCAG
 * 1.4.1): the rail's WIDTH and a strikethrough carry status in greyscale,
 * which is the job the ▲ ● ✓ glyph column used to do before it was removed
 * to give the month grid its width back. See .cal-row[data-status] below.
 *
 * .cal-cat (category shape) still appears on the record and unit pages, but
 * no longer on calendar rows.
 */
.cal-header{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
/* flex-wrap is load-bearing, not cosmetic. Without it this legend is 407px of
   unwrappable content, and because it is the widest min-content box on the
   page, mobile Chrome EXPANDS THE WHOLE LAYOUT VIEWPORT to fit it: measured
   with playwright, window.innerWidth reported 434 on a 320, 360 and 414px
   device, and only ever on the Calendar tab. The entire console then renders
   at 434 logical px and is scaled down — every font, every tap target, on
   every surface reachable from that tab. One un-wrapping flex row was
   shrinking the app on every phone. */
.cal-legend{display:flex;flex-wrap:wrap;gap:8px 14px;font-size:11px;color:var(--faint);margin-bottom:10px}
.cal-legend .swatch{font-weight:600}

table.cal-month,table.cal-week{width:100%;border-collapse:collapse;table-layout:fixed}
table.cal-month th,table.cal-week th{text-align:left;color:var(--faint);font-weight:600;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;padding:6px 4px}
.cal-staff{white-space:nowrap;padding-right:8px}

/* The cell's data-status is the WORST status it contains, so an overdue day
   reads at a glance even collapsed — it carries only a thin outline, never
   a fill, so it never competes with the .cal-row fills inside it. */
.cal-cell{border:1px solid var(--line);vertical-align:top;padding:5px;height:74px;
  overflow:hidden;transition:height .15s ease}
.cal-cell.cal-cell-empty{background:var(--panel3);border-color:var(--panel3)}
.cal-cell.expanded{height:auto}
.cal-daynum{font-size:11px;color:var(--faint);font-weight:600;margin-bottom:3px}

/* Status colour lives ONLY here — the 3px left border plus a background
   fill on the row, never on .cal-title's text color. Category (.cal-cat) is
   drawn in currentColor so it always matches the row's ink, never a
   separate colour channel of its own. */
.cal-row{display:flex;align-items:center;gap:5px;border-left:3px solid var(--line);
  background:var(--panel2);border-radius:var(--radius-sm);padding:2px 5px;margin-bottom:3px;
  font-size:11px;color:var(--ink);overflow:hidden}
.cal-row:last-of-type{margin-bottom:0}
.cal-row .cal-glyph{flex:none;font-size:10px;line-height:1}
.cal-row .cal-cat{flex:none}
/* Category tint, applied wherever a category glyph appears (calendar rows,
   Today cards, the daily checklist, the record, unit detail). The glyph is
   drawn with currentColor, so setting `color` here tints the shape without
   touching the row's status colours, which live on the row's border and
   background. See the --cat-* tokens for why these are all cool hues. */
.cal-cat[data-cat="cleaning"],.daily-cat[data-cat="cleaning"]{color:var(--cat-cleaning)}
.cal-cat[data-cat="inspection"],.daily-cat[data-cat="inspection"]{color:var(--cat-inspection)}
.cal-cat[data-cat="filter"],.daily-cat[data-cat="filter"]{color:var(--cat-filter)}
.cal-cat[data-cat="calibration"],.daily-cat[data-cat="calibration"]{color:var(--cat-calibration)}
.cal-cat[data-cat="other"],.daily-cat[data-cat="other"]{color:var(--cat-other)}
/* reviewGlyph (task_glyphs.js, 2026-09-15): a task the manual reserves to a
   technician is scheduled now, not withheld, so it must stand out on the
   same row rather than only in a tooltip. Uses --bad, the same colour
   .tag.hazard already spends on "licensed tech" elsewhere in this app --
   one meaning, one colour, wherever it appears. */
.cal-row .cal-review,.daily-review{flex:none;color:var(--bad)}
/* Two stacked lines: the machine, then the task. Each truncates to ONE line
   and never wraps — a wrapping line changes row height and breaks the grid
   every other cell relies on.

   The machine goes FIRST and gets its own line because it used to be
   appended to the title, and the row has never had space for both: every
   sampled month-grid row overflowed, so the machine was always what got
   ellipsised. A half-read task still tells you what to go and look at; a
   machine you cannot read does not. */
.cal-row .cal-rowtext{flex:1;min-width:0;display:flex;flex-direction:column;
  gap:1px;overflow:hidden}
.cal-row .cal-machine{font-size:10px;line-height:1.25;color:var(--faint);
  font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cal-row .cal-title{min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* The glyphs sit against the first line rather than centred across both. */
.cal-row{align-items:flex-start}
.cal-row .cal-glyph,.cal-row .cal-cat{margin-top:2px}

/* Status is carried by more than colour here (WCAG 1.4.1), and since the
   ▲ ● ✓ glyph column came out it has to be carried by the row's own form:
   the left rail thickens for overdue, and done and skipped rows are struck
   through. Both are free -- they spend space the row already occupies,
   which is the whole reason the glyph column was the thing that went. */
.cal-row[data-status="overdue"]{background:var(--occ-overdue-bg);
  border-left-color:var(--occ-overdue-line);color:var(--occ-overdue-ink);
  border-left-width:6px}
.cal-row[data-status="today"]{background:var(--occ-today-bg);
  border-left-color:var(--occ-today-line);color:var(--occ-today-ink)}
.cal-row[data-status="done"]{background:var(--occ-done-bg);
  border-left-color:var(--occ-done-line);color:var(--occ-done-ink)}
.cal-row[data-status="done"] .cal-title,
.cal-row[data-status="done"] .cal-rowtext{text-decoration:line-through}
.cal-row[data-status="skipped"]{background:var(--panel3);
  border-left-color:var(--line2);color:var(--dim)}
.cal-row[data-status="skipped"] .cal-title,
.cal-row[data-status="skipped"] .cal-rowtext{text-decoration:line-through}
/* The legend shows the same three treatments rather than naming glyphs that
   no longer exist. */
.cal-legend .swatch[data-status]{border-left:3px solid var(--line);
  padding-left:7px}
.cal-legend .swatch[data-status="overdue"]{border-left-width:6px;
  border-left-color:var(--occ-overdue-line);color:var(--occ-overdue-ink)}
.cal-legend .swatch[data-status="today"]{border-left-color:var(--occ-today-line);
  color:var(--occ-today-ink)}
.cal-legend .swatch[data-status="done"]{border-left-color:var(--occ-done-line);
  color:var(--occ-done-ink);text-decoration:line-through}
/* 'upcoming' takes .cal-row's own neutral default — no override needed. */

/* The routine band is the quiet 'upcoming' treatment even when the tasks it
   summarizes are, individually, overdue — it exists specifically so a
   30-times-repeated daily chore does not shout for attention 30 times. */
.cal-row.cal-routine{background:var(--panel2);border-left-color:var(--line)}

.cal-cell[data-status="overdue"]{outline:1px solid var(--occ-overdue-line)}
.cal-cell[data-status="today"]{outline:1px solid var(--occ-today-line)}

/* Measured live at 320px: 282x20 -- 24px under the glove-sized touch floor
   (UI brief rule 2). Padding does the work of reaching 44px; the text stays
   small so it still reads as a caption, not a button. */
.cal-more{display:flex;align-items:center;width:100%;min-height:44px;
  background:none;border:none;padding:6px 0;font-size:10.5px;color:var(--accent);
  text-align:left;cursor:pointer;touch-action:manipulation}
.cal-more:hover{text-decoration:underline}

/* Task L3: the "include daily cleaning" toggle + the band-echo banner it
   controls. min-height on the LABEL, not just the checkbox, so the whole
   row (box or word) is the 44px tap target (UI brief rule 2) -- same
   label-is-the-hit-area pattern as the daily checklist's own checkboxes. */
.cal-cadence-toggle{display:flex;align-items:center;gap:8px;min-height:44px;
  padding:4px 0;font-size:12.5px;color:var(--ink);touch-action:manipulation}
.cal-cadence-toggle input{width:20px;height:20px;flex:none}
.cal-cadence-banner{font-size:12px;color:var(--faint);margin:2px 0 10px;min-height:1em}
/* Today view isn't part of the periodic/all toggle (web/views/calendar.js
   renderToday always requests 'all') -- hiding both here rather than
   leaving a control on screen that does nothing on that tab. */
.cal-page[data-view="today"] .cal-cadence-toggle,
.cal-page[data-view="today"] .cal-cadence-banner{display:none}

/* Task N1: below 640px table.cal-month (measured 472px inside a 266px
   container -- still sideways-scrolling however it's wrapped) is replaced
   by .cal-agenda, the date-grouped card list calendar.js paints alongside
   it from the same data (UI brief rule 5: card layout, not a scroll
   wrapper, for a 7-column grid this narrow). Mobile-first: cards are the
   base rule, the table only reappears at Marie's desktop width. */
.cal-agenda{display:block}
table.cal-month{display:none}
@media(min-width:640px){
  .cal-agenda{display:none}
  table.cal-month{display:table}
}
.cal-page[data-view="agenda"] table.cal-month{display:none}
.cal-page[data-view="agenda"] .cal-agenda{display:block}
.cal-agenda-card{background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius);padding:10px 12px;margin-bottom:10px}
.cal-agenda-card:last-child{margin-bottom:0}
.cal-agenda-card[data-status="overdue"]{outline:1px solid var(--occ-overdue-line)}
.cal-agenda-card[data-status="today"]{outline:1px solid var(--occ-today-line)}
.cal-agenda-date{font-size:12px;font-weight:700;color:var(--faint);
  text-transform:uppercase;letter-spacing:.04em;margin-bottom:6px}
.cal-agenda .cal-row{font-size:13px;padding:6px 8px;margin-bottom:6px;min-height:32px}

.cal-today-card{background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius);padding:12px;margin-bottom:10px}
.cal-today-card:last-child{margin-bottom:0}
.cal-today-card .cal-row{font-size:13px;padding:6px 8px;margin-bottom:10px}
.cal-today-card .cal-markdone{min-height:44px}

/* MOVE ONE JOB TO ANOTHER DAY (pm.js's moveOccurrence). The button lives on
   every actionable row including the packed month grid, so it has to be small
   enough not to compete with the title -- it is the quietest thing in the row
   until hovered. .cal-row is flex, so `margin-left:auto` pins it right and the
   title keeps the space it had.
   The inline form is `flex-basis:100%` so it wraps onto its OWN line inside
   the row rather than squeezing the title out of a 1/7th-width month cell. */
.cal-row .cal-move{flex:none;margin-left:auto;padding:1px 6px;font-size:10px;
  line-height:1.5;opacity:.55;min-height:0}
.cal-row:hover .cal-move,.cal-move:focus{opacity:1}
.cal-row{flex-wrap:wrap}
.cal-moveform{flex-basis:100%;display:flex;gap:4px;align-items:center;margin-top:4px}
.cal-moveform .cal-movedate{flex:1 1 auto;min-width:0;padding:2px 4px;font-size:11px}
.cal-moveform .btn{padding:2px 8px;font-size:11px;min-height:0}
/* Today's card has room, so the two actions sit side by side at full tap size
   -- 44px is the same minimum .cal-markdone already claims above. */
.cal-actions{display:flex;gap:8px;align-items:stretch}
.cal-actions .cal-markdone{flex:1 1 auto}
.cal-actions .cal-move{flex:none;min-height:44px;padding:0 14px;opacity:1;font-size:13px}
.cal-today-card .cal-moveform{margin-top:8px}
/* The sub-step checklist sub-project A's rollup exists to preserve (spec
   §5.2) -- today view only, never in a month cell (see the comment beside
   its render call in calendar.js). */
.cal-steps{list-style:none;margin:0 0 10px;padding:0 0 0 20px}
.cal-steps li{font-size:12.5px;color:var(--dim);padding:3px 0;position:relative}
.cal-steps li::before{content:"☐";position:absolute;left:-18px;color:var(--faint)}

.cal-viewbtn.on{background:var(--accent-soft);color:var(--accent);border-color:transparent}

.pm-sheet-backdrop{position:fixed;inset:0;z-index:80;background:rgba(17,29,41,.52);display:flex;
  justify-content:flex-end;align-items:stretch}
.pm-sheet{width:min(520px,100%);height:100%;overflow:auto;background:var(--panel);padding:24px;
  box-shadow:-12px 0 36px rgba(42,35,30,.20)}
.pm-sheet-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  border-bottom:1px solid var(--line);padding-bottom:14px;margin-bottom:16px}
.pm-sheet-head h2{margin:2px 0 0}.pm-sheet section{border-bottom:1px solid var(--line);padding:8px 0 16px}
.pm-sheet-action{display:grid;gap:9px}.pm-sheet-action input,.pm-sheet-action textarea{width:100%}
.pm-instructions{padding-left:22px}.pm-instructions li{padding:4px 0}.pm-source-quote{margin:8px 0;
  border-left:3px solid var(--accent);padding:7px 10px;background:var(--panel2);font-size:12px}
.pm-sheet-danger{border-bottom:0!important}.cal-open,.daily-task-open,.maintenance-task-open{appearance:none;
  background:transparent;border:0;color:inherit;font:inherit;text-align:left;padding:0;cursor:pointer}
.cal-open{min-width:0;flex:1}.cal-open:hover .cal-title,.daily-task-open:hover{text-decoration:underline}
@media(max-width:640px){.pm-sheet{padding:18px}.pm-sheet-backdrop{align-items:flex-end}.pm-sheet{height:min(88vh,760px)}}

@media(max-width:640px){
  .cal-cell{height:auto;min-height:56px}
  table.cal-week{display:block;overflow-x:auto}
  /* The month/week steppers measured 41px wide -- a chevron is a small glyph
     and the button had shrunk to fit it. Width is the axis that fails here,
     not height. */
  #calPrev,#calNext{min-width:44px}
}

/* On-screen footprint for the two print-only hooks below (plan Task 9) —
   .cal-checkbox stays weightless on screen (it has no content until print).
   .cal-photo is the today-card's 48px unit-photo slot (spec §6.3) -- shares
   the same swappable-inner-element pattern as .unit-photo below (see that
   comment for why), sized separately since the today card and the unit list
   call for different footprints. */
.cal-photo{width:48px;height:48px;flex:none;border-radius:var(--radius-sm);background:var(--panel3);
  border:1px solid var(--line);display:flex;align-items:center;justify-content:center;overflow:hidden}
.cal-photo img{width:100%;height:100%;object-fit:cover;display:block}
.cal-photo svg{width:42%;height:42%;stroke:currentColor;fill:none;stroke-width:1.5;color:var(--faint)}
.printed-date{color:var(--faint)}
.cal-kitchen-label{font-weight:600;margin-left:10px}

/* --- unit photos + fallback tile (plan "Sub-project C" Task 3, spec §7) ---
 * .unit-photo is a fixed-size wrapper shared by the unit list and the
 * per-unit header; only its INNER element ever swaps (the category-icon
 * <svg> <-> a real <img>) so a photo arriving late never reflows the row --
 * see the layout-shift guarantee in web/views/units.js. Sizes per spec §7:
 * 64-96px in the unit list / per-unit header (size-72 below); 48-56px on
 * the today-checklist card (.cal-photo above handles that one). Never in
 * month/week calendar cells (spec §6.3) -- .cal-photo/.unit-photo are never
 * used there.
 */
.unit-photo{flex:none;border-radius:var(--radius);background:var(--panel3);border:1px solid var(--line);
  display:flex;align-items:center;justify-content:center;overflow:hidden}
.unit-photo img{width:100%;height:100%;object-fit:cover;display:block}
/* The fallback glyph sits centred at ~40% of tile size, tinted --faint so it
   reads as a deliberate placeholder rather than a broken/missing asset. */
.unit-photo svg{width:40%;height:40%;stroke:currentColor;fill:none;stroke-width:1.5;color:var(--faint)}
.unit-photo.size-72{width:72px;height:72px}
.unit-photo.size-48{width:48px;height:48px}

.units-table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--radius)}
.units-table{width:100%;border-collapse:collapse;min-width:760px}
.units-table th{text-align:left;font-family:var(--sans);font-size:11px;
  font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
  padding:12px 16px;border-bottom:1px solid var(--line)}
.units-table td{padding:13px 16px;border-bottom:1px solid var(--line);font-size:13px;
  color:var(--dim);vertical-align:middle}
.units-table tbody tr:last-child td{border-bottom:0}
.units-record{cursor:pointer}
.units-record:hover,.units-record:focus-visible{background:var(--panel3);outline:none}
.units-unit-cell{display:flex;align-items:center;gap:10px;min-width:180px}
.units-record-link{display:block;color:var(--ink);font-size:14px;font-weight:600;text-decoration:none}
.units-record-link:hover{text-decoration:underline}
.units-unit-cell span,.units-table td small{display:block;color:var(--faint);font-size:11px;
  letter-spacing:.04em;text-transform:uppercase}
.units-doc{display:inline-block;font-family:var(--sans);font-size:11px;
  font-weight:600;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:var(--radius-chip);
  background:var(--panel3);color:var(--dim)}
.units-doc[data-state="ready"]{background:var(--occ-done-bg);color:var(--ok)}
.units-due[data-state="overdue"]{color:var(--occ-overdue-ink);font-weight:700}

@media(max-width:720px){
  .units-table-wrap{overflow:visible;border:0}
  .units-table{display:block;min-width:0}
  .units-table thead{display:none}
  .units-table tbody{display:grid;gap:10px}
  .units-table .units-record{display:grid;grid-template-columns:1fr 1fr;background:var(--panel2);
    border:1px solid var(--line);border-radius:var(--radius);padding:12px;gap:10px 16px}
  .units-table td{display:block;padding:0;border:0;min-width:0}
  .units-table td:first-child{grid-column:1/-1;padding-bottom:8px;border-bottom:1px solid var(--line)}
  .units-table td::before{content:attr(data-label);display:block;font-family:var(--sans);
    font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);
    margin-bottom:2px}
  .units-table td:first-child::before{display:none}
  .units-unit-cell{min-width:0}
}

/* The accepted maintenance plan (Schedule > Maintenance plan) gets the same
   treatment on a phone: one card per task, each value under its column's
   word. As a table it was six unwrappable columns -- the shared
   `table{white-space:nowrap}` phone rule made it 1200px wide -- inside a
   266px card, so a cook had to swipe sideways to find "Next due" and the
   Details button. The data-label words are the <th> words, verbatim.
   Unit, task and frequency take a full row each -- the frequency carries a
   provenance chip, and in a half-width cell "Recommended" broke mid-word
   -- then next due and last done pair up. Details is pinned to the card's
   top right, beside the unit, rather than spending a row of its own. */
@media(max-width:640px){
  #planTable{display:block;white-space:normal;overflow:visible}
  #planTable thead{display:none}
  #planTable tbody{display:grid;gap:10px}
  #planTable tr{position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:10px 16px;padding:12px 14px;background:var(--panel);border:1px solid var(--outline);
    border-radius:var(--radius);box-shadow:none}
  #planTable td{display:block;padding:0;border:0;min-width:0;overflow-wrap:anywhere}
  #planTable td:nth-child(-n+3),#planTable td[colspan]{grid-column:1/-1}
  #planTable td:first-child:not([colspan]){padding-right:68px}
  #planTable td:nth-child(2){font-weight:600;color:var(--ink)}
  #planTable td[data-label]::before{content:attr(data-label);display:block;font-family:var(--sans);
    font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);
    margin-bottom:2px}
  #planTable td:last-child:not([colspan]){position:absolute;top:10px;right:12px}
  /* A tapped card keeps :hover on a touch screen; the row tint would sit
     on it as a patchwork of cell rectangles. */
  #planTable tr:hover td{background:transparent}
  /* A chip is nowrap everywhere else; here a long one may wrap, but only
     between words -- never "RECOMMENDE / D". */
  #planTable .tag{display:inline-block;white-space:normal;overflow-wrap:normal}
}

.unit-group{margin-bottom:18px}
/* Heading and its one action on a line, so "Reorder" reads as something
   you turn on rather than something the list is always doing. */
.units-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.units-head h3{margin:0}
.units-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}
.unit-location{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--faint);margin:0 0 8px}
.unit-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
/* Photo, then nickname, then make/model -- that visual priority (plan Task
   4 Step 3). */
/* A row is a link, and the cursor says so. It said `grab` on every row,
   permanently, which is half of why Dylan asked "why drag-and-move" about
   this screen: the list looked like a thing to rearrange even to someone
   who only wanted to open a machine. `grab` now appears only inside
   Reorder mode, on rows that really are handles. */
.unit-row{display:flex;align-items:center;gap:12px;background:var(--panel2);
  border:1px solid var(--line);border-radius:var(--radius);padding:8px 12px;cursor:pointer}
.unit-row.reordering{cursor:grab}
.unit-row.dragging{opacity:.5}
/* Cross-location drop is refused (review FIX 4, web/views/units.js) --
   this is the affordance that shows it DURING the drag, not silently on
   release. */
.unit-row.drop-blocked{outline:2px dashed var(--bad);outline-offset:-2px;cursor:not-allowed}
.unit-drag{color:var(--faint);font-size:15px;flex:none;cursor:grab;user-select:none}
.unit-info{min-width:0}
.unit-nickname{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.unit-submodel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Per-unit status badge (Task M1's rollup, surfaced on the list -- team-lead
   review). Overdue is the ONLY state that shouts, reusing the SAME
   --occ-overdue-* tokens calendar.js already defines -- no new colours. A
   settled unit reuses the calm --occ-done-* and --ok tokens (good news, not
   an empty/error state). NOTE the spelling: writing that pair as
   `--occ-done-*` immediately followed by `/--ok` closes this comment early
   -- the `*` and `/` become a terminator -- and every CSS rule after this
   point is silently dropped. That is not hypothetical: it happened, and it
   took out the mobile bottom bar, the More sheet, the daily checklist, the
   calendar agenda cards and the print stylesheet in one go, with no error
   anywhere. Found by the playwright probe, which measured the desktop tab
   bar reappearing at 320px.
   'due' (open work, nothing overdue) stays neutral. The
   glyph plus the word carry the fact -- colour is never the only channel
   (WCAG 1.4.1, see web/views/units.js's unitStatusBadge). Wraps rather than
   truncating (no white-space:nowrap) so a long "next periodic due" suffix
   never pushes the card into horizontal overflow at 320px. */
.unit-status{display:inline-flex;align-items:center;gap:5px;font-size:11px;
  font-weight:600;padding:3px 8px;border-radius:var(--radius-pill);margin-top:5px;
  max-width:100%;overflow-wrap:anywhere}
.unit-status-glyph{flex:none;font-size:10px;line-height:1}
.unit-status[data-state="overdue"]{background:var(--occ-overdue-bg);
  color:var(--occ-overdue-ink);border:1px solid var(--occ-overdue-line)}
.unit-status[data-state="due"]{background:var(--panel3);color:var(--dim);
  border:1px solid var(--line);font-weight:500}
.unit-status[data-state="settled"]{background:var(--occ-done-bg);color:var(--ok);
  border:1px solid var(--occ-done-line);font-weight:500}

/* --- print (plan Task 9) --------------------------------------------------
 * Not a description of print rules — these rules. See the plan's own
 * commentary (docs/superpowers/plans/2026-08-30-subproject-b-pm-calendar.md,
 * Task 9) for why each one is load-bearing; kept verbatim here.
 */
@media print {
  /* Chrome/Safari strip background-color by default ("Print background
     graphics" is opt-in), and .card/.step/.banner all set explicit
     backgrounds that otherwise survive. !important is required, not tidiness. */
  .wrap, nav.tabs, header.top .who, .btn { display: none !important; }
  body, .cal-page { background: #fff !important; }
  .cal-cell, .cal-cell * {
    background: transparent !important; color: #000 !important; box-shadow: none !important;
  }

  /* Status by border STYLE as well as width. Two 4px black borders are
     identical in grayscale, so solid/double/grey is what actually
     distinguishes them on a photocopy. Colour carries nothing here. */
  .cal-cell { border: 1px solid #999; }
  .cal-row[data-status="overdue"] { border-left: 4px solid #000; }
  .cal-row[data-status="today"]   { border-left: 4px double #000; }
  .cal-row[data-status="done"]    { border-left: 4px solid #999; }

  /* Without the :last-child exception this emits a trailing blank page. */
  .cal-page { page-break-after: always; }
  .cal-page:last-child { page-break-after: auto; }

  .cal-header { display: flex; justify-content: space-between; font-size: 11pt; }
  /* The wall copy needs its provenance -- which kitchen, run off when --
     because a printed month on a noticeboard has no other way to say. */
  .screen-hidden { display: inline !important; }
  .cal-header .printed-date::before { content: "Printed "; }

  /* position: fixed puts the legend on EVERY page. Placed in normal flow it
     appears only on the last one — and a wall reader has no other way to
     learn the code. */
  .cal-legend {
    position: fixed; bottom: 0; left: 0; right: 0;
    display: flex; gap: 16px; font-size: 9pt;
    border-top: 1px solid #000; padding-top: 4px;
  }
  /* The legend must encode status exactly as the ROWS above it do, or the
     wall copy explains a code it does not use. On screen that is rail colour
     plus width plus strikethrough; in print colour carries nothing, so the
     rows switch to border STYLE (solid / double / grey, just above) and the
     swatches have to switch with them. They used to be literal "▲ ● ✓" text,
     which survived print for free; since the glyphs came out, this block is
     what keeps the two halves saying the same thing. */
  .cal-legend .swatch { font-weight: 700; }
  .cal-legend .swatch[data-status] { border-left-style: solid; padding-left: 7px; }
  .cal-legend .swatch[data-status="overdue"] { border-left: 4px solid #000; color: #000; }
  .cal-legend .swatch[data-status="today"]   { border-left: 4px double #000; color: #000; }
  .cal-legend .swatch[data-status="done"]    { border-left: 4px solid #999; color: #000;
    text-decoration: line-through; }

  .cal-checkbox::before { content: "☐"; font-size: 15px; }  /* pen-fillable */

  .cal-photo { max-width: 40px; max-height: 40px; }
  .cal-page[data-view="month"] .cal-photo { display: none; }
}

/* --- Mobile navigation (day plan Task N2) --------------------------------
 *
 * Nine tabs in three wrapped rows is not navigation. Measured at 320px it was
 * 142px of a 720px screen -- 19% -- before a single row of content. The
 * ruleset gives the bottom bar one column per destination; the rest live behind
 * More.
 *
 * Fixed to the bottom because that is where a thumb is at 9pm with one hand
 * on a sheet pan, and padded by env(safe-area-inset-bottom) because a bar
 * underneath the iPhone home indicator is not merely ugly, it is untappable.
 *
 * Desktop is untouched: this whole block is display:none above 640px, and
 * nav.tabs is unchanged there. Marie's console does not move.
 */
.mobilenav{display:none}
.morepanel{display:none}

@media(max-width:640px){
  body.signed-in nav.tabs{display:none}

  body.signed-in .mobilenav{
    /* One column per button, counted from the markup rather than pinned to a
       number here. It was repeat(5,1fr) against four buttons, then six were
       added and the sixth wrapped to a second row -- which is the exact cost
       ("19% of a phone screen before any content") this bar was rearranged to
       avoid. grid-auto-flow:column cannot miscount. */
    display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
    position:fixed;left:0;right:0;bottom:0;z-index:40;
    background:var(--panel);border-top:1px solid var(--line);
    padding:4px 4px calc(4px + env(safe-area-inset-bottom));
    box-shadow:0 -2px 14px rgba(42,35,30,.09);
  }
  .mobilenav button{
    /* 48px, not 44: 44 is the iOS floor and this is used with gloves on.
       touch-action:manipulation removes the 300ms double-tap delay, which on
       a checklist reads as the app ignoring you. */
    min-height:48px;touch-action:manipulation;
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
    background:none;color:var(--dim);font-size:10px;font-weight:600;
    padding:4px 1px;border-radius:var(--radius);
    /* Six labels share the width now. "Equipment" is the long one; let it
       shrink rather than spill into its neighbour. */
    min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  }
  .mobilenav button.on{color:var(--accent);background:var(--accent-soft)}
  .mobilenav button:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}

  /* The bar is fixed, so the last row of content would sit underneath it
     forever without this. 56px bar + the inset. */
  body.signed-in .wrap{padding-bottom:calc(76px + env(safe-area-inset-bottom))}

  body.signed-in .morepanel:not(.hide){
    display:grid;gap:6px;
    position:fixed;right:8px;left:8px;bottom:calc(64px + env(safe-area-inset-bottom));z-index:41;
    background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
    padding:8px;box-shadow:var(--shadow);
  }
  .morepanel button{
    min-height:48px;touch-action:manipulation;text-align:left;
    background:var(--panel2);color:var(--ink);border:1px solid var(--line);
    padding:0 14px;font-weight:600;
  }
  .morepanel button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
}

/* --- Today / daily closing checklist (day plan Task L2) ------------------
 *
 * Grouped by UNIT, not by task, because Theo walks the kitchen unit by unit.
 * Sub-steps are collapsed by default: 28 pilot tasks carry them and expanding
 * them all buries the list this surface exists to shorten.
 */
.daily-header{display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  flex-wrap:wrap;margin-bottom:12px}
.daily-progress{font-variant-numeric:tabular-nums}
.daily-unit-card{margin-bottom:12px;padding:12px}
.daily-unit-card:last-child{margin-bottom:0}
.daily-unit-header{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:8px}
.daily-unit-info{min-width:0;flex:1}
.daily-unit-name{font-weight:600;overflow-wrap:anywhere}
.daily-unit-sub{overflow-wrap:anywhere}
.daily-unit-count{white-space:nowrap;font-variant-numeric:tabular-nums}
.daily-task-list{list-style:none;margin:0;padding:0}
.daily-task-row{border-top:1px solid var(--line);padding:2px 0;display:grid;grid-template-columns:auto minmax(0,1fr);align-items:start}
.daily-task-row:first-child{border-top:none}
/* The whole label is the hit target, at 48px, so a gloved thumb aimed at the
   task title does not miss a 16px checkbox. */
.daily-check-label{display:flex;align-items:center;gap:10px;min-height:48px;
  touch-action:manipulation;cursor:pointer;padding:2px 0}
.daily-task-open{min-height:48px;display:flex;align-items:center;width:100%;padding:2px 6px}
.daily-checkbox{flex:none}
.daily-task-title{flex:1;min-width:0;overflow-wrap:anywhere}
/* .daily-cat is the <svg> itself (categorySvg puts className on the element),
   so the old `.daily-cat svg{width:10px}` descendant rule matched nothing and
   the glyph has always drawn at the 12px its width/height attributes ask for
   -- which is what the checklist wants at its larger row height. Rule removed
   rather than "fixed" to 10px: the 12px was the intended size. */
.daily-cat{flex:none}
.daily-steps-toggle{min-height:44px;touch-action:manipulation;background:none;
  color:var(--dim);font-size:12px;padding:0 6px;display:inline-flex;align-items:center;gap:6px}
.daily-steps-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.daily-chevron{transition:transform .18s ease;display:inline-block}
.daily-steps-toggle[aria-expanded="true"] .daily-chevron{transform:rotate(90deg)}
.daily-steps-wrap{padding:0 0 6px 6px;grid-column:2}
.daily-steps{list-style:none;margin:0;padding:0}
.daily-steps li{font-size:12.5px;color:var(--dim);padding:3px 0;position:relative;padding-left:18px}
.daily-steps li::before{content:"☐";position:absolute;left:0;color:var(--faint)}
/* Keyed off data-status, NOT a .done class. taskRowHtml has only ever
   written data-status="done"; the old `.daily-task-row.done` selector
   matched nothing, so a completed task rendered identically to an open one
   and the only sign anything had happened was the tick itself. Confirmed in
   the running console before the fix: 8 of 24 rows done, 0 with the class,
   computed text-decoration `none`. data-status is also what .cal-row uses,
   so this keeps one convention across the views. */
.daily-task-row[data-status="done"] .daily-task-title{color:var(--faint);text-decoration:line-through}

/* --- Unit detail (day-plan Task M2/M4) ------------------------------------
 * Marie navigates by time, Theo by equipment, Sam by symptom -- all three
 * converge here (day-plan §1), which is why it borrows visual language from
 * three other surfaces rather than inventing a fourth: .unit-photo (units.js),
 * .cal-row/.cal-cat status colour+shape (calendar.js), .daily-steps* disclosure
 * (daily.js). Only the NEW shapes below are new rules.
 */
/* The unit page shares the one measure too -- capped at 1180 it was the one
   screen that still sat in a narrower column than the tabs above it. */
.udet-page{max-width:none}
.udet-back{display:inline-flex;align-items:center;min-height:44px;
  font-weight:600;font-size:13px;touch-action:manipulation}
.udet-hero-grid,.udet-content-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:24px;align-items:start}
.udet-content-grid{margin-top:24px}
.udet-hero-grid>.card+.card{margin-top:0}
.udet-column{min-width:0}
@media(max-width:900px){
  .udet-hero-grid,.udet-content-grid{grid-template-columns:1fr;gap:14px}
  .udet-content-grid{margin-top:14px}
}
/* .udet-header is the CARD (block); .udet-header-row is the photo+text flex
   row inside it -- split so the photo-capture controls below (Task P1) can
   sit under that row, full width, without becoming a third flex child. */
.udet-header-row{display:flex;gap:14px;align-items:flex-start}
.udet-info{min-width:0;flex:1}
.udet-name{overflow-wrap:anywhere}
.udet-sub,.udet-serial,.udet-badges{overflow-wrap:anywhere;margin-top:2px}
.udet-serial{color:var(--dim)}
.udet-badges{color:var(--warn)}

/* Staff-snapshot photo capture (Task P1) -- a compact control, deliberately
   NOT a full-width block button, so it never competes with the two doors
   (below) for visual weight or space above the fold at 320px. The real
   file input is visually hidden; its LABEL is styled as the button (the
   standard accessible pattern -- a native file input cannot be restyled
   consistently across browsers). */
.udet-photo-actions{margin-top:12px;display:flex;align-items:center;
  gap:10px;flex-wrap:wrap}
.udet-photo-btn{min-height:44px;touch-action:manipulation;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;padding:8px 14px}
.udet-photo-input{display:none}
.udet-photo-progress{flex:1;min-width:80px;height:6px;background:var(--panel3);
  border-radius:var(--radius-pill);overflow:hidden}
.udet-photo-progress-bar{height:100%;background:var(--accent);
  transition:width .2s ease}
.udet-photo-error{width:100%;color:var(--bad);overflow-wrap:anywhere}

/* Sam's two doors (day-plan Task M4) -- above the schedule, both >=44px
   regardless of viewport (the brief's exact wording), not gated to the
   mobile breakpoint the way the rest of this file's control-sizing rules are. */
.udet-doors{display:flex;flex-direction:column;gap:10px}
.udet-door{min-height:44px;touch-action:manipulation}

/* Remove this unit.

   The card sits LAST on the page and, at rest, holds one quiet secondary
   button -- the same weight as "Something wrong? Get help" above it. Nothing
   red is on screen until someone has actually asked to remove something.
   Spending red on the resting state would make the most destructive control
   in the console also its most eye-catching, on a tablet operated with
   gloves.

   Once confirming, the card takes a --bad hairline and a tinted ground so it
   reads as a different mode rather than one more card in the stack, and the
   two choices sit side by side at equal size: "Keep it" is not a whispered
   link, because backing out must be at least as easy as going through. Both
   clear 44px at every viewport, matching .udet-door's reasoning. */
.udet-remove{margin-top:14px}
.udet-remove-open{min-height:44px;touch-action:manipulation;color:var(--dim)}
.udet-remove[data-stage]{border-color:var(--bad);background:#fff7f8}
.udet-remove-h{margin:0 0 6px;font-size:14px;color:var(--ink);overflow-wrap:anywhere}
.udet-remove-what{margin:0 0 10px;color:var(--dim);line-height:1.5}
.udet-remove-error{margin:8px 0 0;color:var(--bad);overflow-wrap:anywhere}
.udet-remove-actions{display:flex;gap:8px;flex-wrap:wrap}
.udet-remove-actions .btn{flex:1 1 auto;min-height:44px;min-width:120px;
  touch-action:manipulation;overflow-wrap:anywhere}

.udet-section{margin-top:16px}
.udet-section:first-child{margin-top:0}
.udet-section-h{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--faint);font-weight:600;margin:0 0 8px}
.udet-row{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);
  padding:10px 12px;margin-bottom:8px}
.udet-row:last-child{margin-bottom:0}
/* Sizing/spacing only -- deliberately does NOT touch .cal-row's own
   background/border-left, which is where status colour (overdue/today/
   upcoming) actually lives; overriding it here would fight
   .cal-row[data-status] on equal specificity by source order. Same pattern
   as .cal-today-card .cal-row above. */
.udet-row .cal-row{font-size:13.5px;padding:8px 10px;margin-bottom:8px}
.udet-row .daily-steps-wrap{padding-left:18px}
.udet-markdone{min-height:44px}

/* --- Ask, pre-scoped from a unit (day-plan Task M4) -----------------------
 * Sam never picks a machine from a dropdown one-handed -- the dropdown is
 * swapped for a static readout plus an explicit "Change" escape hatch. */
.ask-preset-row{display:flex;align-items:center;justify-content:space-between;
  gap:10px;background:var(--panel2);border:1px solid var(--line);
  border-radius:var(--radius);padding:10px 12px;margin-bottom:12px}
.ask-preset-label{font-size:13px;font-weight:600;overflow-wrap:anywhere}

/* A collapsed run of future daily occurrences on the unit screen (Task M2
   follow-up). Reads as context, not as a call to action, and carries no
   control: completing work that is not due yet re-anchors the task from today
   and supersedes today's real row, so a tappable future daily is a foot-gun.
   Today's copy of the same task sits in "Due today" above with its button. */
.udet-band .cal-row{opacity:.72}
.udet-band-note{margin:2px 0 0 22px;overflow-wrap:anywhere}

/* An upcoming (non-daily) row's due date -- same indent as udet-band-note,
   same reason: it sits under the icon+title row it belongs to. Added
   because upcoming periodic rows previously carried NO date at all, so
   several occurrences of the same task (e.g. a weekly filter clean's next
   dozen dates) rendered as identical, undated, unticked-looking duplicates
   (Fable's e2e finding). */
.udet-upcoming-date{margin:2px 0 0 22px}

/* --- Schedule start + feed scope (sub-project Q, web half) ----------------
 * Two cards, siblings of #onbTimezoneCard: "When should scheduled work
 * start?" and "Calendar subscription includes". Both use plain <label>
 * wraps around native inputs so the WHOLE row is the tap target (>=44px),
 * not just the 16px checkbox/radio circle -- the same reasoning as
 * .udet-photo-btn above, just for a row instead of a button. */
.onbsch-row{display:flex;align-items:flex-start;gap:10px;
  min-height:44px;padding:9px 2px;cursor:pointer}
.onbsch-row input{margin-top:2px;flex:none}
.onbsch-row .txt{flex:1;overflow-wrap:anywhere}
.onbsch-row .txt .sub{display:block;color:var(--faint);font-size:11px;margin-top:2px}
/* Field-level error, directly under the control it belongs to -- the UI
   brief's "errors render next to the field that caused them", not only in
   a banner at the top. */
.onbsch-err{color:var(--bad);font-size:11.5px;margin-top:4px}
.onbsch-note{color:var(--faint);font-size:11px;margin:4px 0 0}
/* The live "your Today list will be empty" warning under Start date --
   --warn, not --faint/--bad: this is a real heads-up (not routine helper
   text, not an error), same token unit_detail.js's udet-badges uses for
   "next due" callouts. */
.onbsch-future{color:var(--warn)}

/* Manuals list on the console unit page. Rows, not buttons: each one is a
   real link to a real PDF, and it should behave like one (middle-click,
   copy link address). The arrow says it leaves for a new tab. */
.udet-manual-list{display:flex;flex-direction:column;gap:6px}
/* Two elements share this class: an <a> for catalog manuals and a <button>
   for uploaded ones, which need an auth header a bare navigation cannot send
   (see manualsCardHtml). font/cursor/text-align/width are here so the button
   is visually indistinguishable from the link -- a browser's default button
   chrome would otherwise make one row in the list look like a different
   control than the row above it. */
.udet-manual{display:flex;align-items:center;gap:10px;min-height:44px;
  padding:8px 12px;border:1px solid var(--line);border-radius:var(--radius);
  background:var(--panel2);color:var(--ink);text-decoration:none;font-size:13px;
  font-family:inherit;text-align:left;width:100%;cursor:pointer}
.udet-manual:hover{border-color:var(--accent);color:var(--accent)}
.udet-manual-icon{flex:none;color:var(--faint);font-size:13px}
.udet-manual-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.udet-manual-go{flex:none;color:var(--faint);font-size:12px}
/* A document with no bytes to open yet (still processing, or failed) --
   deliberately its OWN class, not a disabled `.udet-manual`, so it never
   picks up that class's link hover/cursor styling. Dashed border reads as
   "placeholder", not "broken control". */
.udet-manual-pending{display:flex;align-items:center;gap:10px;min-height:44px;
  padding:8px 12px;border:1px dashed var(--line);border-radius:var(--radius);
  background:var(--panel3);color:var(--dim);font-size:13px}

/* --- Document review (plan task A0.3) --------------------------------------
 * Per-document detail below the open link/button: doc_type + ingestion
 * badges, page count, source URL, match_reason, Delete -- shared markup
 * (web/views/shared/doc_review.js) used by both the unit page's Manuals
 * card and the Documents tab's per-unit review list, so the two surfaces
 * cannot drift into showing this differently.
 */
.doc-review-row{border-top:1px dashed var(--line);margin-top:8px;padding-top:8px}
.udet-manual-list .doc-review-row:first-child{border-top:none;margin-top:0;padding-top:0}
.doc-review-row .doc-review-main{margin-bottom:6px}
.doc-review-meta{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-bottom:4px}
.doc-review-pages{white-space:nowrap}
.doc-review-source{overflow-wrap:anywhere;margin-bottom:2px}
.doc-review-reason{margin-bottom:6px}
/* Colour rides the SAME tone vocabulary as the badges beside it (good/check/
   bad/unknown) -- redundant with the words, never the only signal. */
.doc-review-reason[data-tone="good"]{color:var(--ok)}
.doc-review-reason[data-tone="check"]{color:var(--warn)}
.doc-review-reason[data-tone="bad"]{color:var(--bad)}
.doc-review-reason[data-tone="unknown"]{color:var(--faint)}
.doc-review-actions{margin-top:4px}
.doc-review-actions .btn{min-height:36px;padding:7px 12px;font-size:12.5px}
.doc-review-confirm{background:#fff7f8;border:1px solid var(--bad);border-radius:var(--radius);padding:8px 10px}
.doc-review-confirm p{margin:0 0 6px}
.doc-review-confirm .row{gap:6px}
.doc-review-confirm .btn{flex:1 1 auto;min-width:100px}
/* A neutral badge for a tone this table has no colour opinion about yet
   (match_reason absent on an older document, an unrecognized doc_type) --
   visible without reading as either good or bad news. */
.tag.info{background:var(--panel3);color:var(--dim);border:1px solid var(--line)}

.udet-manual-research{margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}

/* Toasts (api.toast) -- what replaced alert() on every failure path.
   Bottom-centre rather than a corner: on a wall tablet the corner is the
   easiest place on the screen not to look at, and a failed "mark done" that
   nobody sees is a task the kitchen believes is finished. Fixed, above
   everything, and never blocking -- unlike the native modal, the console
   stays usable while it is up. */
#skdToasts{position:fixed;left:50%;bottom:20px;transform:translateX(-50%);
  z-index:9999;display:flex;flex-direction:column;align-items:center;gap:8px;
  pointer-events:none;width:min(560px,calc(100vw - 32px))}
.toast{pointer-events:auto;cursor:pointer;width:100%;box-sizing:border-box;
  padding:11px 14px;border-radius:var(--radius);font-size:12.5px;line-height:1.45;
  box-shadow:var(--shadow-lg);
  animation:toast-in .18s ease-out}
.toast-bad{color:#8f1d35;background:#fff1f3;border:1px solid #f3a5b4}
.toast-ok{color:#04603a;background:var(--occ-done-bg);border:1px solid #6fce9f}
.toast.leaving{opacity:0;transition:opacity .2s ease}
@keyframes toast-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .toast{animation:none}
  .toast.leaving{transition:none}
}

/* --- manual ingestion progress (plan P2"/P5) --------------------------------
   Determinate and indeterminate are two DIFFERENT things on purpose. The
   server sends a denominator only where a real one exists -- pages after
   extraction, embedding batches after batching -- and none for a web search or
   a non-streaming download. The indeterminate variant sweeps; it never shows a
   number, because a bar that sticks at 40% reads as a hang. */
.udet-ingest-line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: baseline;
  margin-bottom: 10px;
}
.udet-ingest-bar {
  height: 4px;
  border-radius:var(--radius-pill);
  /* The TRACK, not the fill: --faint is body-text grey and painted this bar
     almost black with a blue sliver moving across it. */
  background: var(--panel2);
  overflow: hidden;
}
.udet-ingest-fill {
  height: 100%;
  background: var(--accent, #2563eb);
  border-radius:var(--radius-pill);
  transition: width .4s ease;
}
.udet-ingest-bar.indeterminate .udet-ingest-fill {
  width: 40% !important;
  animation: udet-ingest-sweep 1.4s ease-in-out infinite;
}
@keyframes udet-ingest-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}
/* A kitchen screen left on overnight should not animate forever at someone
   who has asked the OS to stop moving things. */
@media (prefers-reduced-motion: reduce) {
  .udet-ingest-bar.indeterminate .udet-ingest-fill { animation: none; width: 100% !important; opacity: .5; }
  .udet-ingest-fill { transition: none; }
}
.udet-ingest .btn { margin-top: 12px; }
/* Technician settings use the reference's compact, bordered trade rows.
   Tokens, not literals: these were the same four greys the tokens already
   name, written out by hand, so a retune of --line or --panel2 moved every
   surface in the console EXCEPT this one. */
.technicianRow { display:flex; align-items:end; flex-wrap:wrap; gap:16px; border:1px solid var(--line); padding:16px 18px; margin:16px 0; }
.technicianRow label { display:flex; flex:1 1 150px; flex-direction:column; gap:5px; color:var(--dim); font-size:12px; }
.technicianRow input { width:100%; min-height:40px; background:var(--panel2); border:1px solid var(--line); padding:8px; color:var(--ink); }
.technicianActions { display:flex; flex-wrap:wrap; gap:12px; margin-top:18px; }
.technicianStatus { min-height:24px; }
.technicianForm fieldset:disabled { opacity:.7; }

/* Physical equipment identity and editable profile controls. The detail
   page remains a narrow field sheet; these rows use the same grey controls
   and blue action hierarchy as the rest of the operations console. */
#unitsSearch{margin:0 0 16px}
.udet-profile fieldset{border:0;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:12px}
.udet-profile label{display:flex;flex-direction:column;gap:5px;color:var(--dim);font-size:12px}
.udet-profile label:first-child{grid-column:1/-1}
.udet-profile button[type=submit]{justify-self:start;align-self:end;min-height:42px}
.udet-form-error{color:var(--bad);margin:10px 0 0}
.udet-form-ok{color:var(--ok);margin:10px 0 0}
.udet-contact-row,.ask-contact-card{display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap}
.udet-contact-row>div:first-child,.ask-contact-card>div:first-child{display:grid;gap:2px}
.udet-contact-row>div:last-child,.ask-contact-actions{display:flex;gap:8px;flex-wrap:wrap}
.udet-contact-row .btn,.ask-contact-actions .btn{min-height:40px;padding:8px 12px;text-decoration:none}
.udet-model-change,.udet-manual-change{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:12px 0}
.udet-model-change>div,.udet-manual-change>div{background:var(--panel2);border:1px solid var(--line);padding:12px;border-radius:var(--radius);display:grid;gap:4px}
.udet-manual-change ul{padding-left:18px;margin:7px 0 0;color:var(--dim)}
.udet-confirm-actions{display:flex;gap:9px;flex-wrap:wrap;margin-top:14px}
.ask-unit-context{display:flex;justify-content:space-between;gap:12px;border-top:1px solid var(--line);padding-top:12px;margin-top:14px;font-weight:600}
.ask-contact-card{background:var(--accent-soft);border:1px solid #b9cadb;border-radius:var(--radius);padding:11px 12px;margin-top:10px}
.unit-choice{display:grid;gap:8px}
.unit-choice-row{display:flex;justify-content:space-between;gap:14px;text-align:left;background:var(--panel2);border:1px solid var(--line);padding:12px;color:var(--ink)}
.unit-choice-row:hover{border-color:var(--accent)}
.unit-choice-row span:first-child{display:grid;gap:2px}
.unit-choice-row small{color:var(--dim)}
.udet-task-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:9px}
.udet-task-actions .btn{min-height:40px;flex:1 1 140px}
.ask-task-context{background:var(--accent-soft);border:1px solid #b9cadb;border-radius:var(--radius);padding:10px 12px;margin-bottom:12px}
/* Symptom openers. They replace the [Question]/[Something isn't working] strip
   with the phone's answer to the same problem -- see ASK_STARTERS in
   views/ask.js. 44px tall because they are the primary way a repair gets
   opened, and the viewport probe checks that floor on every mobile width. */
.ask-starters{display:flex;flex-wrap:wrap;gap:8px;margin:8px 0 0}
.ask-starter{min-height:44px;padding:0 12px;background:transparent;border:1px solid var(--line2);
  border-radius:var(--radius);color:var(--dim)}
.ask-starter:hover{border-color:var(--ink);color:var(--ink)}
.ask-step-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
.ask-step-actions .btn{min-height:40px}
.ask-incident-state{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-top:12px;padding-top:12px;border-top:1px solid var(--line)}
.ask-outcome-backdrop{position:fixed;inset:0;z-index:85;background:rgba(17,29,41,.52);display:flex;justify-content:flex-end;align-items:stretch}
.ask-outcome-sheet{width:min(520px,100%);height:100%;overflow:auto;background:var(--panel);padding:24px;box-shadow:-12px 0 36px rgba(42,35,30,.20)}
.ask-outcome-sheet label{display:grid;gap:5px;color:var(--dim);font-size:12px;margin:12px 0}
.ask-outcome-sheet input,.ask-outcome-sheet textarea{width:100%;box-sizing:border-box}
.ask-outcome-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
.ask-outcome-actions .btn{min-height:42px}
.ask-outcome-actions[aria-busy="true"]{opacity:.7;pointer-events:none}
.ask-interrupted{display:grid;gap:8px;justify-items:start;margin-top:12px}
.ask-packet{display:grid;gap:10px}
.ask-packet pre{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--panel2);border:1px solid var(--line);padding:12px}
.activity-actions,.activity-filters{display:flex;gap:12px;align-items:end;flex-wrap:wrap}
.activity-filters label{display:grid;gap:5px;flex:1;min-width:140px}
.activity-filters input,.activity-filters select{width:100%;min-height:44px}
.activity-kpis{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin:20px 0}
.dashboard-kpi{color:var(--ink);text-decoration:none;transition:border-color .18s ease}
.dashboard-kpi:hover{background:transparent;border-color:var(--ink)}
.dashboard-kpi:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.activity-evidence{white-space:pre-wrap;overflow-wrap:anywhere;max-width:48ch;font-size:12px}
/* The two panels are one row, so they share a bottom edge. I had set
   align-items:start to let each size to its own content, which left the
   shorter one floating against a taller neighbour -- two boxes that are
   obviously a pair, not lining up. Stretch is the default; it is here
   explicitly so the next person does not "fix" it back. */
.dashboard-columns{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:stretch}
.dashboard-attention{color:var(--ink)}
.settingsPanel form label,.profile-form label{display:grid;gap:8px;margin:14px 0}
.settingsPanel textarea{width:100%;min-height:160px;padding:12px;font:inherit}
.table-wrap{overflow:auto}.table-wrap table{width:100%}
@media(max-width:760px){.activity-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}.dashboard-columns{grid-template-columns:1fr}}
@media(max-width:640px){
  .udet-profile fieldset,.udet-model-change,.udet-manual-change{grid-template-columns:1fr}
  .udet-profile label:first-child{grid-column:auto}
  .udet-contact-row,.ask-contact-card,.unit-choice-row{align-items:stretch;flex-direction:column}
  .ask-outcome-backdrop{align-items:flex-end}
  .ask-outcome-sheet{height:min(88vh,760px);padding:18px}
}


.brand{display:flex;align-items:center;gap:10px}
.brand__mark{display:block;height:24px;width:auto;flex:none}

/* --- account menu, developer options, the Ask front door ----------------
 *
 * "Working as" used to be a bare text input sitting in the global header on
 * every screen, beside a "Switch person" button — identity management as
 * permanent chrome. It is the same input (app.js wires #actorNameInput by
 * id); it now lives one click down, behind the person's own initials.
 */
.accountMenu{position:relative}
.accountMenu>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
  justify-content:center;min-width:38px;height:38px;padding:0 10px;
  border-radius:var(--radius-pill);background:var(--accent-soft);
  color:var(--accent-text);font-weight:600;font-size:13px}
.accountMenu>summary::-webkit-details-marker{display:none}
.accountMenu>summary:hover{background:#dce8fa}
.accountMenu__panel{position:absolute;right:0;top:calc(100% + 8px);z-index:40;
  width:260px;display:grid;gap:8px;padding:14px;background:var(--panel);
  border-radius:var(--radius);box-shadow:var(--shadow-lg)}
.accountMenu__panel .tiny{margin:0}

/* The Backend URL field. A dev needs it; a kitchen manager was being shown
   it before they could sign in. Kept, collapsed, and named for who it is
   for — deleting it would break the dev loop in docs/runbooks. */
.devopts{margin-top:14px}
.devopts>summary{cursor:pointer;font-size:12.5px;color:var(--dim);
  padding:6px 0;list-style:none;display:flex;align-items:center;gap:6px}
.devopts>summary::-webkit-details-marker{display:none}
.devopts>summary::before{content:"";width:0;height:0;
  border-left:5px solid var(--faint);border-top:4px solid transparent;
  border-bottom:4px solid transparent;transition:transform .15s ease}
.devopts[open]>summary::before{transform:rotate(90deg)}
.devopts>summary:hover{color:var(--ink)}


/* ==========================================================================
 * NELLIE — warm paper, soft shape, real depth (2026-09-18)
 * ==========================================================================
 *
 * Replaces the INDUSTRY block (2026-09-16), which squared and flattened
 * every component here and attributed that to prototype/. Worth recording
 * why that was wrong, because the mistake is easy to repeat: the prototype
 * specifies --radius-sm/md/lg 2/4/7 and a three-step shadow scale. The
 * wireframe frame was a drift AWAY from the design target, not a port of it.
 *
 * It still sits LAST so a component added later inherits the shape without
 * anyone having to remember it, and it still only ever overrides shape,
 * fill and elevation — colour, spacing and behaviour stay where they were.
 *
 * THE RULE THIS BLOCK ENFORCES: a surface is identified by its FILL and its
 * ELEVATION, not by a hairline around it. Under the old block --panel2 sat
 * 1.09:1 from the page, so the border WAS the only edge and every component
 * needed one — 61 of them, which is why the console read as an
 * undifferentiated field of boxes. With a real fill difference the border
 * is redundant, so it is removed here rather than restyled. Do not add one
 * back to something that already has a fill and a shadow.
 *
 * What is deliberately NOT on the radius scale: the spinner and the radio
 * dot (circles are their meaning) and native checkbox/radio chrome. What
 * keeps the heavy shadow: things that must occlude the page behind them —
 * the side sheets, the toast stack, the fixed mobile bar.
 */

.prose,.banner,.card,.kpi,.unit-row,.cal-row,.cal-agenda-card,.cal-today-card,
.udet-row,.udet-manual,.udet-manual-pending,.udet-remove,.units-table-wrap,
.unit-choice-row,.ask-preset-row,.ask-contact-card,.ask-task-context,
.ask-packet pre,.doc-review-confirm,.hazardBanner,.proBanner,.technicianRow,
.udet-model-change>div,.udet-manual-change>div,.morepanel,.pm-sheet,
.step,.cite{
  border-radius:var(--radius);
}
.unit-photo,.cal-photo,.udet-photo-progress,.udet-ingest-bar,.udet-ingest-fill,
.morepanel button,.mobilenav button,.technicianRow input,
select,input:not([type=checkbox]):not([type=radio]),textarea,button{
  border-radius:var(--radius-sm);
}
.pill,.tag,.res,.units-doc,.unit-status,.step .n{border-radius:var(--radius-chip)}

/* Surfaces carry a fill again. This is the half of the change that lets the
   borders go: a card is now visibly a different plane from the page, so it
   no longer needs to be outlined to be seen. */
.card,.kpi,.unit-row,.udet-row,.ask-preset-row,.unit-choice-row,
.cal-agenda-card,.cal-today-card,.udet-manual,.units-table-wrap{
  background:var(--panel);
  box-shadow:var(--shadow);
}
.prose,.step{background:var(--panel3)}

/* The borders these components used to need. Removed, not restyled — see
   the block comment. A tinted component (overdue, done, hazard) keeps its
   own status fill, set where it is defined; only the hairline goes. */
.unit-row,.udet-row,.ask-preset-row,.unit-choice-row,.udet-manual,
.cal-agenda-card,.cal-today-card,.units-table-wrap,.kpi,.prose,.step,
.udet-model-change>div,.udet-manual-change>div,.technicianRow{
  border:0;
}

/* Overlays still occlude, so they still lift further than a card does. */
.morepanel{box-shadow:var(--shadow-lg)}

/* Status chips. Same inks as everywhere else; the shape softened and the
   face moved off --heading, which is now a serif and was never right at
   11px uppercase. */
.unit-status{padding:3px 8px;font-family:var(--sans);font-size:11px;
  font-weight:600;letter-spacing:.07em;text-transform:uppercase}

/* A panel in that row fills the height the row gives it, and its list of
   rows sits at the top of that space rather than spreading to fill it.

   The margin reset is load-bearing: `.card+.card{margin-top:14px}` is the
   rule for cards STACKED down a page, and in a two-column grid it lands on
   the second item as a 14px top offset -- so the pair shared a bottom edge
   and missed at the top by exactly that. Same reset .udet-hero-grid already
   carries, for the same reason. */
.dashboard-columns>.card{display:flex;flex-direction:column}
.dashboard-columns>.card+.card{margin-top:0}

/* --- dashboard panels ---------------------------------------------------
 * Both columns are lists of hairline-separated rows inside a bordered
 * frame, and each row is: an identifier, a quieter line of provenance
 * under it, and one fact pinned to the right edge. That shape is what the
 * design repeats on every surface — the calendar agenda, the unit sheet,
 * the equipment table all restate it — so it is written once here.
 */
/* margin-bottom matches .activity-kpis' 20px, so Daily routine sits as far
   below the panels as the panels sit below the KPIs. */
.dashboard-columns{gap:24px;margin-bottom:20px}
.dashboard-attention,.dashboard-event{
  display:flex;flex-direction:row;align-items:flex-start;gap:14px;padding:13px 0;
  border-bottom:1px solid rgba(29,31,32,.1);text-decoration:none}
.dashboard-attention:last-child,.dashboard-event:last-child{border-bottom:0}
.dashboard-attention:hover{background:rgba(29,31,32,.035)}
.dashboard-attention-main,.dashboard-event-main{
  flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
/* The identifier is the condensed heading face: it is what the eye lands
   on first in a column of six, and it is the only thing in the row that
   needs to survive being read at arm's length. */
.dashboard-attention strong,.dashboard-event strong{
  font-family:var(--heading);font-weight:600;font-size:17px;line-height:1.2;
  letter-spacing:-.005em;color:var(--ink);overflow-wrap:anywhere}
.dashboard-attention-main>span,.dashboard-event-main>span{
  font-size:12.5px;color:var(--faint);overflow-wrap:anywhere}
/* A fixed, tabular time gutter. Fixed because the point of a left-hand time
   column is that the headlines beside it line up; tabular because otherwise
   09:12 and 13:40 are different widths and they do not. */
.dashboard-event-when{flex:none;width:52px;font-size:12px;color:var(--faint);
  font-variant-numeric:tabular-nums;padding-top:3px}
.dashboard-event-type{flex:none;font-size:11.5px;color:var(--faint);
  text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;padding-top:3px}
/* Overdue is the only thing on this page allowed to shout, and it shouts in
   the same red the calendar and the unit list already use. */
.tag.late{flex:none;background:var(--occ-overdue-bg);color:var(--occ-overdue-ink);
  border:1px solid var(--occ-overdue-line);align-self:center}

/* A secondary button on the dark band is drawn in the band's own ink. The
   page-level rule paints it near-black, which on #1d2d3d is an invisible
   control -- Sign out disappeared entirely. */
.top .btn.sec{color:#fff;border-color:rgba(255,255,255,.34);background:transparent}
.top .btn.sec:hover:not(:disabled){background:rgba(255,255,255,.1);border-color:#fff}
.top input,.top select{background:rgba(255,255,255,.1);border-color:rgba(255,255,255,.28);color:#fff}
.top input::placeholder{color:rgba(255,255,255,.55)}

/* ...but the account dropdown is NOT the dark band. It happens to live
   inside .top, so the three rules above were painting white ink and
   translucent-white borders onto a --panel background: "Sign out" was
   white-on-near-white and effectively invisible, and the "Working as"
   field had a border nobody could see either. The panel takes the page's
   ordinary treatment back. */
.top .accountMenu__panel .btn.sec{color:var(--ink);border-color:var(--line2);
  background:transparent}
.top .accountMenu__panel .btn.sec:hover:not(:disabled){
  background:var(--panel2);border-color:var(--ink)}
.top .accountMenu__panel input{background:var(--bg);border-color:var(--line2);
  color:var(--ink)}
.top .accountMenu__panel input::placeholder{color:var(--faint)}
/* "Switch person" is a .textAction, not a .btn, so the rules above missed
   it and it inherited the band's white straight off header.top: white text
   on the panel's near-white, invisible in exactly the same way Sign out
   was. Every control in this panel takes the page's ink. */
.top .accountMenu__panel .textAction{color:var(--ink)}

/* The denominator in a "39/43" figure is context, not the number being
   reported -- it reads at the sub-line's size and ink so the count itself
   keeps the display weight. */
.kpi .v small{font-size:.5em;font-weight:600;color:var(--faint);letter-spacing:0}

/* A <button> with no class of its own. Before the blueprint frame these
   were merely flat; now that every other control is a bordered rectangle,
   an unframed one reads as a paragraph -- "Save contacts", "Add a trade",
   "Remove" and "Copy number" were all sitting in Settings looking like
   prose. Giving the bare element the secondary treatment fixes the ones
   that exist and the next one somebody writes; a button that wants to look
   like text asks for .textAction. */
/* Scoped to the view, NOT to every classless <button> on the page. The
   navigation's own buttons are written without a class in index.html and
   only get one when the router marks the current tab -- so an app-wide rule
   would dress five destinations as bordered controls on any paint that
   reached the user before the router had touched them. Today the router
   always wins that race; a rule that is only correct because of a race is a
   defect waiting for the next nav button somebody adds. */
.view button:not([class]),.view button.primary,button.primary{
  background:transparent;color:var(--ink);border:1px solid var(--control-line);
  padding:10px 16px;font-family:var(--heading);font-size:14px;
  font-weight:600;line-height:1.2;text-transform:uppercase;letter-spacing:.07em;
  transition:background .18s ease,border-color .18s ease}
.view button:not([class]):hover:not(:disabled),button.primary:hover:not(:disabled){
  background:rgba(29,31,32,.06);border-color:var(--ink)}
.view button:not([class]):disabled,button.primary:disabled{opacity:.45;cursor:not-allowed}
/* The form's own submit is the primary action on that panel. */
button.primary{background:var(--accent-dark);border-color:var(--accent-dark);color:var(--accent-ink)}
button.primary:hover:not(:disabled){background:#2c455d;border-color:#2c455d}

/* `hidden` must actually hide. The UA sheet says [hidden]{display:none},
   which ANY display declaration in this file outranks -- and several do, on
   elements views toggle with `el.hidden = true`. Measured live: the
   calendar's week-only "Group week by" select reported hidden:true and
   display:flex at the same time, so a control that does nothing in month
   view sat on screen under every month. One rule, because the next
   component to set `display` on a toggled element would reintroduce it. */
[hidden]{display:none!important}

/* The month is the calendar's title, not a caption. `.card h3` turns every
   h3 inside a card into an 11px section label, which is right for a panel
   header and wrong for the one line that says which month you are reading. */
#calMonthLabel{font-size:22px;text-transform:none;letter-spacing:-.01em;
  color:var(--ink);margin:14px 0 10px}

/* Carried by the printed sheet, not the screen. The print block below
   reveals these; see the .cal-header rules there. */
.screen-hidden{display:none}

/* The line under a paginated table, and the control that extends it. Both
   sit in the same row so "showing N" and "load more" read as one statement
   about the same list rather than two loose objects at the end of the page. */
.activity-foot{display:flex;align-items:center;justify-content:space-between;
  gap:16px;flex-wrap:wrap;margin:12px 0 8px}
.activity-foot p{margin:0}
.activity-foot p:empty{display:none}

/* --- equipment table ----------------------------------------------------- */
.units-table-wrap{border:1px solid var(--line)}
.units-doc{border:1px solid var(--line);background:transparent}
.units-doc[data-state="ready"]{border-color:var(--occ-done-line)}

/* --- calendar ------------------------------------------------------------
 * A month cell is a drawn box on a drawing, not a card: the grid's own
 * hairlines are the structure. Rows inside keep their 3px status edge,
 * which is the whole colour channel, and lose the rounded fill around it.
 */
.cal-cell{border-color:var(--line)}
.cal-row{background:transparent;border:1px solid var(--line);
  border-left-width:3px;padding:3px 6px}
.cal-row[data-status="overdue"]{border-color:var(--occ-overdue-line)}
.cal-row[data-status="today"]{border-color:var(--occ-today-line)}
.cal-row[data-status="done"]{border-color:var(--occ-done-line)}
.cal-viewbtn.on{background:transparent;color:var(--ink);border-color:var(--ink)}
.cal-agenda-date{font-family:var(--heading);font-size:13px;letter-spacing:.1em}

/* --- ask ----------------------------------------------------------------- */
.ask-contact-card,.ask-task-context{background:var(--accent-soft);border-color:#b9cadb}

/* --- mobile -------------------------------------------------------------
 * The bottom bar is fixed over the page, so it is one of the few surfaces
 * that keeps a solid ground and its shadow; only its corners and its
 * active-tab pill change. A square tab marked by a rule, like the desktop
 * destinations above it.
 */
@media(max-width:640px){
  /* The page title and its one action stop sharing a line at phone width:
     condensed uppercase button labels are wider than the sentence-case ones
     they replaced, and "Add documents" was pushing the whole layout into
     horizontal overflow rather than dropping below the heading. */
  .pageHeading{flex-wrap:wrap;align-items:flex-start}
  .pageHeading>div{flex:1 1 100%}
  .pageHeading .btn{width:100%}
  header.top{flex-wrap:wrap}
  .who{flex-wrap:wrap;min-width:0}
  .who .textAction{flex:none}
  /* 44px is the brief's rule-2 floor and "Switch person" measured 232x37 on
     every one of the eleven tabs -- it lives in the shell's account panel, so
     the miss was not on one screen but on all of them. A .textAction takes 7px
     of padding because it is meant to read as prose, which is right for an
     inline link and wrong for the control that decides whose name the log
     credits. Height only: it is already 232px wide. */
  .accountMenu__panel .textAction{min-height:44px}
  /* The desktop destinations are display:none for a signed-in phone (the
     bottom bar takes over); these keep the pre-sign-in shell sane rather
     than stretching five typographic tabs to a third of the width each. */
  nav.tabs{gap:0 18px;padding:0 12px}
  nav.tabs button{flex:0 1 auto;min-width:0;font-size:14px;letter-spacing:.06em}
  body.signed-in .mobilenav{background:var(--bg)}
  .mobilenav button{border-top:2px solid transparent}
  .mobilenav button.on{background:transparent;color:var(--ink);border-top-color:var(--ink-deep)}
  .morepanel button{background:transparent}
}

/* Renaming a machine is one click on its own name, not a trip through the
   four-field Equipment details form below (see unitNameHtml). The row keeps
   the heading's baseline whether it is showing the name or the input, so
   opening the editor does not make the header jump. */
.udet-rename{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.udet-rename .udet-name{margin:0}
.udet-rename-input{font:inherit;font-size:1.15rem;padding:6px 10px;min-width:min(320px,100%);
  border:1px solid var(--control-line);border-radius:var(--radius-sm);background:var(--panel)}
.udet-rename-open{padding:5px 12px}
/* Visible to a screen reader, not on screen -- the input needs a label even
   though the heading beside it already names the field for a sighted user. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}


/* ==========================================================================
 * CLEAN — white page, quiet chrome, one centred column (2026-09-28)
 * ==========================================================================
 *
 * Replaces OUTLINE, from earlier the same day. It keeps OUTLINE's material
 * (solid white, nothing translucent, nothing blurred) and changes the
 * layout and type around it:
 *
 *   - The top bar is the wordmark, the destinations as plain words, and the
 *     person. The current destination is marked by ink and weight, not by a
 *     segmented well.
 *   - The working surface stays edge to edge at the --gutter inset, as
 *     .wrap above has it. Only the Today hero centres its own content.
 *   - Labels are sentence case. The letterspaced uppercase that section
 *     labels, table heads, chips and KPI labels still carried came from the
 *     condensed face of the INDUSTRY direction; in a UI sans it shouts.
 *   - Figures are tinted flat tiles: a fill, no edge. Overdue and due-today
 *     get their own tint, and only when the count is real and non-zero
 *     (dashboard.js's kpiTone decides that, not this file).
 *   - Panes are white with a light edge (--outline) and no shadow. Things
 *     you press that have no fill of their own take the firmer
 *     --outline-strong.
 *
 * Like NELLIE and OUTLINE before it, it sits LAST so it wins without
 * raising specificity, and it only changes how things look.
 */

::selection{background:rgba(58,114,212,.22)}

#envNote{margin-top:48px!important}

/* --- top bar -------------------------------------------------------------
 * position + z-index lift the bar's stacking context above the cards
 * below it; without that, the account menu that drops out of it would be
 * painted UNDER any later card that forms a stacking context of its own. */
header.top{position:relative;z-index:30;gap:12px 28px;padding:20px var(--gutter);
  background:var(--panel);color:var(--ink);border:0;box-shadow:none}
#appScreen>header.top{margin-bottom:36px}
.top .brand{gap:14px;font-family:var(--sans);font-size:19px;font-weight:650;
  text-transform:none;letter-spacing:-.015em;color:var(--ink)}
.top .brand__mark{height:30px}
.top .brand__name b{color:inherit;font-weight:inherit}
/* The kitchen, as quiet text after the wordmark: it answers "whose console
   is this", it is not a status. The slash is decoration, so its alt text is
   empty and a screen reader skips it. */
.top .pill{display:inline-flex;align-items:center;gap:16px;padding:0;
  background:none;border:0;border-radius:0;box-shadow:none;
  font-family:var(--sans);font-size:15px;font-weight:400;letter-spacing:0;
  text-transform:none;color:var(--faint)}
.top .pill::before{content:"/";content:"/" / "";color:var(--outline-strong);flex:none}

/* The destinations: plain words, spaced apart. The current one is ink and
   heavier; the rest are --dim and darken under the pointer.
   No `display` here: `body.signed-in nav.tabs{display:none}` hides this bar
   on a phone at the same specificity, and a later display would undo it. */
.top nav.tabs{gap:4px;margin:0;padding:0;flex-wrap:nowrap;align-items:center;
  background:none;border:0;border-radius:0;box-shadow:none}
.top nav.tabs button{padding:8px 12px;border:0;border-radius:var(--radius-sm);background:none;
  font-family:var(--sans);font-size:16px;font-weight:500;line-height:1.25;
  text-transform:none;letter-spacing:0;white-space:nowrap;color:var(--dim);box-shadow:none;
  transition:color .15s ease,background .15s ease}
.top nav.tabs button:hover{background:none;color:var(--ink)}
.top nav.tabs button.on{background:none;color:var(--ink);font-weight:650;box-shadow:none}
/* Ask is the one thing on this bar you DO rather than a place you GO, so it
   is a soft accent button rather than another word. --accent-text on
   #eef4ff is 5.32:1; white on --accent-text 5.87:1. */
.top nav.tabs .tabs-ask{margin-left:10px;padding:8px 16px;justify-content:center;
  background:#eef4ff;color:var(--accent-text);font-weight:600;border-radius:var(--radius)}
.top nav.tabs .tabs-ask:hover{background:#dfe9fd;color:var(--accent-dark)}
.top nav.tabs .tabs-ask.on{background:var(--accent-text);color:#fff;font-weight:600}

.accountMenu>summary{min-width:40px;height:40px;padding:0 8px;
  background:#f2f4f7;color:var(--ink);font-size:13px;font-weight:600;box-shadow:none;
  transition:background .15s ease}
.accountMenu>summary:hover{background:#e4e7ec}
.accountMenu__panel{background:var(--panel);border:1px solid var(--outline);
  border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);padding:16px}
/* The panel is light but lives inside .top, which paints .tiny #9fb0c0 and
   links #b5d9fd for the old dark band. */
.top .accountMenu__panel .tiny{color:var(--faint)}
.top .accountMenu__panel a{color:var(--accent-text)}

/* Too narrow for one row: the destinations take a row of their own, and
   the person stays top right. */
@media(max-width:1100px){
  .top nav.tabs{order:1;flex:1 1 100%;margin-left:-12px}
}

/* --- page headings -------------------------------------------------------- */
.pageHeading{margin-bottom:28px}
.pageHeading h1{font-size:32px;font-weight:700;letter-spacing:-.025em;line-height:1.15}
.pageHeading p{margin-top:6px;font-size:15px;color:var(--dim)}
.eyebrow{font-family:var(--sans);font-size:14px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--faint);margin:0 0 8px;line-height:1.3}

/* --- Today ----------------------------------------------------------------
 * A centred hero (the date, the kitchen, and a way in to Ask), a rule under
 * it that reaches both edges of the screen, then the figures and the two
 * lists at the usual inset. */
#view-dashboard>.pageHeading,#view-dashboard>p[role="status"]{text-align:center;justify-content:center}
#view-dashboard>.pageHeading{padding-top:40px}
#view-dashboard>.dash-hero{margin:0 calc(-1 * var(--gutter));
  padding:28px var(--gutter) 52px;border-bottom:1px solid var(--line)}
.dash-hero__inner{position:relative;text-align:center}
.dash-hero .pageHeading{display:block;margin:0}
.dash-hero .eyebrow{font-size:16px;font-weight:400;margin-bottom:14px}
.dash-hero h1{font-size:48px;font-weight:700;letter-spacing:-.035em;line-height:1.08;
  overflow-wrap:anywhere}

/* The ask box. A real form since the 2026-09-30 review: it answers in
   place, below the hero (dashboard.js wireAsk). */
.dash-ask{display:flex;align-items:center;gap:16px;max-width:760px;min-height:72px;
  margin:32px auto 0;padding:12px 12px 12px 24px;background:var(--panel);
  border:1px solid var(--outline-strong);border-radius:16px;
  box-shadow:0 1px 2px rgba(16,24,40,.04),0 8px 24px -12px rgba(16,24,40,.14);
  color:var(--faint);font-size:17px;text-align:left;text-decoration:none;
  transition:border-color .15s ease,box-shadow .15s ease}
.dash-ask:hover{border-color:var(--outline-strong-hover);
  box-shadow:0 1px 2px rgba(16,24,40,.05),0 12px 28px -12px rgba(16,24,40,.2)}
.dash-ask__text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dash-ask__go{flex:none;display:inline-flex;align-items:center;min-height:44px;padding:0 20px;
  border-radius:var(--radius);background:var(--accent-text);color:#fff;
  font-size:16px;font-weight:600;transition:background .15s ease}
.dash-ask:hover .dash-ask__go{background:var(--accent-dark)}
/* 600px holds three openers a row and not four, so the six sit as two even
   rows -- which is also Ask's own split: the three that open a repair,
   then the three that are questions. */
.dash-starters{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;
  max-width:600px;margin:20px auto 0}
.dash-starter{display:inline-flex;align-items:center;min-height:40px;padding:0 16px;
  background:var(--panel);border:1px solid var(--outline-strong);border-radius:var(--radius-pill);
  color:var(--ink);font-size:15px;text-decoration:none;
  transition:background .15s ease,border-color .15s ease}
.dash-starter:hover{background:#f9fafb;border-color:var(--outline-strong-hover)}

#view-dashboard>.activity-kpis{margin-top:40px;margin-bottom:0;gap:20px}
/* "Add documents" lives on the tile whose number it moves. It sits beside
   the tile's link, not inside it (a button in a link is invalid), and
   rests in room the tile keeps free at its foot, so the whole tile still
   hovers and opens Equipment. Styled as the text links the lists use. */
.dashboard-kpi-docs{position:relative;display:flex;min-width:0}
.dashboard-kpi-docs>.dashboard-kpi{flex:1;min-width:0;padding-bottom:62px}
.dashboard-kpi-action{position:absolute;left:22px;bottom:14px;display:inline-flex;
  align-items:center;gap:6px;min-height:32px;padding:0;background:none;
  font-size:14px;font-weight:500;color:var(--accent-text);text-underline-offset:3px}
.dashboard-kpi-action::before{content:"+";content:"+" / "";font-size:18px;font-weight:400;line-height:1}
.dashboard-kpi-action:hover{text-decoration:underline}
#view-dashboard>.card[role="alert"]{margin-top:20px}
/* The lists and Daily routine sit on the same 20px step as the figures'
   own gap, and the lists share the figures' column gap, so the middle
   seam of the two cards lines up with the seam between tiles two and
   three: one grid down the page rather than three blocks. */
#view-dashboard>.dashboard-columns{margin-top:20px;margin-bottom:0;gap:20px}
#view-dashboard>details.card{margin-top:20px}

/* The two lists are cards: the white pane and --outline edge every other
   card has. padding:0 so the rows can run the full width of the pane --
   the hairlines between them and the hover on Needs attention reach both
   edges -- which is why each child carries the 24px inset itself.
   overflow:hidden trims a hovered last row to the rounded corners. */
.dashboard-columns>.card{padding:0;overflow:hidden}
.dashboard-columns>.card>*{padding-left:24px;padding-right:24px}
.dashboard-columns>.card>.pageHeading{align-items:baseline;margin:0;padding-top:20px;
  padding-bottom:16px;border:0}
/* The period is the pane's footnote (order puts it last; margin-top:auto
   keeps it at the foot of a stretched pane), so both headings are one
   line tall and the first rows of the two lists start level. */
.dashboard-columns>.card>.tiny{order:1;margin:auto 0 0;padding-top:14px;padding-bottom:16px;
  border-top:1px solid var(--line);font-size:13px;color:var(--faint)}
.dashboard-columns>.card>p{margin:0;padding-top:16px;padding-bottom:20px;
  border-top:1px solid var(--line);font-size:14px;color:var(--dim)}
.dashboard-columns>.card>a:not(.dashboard-attention){margin-top:-8px;padding-bottom:20px;
  font-size:14px;font-weight:500;color:var(--accent-text)}
.dashboard-attention,.dashboard-event{padding-top:14px;padding-bottom:14px;
  border-top:1px solid var(--line);border-bottom:0}
.dashboard-attention:last-child,.dashboard-event:last-child{border-bottom:0}
.dashboard-attention strong,.dashboard-event strong{font-family:var(--sans);font-size:15px;
  font-weight:500;letter-spacing:-.005em;line-height:1.4}

/* A unit that needs attention: its name, the count as a soft red pill,
   and a chevron, since the whole row opens the unit. The focus ring is
   drawn inside the row, where overflow:hidden cannot clip it. */
.dashboard-attention{align-items:center;gap:12px;min-height:56px;color:var(--ink);
  transition:background .15s ease}
.dashboard-attention:hover{background:var(--tile)}
.dashboard-attention:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.dashboard-attention::after{content:"";flex:none;width:7px;height:7px;margin:0 3px 0 2px;
  border-right:2px solid var(--faint);border-bottom:2px solid var(--faint);
  transform:rotate(-45deg);transition:transform .15s ease,border-color .15s ease}
.dashboard-attention:hover::after{border-color:var(--ink);transform:translateX(2px) rotate(-45deg)}
/* "N overdue scheduled tasks" restates the count pinned to the right of the
   same row, so it stays for a screen reader and leaves the screen. */
.dashboard-attention-main>span{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* #b42318 on #fef3f2 is 6.0:1. */
.dashboard-attention .tag.late{padding:3px 10px;background:var(--tone-overdue-bg);border:0;
  border-radius:var(--radius-pill);font-size:13px;font-weight:600;line-height:1.4;
  letter-spacing:0;text-transform:none;color:var(--tone-overdue-ink)}

/* An entry is two lines -- what happened, then where and who in a quieter
   ink -- with its time and its kind stacked at the right edge, the way a
   notification list reads. The markup order is when/what/kind; the grid
   areas place it. The time sits on the headline's first line (its 21px
   line matches the headline's) and the kind on the where-and-who line:
   the 1fr row takes whatever height a wrapped headline adds, and the kind
   rests at its foot. */
.dashboard-event{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-rows:auto 1fr;
  grid-template-areas:"main when" "main type";align-items:start;gap:0 20px}
.dashboard-event-main{grid-area:main;order:0;display:flex;flex-direction:column;gap:2px}
.dashboard-event-main strong{display:block}
.dashboard-event-main>span{display:block;font-size:14px;line-height:1.45;color:var(--faint)}
.dashboard-event-when{grid-area:when;order:0;width:auto;padding:0;text-align:right;
  font-size:13px;line-height:21px;color:var(--faint);white-space:nowrap}
.dashboard-event-type{grid-area:type;order:0;justify-self:end;align-self:end;padding:0;font-size:13px;
  line-height:20px;letter-spacing:0;text-transform:none;color:var(--faint)}
.dashboard-event-type::first-letter{text-transform:uppercase}

/* --- panes -------------------------------------------------------------- */
.card{background:var(--panel);border:1px solid var(--outline);border-radius:var(--radius-lg);
  padding:24px;box-shadow:none}
.card+.card{margin-top:16px}
/* Section labels read as headings, not as captions. */
.card h3,.card>.pageHeading h2{font-family:var(--sans);font-size:16px;font-weight:600;
  letter-spacing:-.01em;text-transform:none;color:var(--ink);line-height:1.3}
.card h3{margin-bottom:14px}
.card>.pageHeading{align-items:center;margin:0 0 12px;padding-bottom:0;border-bottom:0}
.card>.pageHeading a{font-size:14px;font-weight:500;color:var(--accent-text)}
.card[role="alert"]{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:14px 18px;background:var(--tone-today-bg);border-color:#fedf89;color:#93370d;font-size:14px}

/* A disclosure card: the summary is its heading row, with a chevron. */
details.card{padding:0}
details.card>summary{display:flex;align-items:center;gap:10px;padding:18px 24px;
  list-style:none;cursor:pointer;font-weight:600;font-size:16px;color:var(--ink)}
details.card>summary::-webkit-details-marker{display:none}
details.card>summary::before{content:"";flex:none;width:7px;height:7px;margin:0 3px;
  border-right:2px solid var(--faint);border-bottom:2px solid var(--faint);
  transform:rotate(-45deg);transition:transform .15s ease}
details.card[open]>summary::before{transform:rotate(45deg)}
details.card[open]>summary{border-bottom:1px solid var(--line)}
details.card>:not(summary){margin-left:24px;margin-right:24px}
details.card>:not(summary):last-child{margin-bottom:24px}
details.card>p:first-of-type{margin-top:18px;color:var(--dim);font-size:14px}

/* The sign-in card. `#authScreen>*:not(header)` outranks .centerCard, so
   its measure and auto margins are restated here. */
#authScreen>.centerCard{max-width:440px;margin:72px auto 0;padding:32px}
#authScreen .centerCard h1{font-size:26px;font-weight:700;letter-spacing:-.02em;margin-bottom:22px}

/* Figures: flat tiles, a fill and no edge. Label above, figure under it,
   the line of context last (markup order is figure/label/context; see the
   .kpi rules above). */
.kpi{background:var(--tile);border:0;border-radius:14px;padding:20px 22px;box-shadow:none}
.kpi .l{font-family:var(--sans);font-size:15px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--dim);line-height:1.3}
.kpi .v{font-family:var(--sans);font-size:38px;font-weight:700;letter-spacing:-.025em;
  line-height:1.1;margin-top:10px;color:var(--ink)}
.kpi .v small{font-size:.55em;font-weight:600;color:var(--faint)}
.kpi .s{font-size:13px;color:var(--faint);margin-top:8px}
.dashboard-kpi{transition:background .15s ease}
.dashboard-kpi:hover{background:var(--tile-hover);border-color:transparent}
.kpi[data-tone="overdue"] .v{color:var(--tone-overdue-ink)}
.kpi[data-tone="today"] .v{color:var(--tone-today-ink)}

/* Tiles: things that sit INSIDE a pane (rows, list items, answer steps).
   The same white and the same light edge as the pane around them. Hover
   firms the edge rather than thickening it, so nothing shifts a pixel. */
.unit-row,.udet-row,.ask-preset-row,.unit-choice-row,.cal-agenda-card,.cal-today-card,
.udet-manual,.step,.prose,.udet-model-change>div,.udet-manual-change>div,.technicianRow,
.ask-packet pre{background:var(--panel);border:1px solid var(--outline);box-shadow:none;border-radius:12px}
.unit-row,.udet-manual,.unit-choice-row{transition:border-color .15s ease,color .15s ease}
.unit-row:hover,.unit-choice-row:hover{border-color:var(--outline-strong-hover)}
.udet-manual:hover{color:var(--accent-text);border-color:rgba(58,114,212,.5)}
.unit-photo,.cal-photo{background:var(--tile);border-color:var(--outline);box-shadow:none}
.ask-contact-card,.ask-task-context{background:#f5f8ff;border-color:#d1e0ff}
.card h3.udet-name{font-size:22px;font-weight:700;letter-spacing:-.02em;margin:0}

/* Hairlines inside panes. */
td{border-bottom-color:var(--line)}
tr:hover td{background:#f9fafb}

/* Helper text. 11px and 12.5px were sized against the old 13px body; next
   to 15px copy they read as fine print. */
.tiny{font-size:12.5px;line-height:1.5}
.muted{font-size:14px}

/* --- tables --------------------------------------------------------------- */
table{font-size:14px}
th{font-family:var(--sans);font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;
  color:var(--faint);padding:10px 12px}
td{padding:12px}
.units-table-wrap{background:var(--panel);border:1px solid var(--outline);border-radius:14px;box-shadow:none}
.units-table th{font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;color:var(--faint);
  background:#f9fafb;padding:12px 18px;white-space:nowrap}
.units-table td{padding:14px 18px;font-size:14px}
.units-record:hover,.units-record:focus-visible{background:#f9fafb}
.units-record-link{font-size:15px}
.units-unit-cell span,.units-table td small{font-size:13px;letter-spacing:0;text-transform:none}
table.cal-month th,table.cal-week th{font-size:12.5px;font-weight:500;letter-spacing:0;text-transform:none}

/* --- chips ----------------------------------------------------------------
 * Sentence case. The words are whatever the view wrote; a lowercase chip
 * is capitalised on its first letter only, which is what inline-block is
 * for (::first-letter does not reach an inline box). */
.tag,.res,.units-doc,.unit-status{font-family:var(--sans);font-size:12.5px;font-weight:500;
  letter-spacing:0;text-transform:none;border-radius:var(--radius-pill);padding:2px 10px;line-height:1.5}
.tag,.res{display:inline-block}
.tag::first-letter,.res::first-letter{text-transform:uppercase}
.tag.ok,.res.self{background:#ecfdf3;color:#067647}
.tag.hazard,.res.contracted{background:#fef3f2;color:var(--tone-overdue-ink);border:1px solid #fecdca}
.tag.unverified,.res.open{background:var(--tone-today-bg);color:var(--tone-today-ink)}
.tag.info{background:#f2f4f7;color:var(--dim);border:1px solid var(--outline)}
.tag.late{background:var(--tone-overdue-bg);color:var(--tone-overdue-ink);border:1px solid #fecdca}
.units-doc{background:#f2f4f7;color:var(--dim);border:1px solid var(--outline)}
.units-doc[data-state="ready"]{background:#ecfdf3;color:#067647;border-color:#abefc6}
.unit-location,.udet-section-h,.sources .lbl{font-size:13px;font-weight:600;letter-spacing:0;
  text-transform:none;color:var(--dim)}
.cal-agenda-date{font-family:var(--sans);font-size:13px;font-weight:600;letter-spacing:0;
  text-transform:none;color:var(--dim)}

/* --- banners ---------------------------------------------------------------- */
.banner{border-radius:12px;padding:12px 16px;font-size:14px;color:#93370d;
  background:var(--tone-today-bg);border:1px solid #fedf89}
.banner b{color:#7a2e0e}
.banner.info{color:var(--dim);background:#f5f8ff;border-color:#d1e0ff}
.banner.bad{color:var(--tone-overdue-ink);background:var(--tone-overdue-bg);border-color:#fecdca}
.banner.bad b{color:#912018}
.banner.quiet{font-size:13px;background:transparent;border-color:var(--outline);color:var(--faint)}
.hazardBanner{border-radius:12px;background:var(--tone-overdue-bg);border-color:#fecdca;color:var(--tone-overdue-ink)}
.proBanner{border-radius:12px;background:#f5f8ff;border-color:#d1e0ff}

/* --- controls --------------------------------------------------------------- */
label.lab{font-size:14px;font-weight:500;color:#344054;margin:18px 0 6px}
select,input:not([type=checkbox]):not([type=radio]),textarea{
  background:var(--panel);border-color:var(--control-line);border-radius:var(--radius);
  padding:10px 14px;font-size:15px;box-shadow:0 1px 2px rgba(16,24,40,.04);
  transition:border-color .15s ease,box-shadow .15s ease}
select:hover,input:not([type=checkbox]):not([type=radio]):hover,textarea:hover{border-color:var(--dim)}
/* The ring replaces the 2px outline on text fields only. The transparent
   outline is not a leftover: forced-colors mode drops box-shadow entirely
   and paints outlines in a system colour, so it is the ring there. */
select:focus-visible,input:not([type=checkbox]):not([type=radio]):focus-visible,textarea:focus-visible{
  background:#fff;border-color:var(--accent);outline:2px solid transparent;outline-offset:1px;
  box-shadow:0 0 0 4px rgba(58,114,212,.2)}

.btn,.view button:not([class]),.view button.primary,button.primary{
  border-radius:var(--radius);padding:10px 16px;font-family:var(--sans);font-size:15px;
  font-weight:600;line-height:1.25;text-transform:none;letter-spacing:0;
  transition:background .15s ease,border-color .15s ease,color .15s ease}
/* Primary: a flat accent fill. It is --accent-text, not --accent: the label
   wants the darker of the two (white on it 5.87:1, against 4.63:1). */
.btn,button.primary{background:var(--accent-text);border-color:var(--accent-text);color:#fff;
  box-shadow:0 1px 2px rgba(16,24,40,.06)}
.btn:hover:not(:disabled),button.primary:hover:not(:disabled){
  background:var(--accent-dark);border-color:var(--accent-dark)}
.btn:active:not(:disabled){background:#1f4687;border-color:#1f4687}
/* Secondary: white with a firm grey edge. It needs its own :active -- the
   shared .btn:active above would otherwise press a light button dark. */
.btn.sec,.view button:not([class]),.view button.primary{background:var(--panel);
  color:var(--ink);border-color:var(--outline-strong);box-shadow:0 1px 2px rgba(16,24,40,.05)}
/* .view button.primary is here too: inside a view it has always RESTED as a
   secondary (.view button.primary outranks button.primary), so it hovers as
   one as well. */
.btn.sec:hover:not(:disabled),.view button:not([class]):hover:not(:disabled),
.view button.primary:hover:not(:disabled){background:#f9fafb;border-color:var(--outline-strong-hover)}
.btn.sec:active:not(:disabled){background:#f2f4f7;border-color:var(--outline-strong-hover)}
.btn.ok{background:var(--ok);border-color:var(--ok)}
.btn.ok:hover:not(:disabled){background:#265a3a;border-color:#265a3a}
.btn.ok:active:not(:disabled){background:#214e33;border-color:#214e33}
.btn.bad{background:var(--bad);border-color:var(--bad)}
.btn.bad:hover:not(:disabled){background:#751a26;border-color:#751a26}
.btn.bad:active:not(:disabled){background:#661620;border-color:#661620}
/* A pressed view toggle (Month / Agenda) is a quiet well, not a raised pane. */
.cal-viewbtn.on{background:#f2f4f7;color:var(--ink);border-color:var(--outline-strong);box-shadow:none}

/* 44px, as the rule above says and the viewport probe checks: these are
   the primary way a repair gets opened. */
.ask-starter{min-height:44px;padding:0 16px;background:var(--panel);color:var(--ink);
  border:1px solid var(--outline-strong);border-radius:var(--radius-pill);box-shadow:none;
  transition:background .15s ease,border-color .15s ease}
.ask-starter:hover{background:#f9fafb;border-color:var(--outline-strong-hover);color:var(--ink)}
.cite{background:var(--panel);border-color:var(--outline);border-radius:var(--radius-pill);font-size:12px}
.cite:hover{color:var(--accent-text);border-color:rgba(58,114,212,.4)}
.cite:hover .p{color:var(--accent-text)}
.step .n{color:var(--accent-text);border-color:rgba(58,114,212,.25)}
/* Text that was set in --accent, which :root says never to do: on white
   it is 4.34:1. --accent-text is the same hue at 5.87:1. */
.citation-link,.cal-more,.proBanner h4{color:var(--accent-text)}
a{color:var(--accent-text)}
.udet-back{color:var(--dim);text-decoration:none;font-weight:500;font-size:14px}
.udet-back:hover{color:var(--ink)}

/* --- section switchers ---------------------------------------------------
 * The side rail (Equipment, Settings) is a plain list with the current
 * section as a soft grey well. The top bar (Schedule) is a row of words
 * over a hairline, the current one underlined in ink. */
.settingsLayout{gap:40px}
.settingsNav{background:none;border:0;border-radius:0;padding:0;gap:2px;box-shadow:none}
.settingsNav button{padding:9px 12px;border:0;border-radius:var(--radius-sm);
  font-size:15px;font-weight:500;color:var(--dim);
  transition:background .15s ease,color .15s ease}
.settingsNav button:hover{background:#f9fafb;color:var(--ink)}
.settingsNav button.on{background:#f2f4f7;color:var(--ink);font-weight:600;box-shadow:none}
.settingsLayout--bar{gap:24px}
.settingsLayout--bar .settingsNav{justify-self:stretch;max-width:100%;overflow-x:auto;gap:24px;
  border:0;border-bottom:1px solid var(--line);border-radius:0;padding:0}
.settingsLayout--bar .settingsNav button{padding:10px 0;border:0;border-radius:0;background:none;
  box-shadow:inset 0 -2px 0 transparent}
.settingsLayout--bar .settingsNav button:hover{background:none}
.settingsLayout--bar .settingsNav button.on{background:none;box-shadow:inset 0 -2px 0 var(--ink)}

/* --- calendar ------------------------------------------------------------ */
.cal-row{background:var(--panel);border-color:var(--outline)}
.cal-cell{border-color:var(--outline)}
#calMonthLabel{font-size:22px;font-weight:700;letter-spacing:-.02em}

/* Schedule > Calendar is not boxed. The month and its arrows share a line
 * with the Month / Agenda switch at the far end, the daily-cleaning switch
 * and the key sit under them, and the grid (or the agenda list) is its own
 * rounded frame.
 *
 * All of it is screen only. The print block above draws the wall copy its
 * own way -- status as border STYLE, the key fixed to every page -- and
 * none of this may reach it.
 *
 * Status keeps every form it had: the rail, the wider rail for overdue, the
 * strikethrough for done and skipped. Only the tints are new, and they are
 * CLEAN's --tone-* pairs, so a red here is the red of the Today tiles.
 * Upcoming stays neutral, as the --occ-* note in :root asks. Each rail
 * measures 3:1 or better against its own fill. */
@media screen{
  .cal-page{
    --cal-overdue-bg:var(--tone-overdue-bg); --cal-overdue-rail:#d92d20; --cal-overdue-ink:var(--tone-overdue-ink);
    --cal-today-bg:var(--tone-today-bg);     --cal-today-rail:#dc6803;   --cal-today-ink:var(--tone-today-ink);
    --cal-done-bg:#ecfdf3;                   --cal-done-rail:#079455;    --cal-done-ink:#067647;
    --cal-upcoming-bg:#f2f4f7;               --cal-upcoming-rail:#98a2b3;
  }

  /* The header. .cal-header's two button rows are laid out as if they were
     .cal-page's own children (display:contents), so the month title can sit
     between them without the markup moving. */
  .card.cal-page{display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;
    align-items:center;gap:12px 16px;padding:0;background:none;border:0;border-radius:0;box-shadow:none}
  .cal-page>.cal-header{display:contents}
  .cal-page>*{grid-column:1/-1}
  .cal-page>#calMonthLabel{grid-row:1;grid-column:1;font-size:26px;letter-spacing:-.025em}
  .cal-header>.row:nth-of-type(2){grid-row:1;grid-column:2}
  .cal-header>.row:nth-of-type(1){grid-row:1;grid-column:4}
  .cal-page>#calCadenceToggle{grid-row:2;grid-column:1/3}
  .cal-page>.cal-legend{grid-row:2;grid-column:3/5;justify-self:end;margin:0}
  .cal-page>.cal-cadence-banner{margin:-14px 0 4px}
  .cal-page>.cal-cadence-banner:empty,.cal-page>#calTzWarning:empty{display:none}
  .cal-page>#calBody{margin-top:4px}
  #calSubscribeCard{margin-top:40px}

  /* Previous / next: two quiet square buttons beside the month they turn. */
  #calPrev,#calNext{display:inline-flex;align-items:center;justify-content:center;
    width:36px;height:36px;min-width:36px;padding:0 0 3px;border-radius:var(--radius);
    font-size:24px;font-weight:400;line-height:1;color:var(--dim)}
  #calPrev:hover:not(:disabled),#calNext:hover:not(:disabled){color:var(--ink)}

  /* Month / Agenda: a segmented switch. The current view is a white pill
     in a grey well; both keep one weight so the switch never changes width. */
  .cal-header>.row:nth-of-type(1){padding:3px;background:#f2f4f7;border-radius:12px}
  .cal-page .cal-viewbtn{min-height:34px;padding:6px 18px;border-radius:9px;
    background:transparent;border-color:transparent;box-shadow:none;
    font-size:14px;font-weight:500;color:var(--dim)}
  .cal-page .cal-viewbtn:hover:not(:disabled){background:transparent;border-color:transparent;color:var(--ink)}
  .cal-page .cal-viewbtn.on,.cal-page .cal-viewbtn.on:hover:not(:disabled){background:var(--panel);
    border-color:transparent;color:var(--ink);
    box-shadow:0 1px 2px rgba(16,24,40,.06),0 1px 3px rgba(16,24,40,.1)}

  /* Include daily cleaning: the checkbox drawn as a switch. It is still the
     native input -- same focus, same keyboard, same label as its hit area.
     The off track is --control-line, the text-field edge, because a switch
     boundary wants the same 3:1 a field does. */
  .cal-page .cal-cadence-toggle{gap:10px;padding:0;font-size:14px;color:var(--dim);cursor:pointer}
  .cal-page .cal-cadence-toggle input[type=checkbox]{-webkit-appearance:none;appearance:none;
    position:relative;width:36px;height:20px;margin:0;border-radius:var(--radius-pill);
    background:var(--control-line);cursor:pointer;transition:background .15s ease}
  .cal-page .cal-cadence-toggle input[type=checkbox]::before{content:"";position:absolute;top:2px;left:2px;
    width:16px;height:16px;border-radius:50%;background:#fff;
    box-shadow:0 1px 2px rgba(16,24,40,.2);transition:transform .15s ease}
  .cal-page .cal-cadence-toggle input[type=checkbox]:checked{background:var(--accent-text)}
  .cal-page .cal-cadence-toggle input[type=checkbox]:checked::before{transform:translateX(16px)}
  .cal-page .cal-cadence-toggle input[type=checkbox]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .cal-page .cal-cadence-toggle select{min-height:36px;padding:6px 12px;font-size:14px}

  /* The key: each word drawn as a tiny row, in exactly the rows' form. */
  .cal-page .cal-legend{gap:6px;font-size:12.5px}
  .cal-page .cal-legend .swatch[data-status]{padding:2px 10px 2px 7px;border-radius:3px;
    font-weight:500;line-height:1.5}
  .cal-page .cal-legend .swatch[data-status="overdue"]{padding-left:4px}

  /* --- rows (grid cells and agenda alike) --- */
  .cal-page .cal-row{gap:6px;margin:0 0 4px;padding:4px 7px;border:0;
    border-left:3px solid var(--cal-upcoming-rail);border-radius:6px;background:var(--cal-upcoming-bg);
    color:var(--ink);font-size:12px;line-height:1.3;box-shadow:none}
  .cal-page .cal-row:last-of-type{margin-bottom:0}
  .cal-page .cal-row .cal-machine{font-size:11px;font-weight:500;line-height:1.3;color:var(--dim)}
  .cal-page .cal-row .cal-title{font-weight:500}
  /* The wider overdue rail eats into the padding rather than the text, so
     every title starts at the same x whatever its status. */
  .cal-page .cal-row[data-status="overdue"],.cal-page .cal-legend .swatch[data-status="overdue"]{
    background:var(--cal-overdue-bg);border-left:6px solid var(--cal-overdue-rail);color:var(--cal-overdue-ink)}
  .cal-page .cal-row[data-status="overdue"]{padding-left:4px}
  .cal-page .cal-row[data-status="today"],.cal-page .cal-legend .swatch[data-status="today"]{
    background:var(--cal-today-bg);border-left-color:var(--cal-today-rail);color:var(--cal-today-ink)}
  .cal-page .cal-row[data-status="done"],.cal-page .cal-legend .swatch[data-status="done"]{
    background:var(--cal-done-bg);border-left-color:var(--cal-done-rail);color:var(--cal-done-ink)}
  .cal-page .cal-row[data-status="skipped"]{background:#f9fafb;border-left-color:#d0d5dd;color:var(--faint)}
  .cal-page .cal-row[data-status="done"] .cal-machine,
  .cal-page .cal-row[data-status="skipped"] .cal-machine{color:inherit}
  .cal-page .cal-row.cal-routine{background:#f9fafb;border-left-color:#d0d5dd;color:var(--dim)}

  /* --- month grid ---
   * One rounded frame; the cells inside it are divided by hairlines on
   * their right and bottom edges only, so no line is ever drawn twice. */
  .cal-page table.cal-month{border-collapse:separate;border-spacing:0;background:var(--panel);
    border:1px solid var(--outline);border-radius:14px}
  .cal-page table.cal-month th{padding:10px 12px;background:#f9fafb;border-bottom:1px solid var(--outline);
    font-size:13px;font-weight:500;color:var(--faint)}
  .cal-page table.cal-month th:first-child{border-top-left-radius:13px}
  .cal-page table.cal-month th:last-child{border-top-right-radius:13px}
  /* 144px is a full cell -- a date, two rows and "+N more" -- so every
     week is the same height until a day is expanded. */
  .cal-page .cal-cell{height:144px;padding:6px;background:var(--panel);outline:0;
    border:0;border-right:1px solid var(--outline);border-bottom:1px solid var(--outline)}
  .cal-page .cal-cell:last-child{border-right:0}
  .cal-page tbody tr:last-child .cal-cell{border-bottom:0}
  .cal-page tbody tr:last-child .cal-cell:first-child{border-bottom-left-radius:13px}
  .cal-page tbody tr:last-child .cal-cell:last-child{border-bottom-right-radius:13px}
  .cal-page .cal-cell.cal-cell-empty{background:#f9fafb}
  .cal-page .cal-cell.expanded{height:auto}
  /* The day's worst status used to be an outline round the whole cell, and
     neighbouring outlines overlapped into a red lattice. It is a dot by the
     date now: the same "look here" when the row that earned it is hidden
     behind "+N more", without redrawing the grid. */
  .cal-page .cal-daynum{display:flex;align-items:center;gap:6px;height:22px;margin:0 0 4px;padding:0 4px;
    font-size:13px;font-weight:500;color:var(--dim)}
  .cal-page .cal-cell[data-status="overdue"] .cal-daynum::after,
  .cal-page .cal-cell[data-status="today"] .cal-daynum::after{content:"";flex:none;width:6px;height:6px;
    border-radius:50%;background:var(--cal-overdue-rail)}
  .cal-page .cal-cell[data-status="today"] .cal-daynum::after{background:var(--cal-today-rail)}
  .cal-page .cal-cell .cal-row{margin-bottom:3px}
  .cal-page .cal-cell .cal-more{margin-top:2px;padding:2px 7px;border-radius:6px;
    font-size:12px;font-weight:600;color:var(--accent-text);transition:background .15s ease}
  .cal-page .cal-cell .cal-more:hover{background:#f2f4f7;text-decoration:none}
  /* The 44px floor is for fingers. With a mouse the caption can be the
     size of a caption, and the week rows stop growing to hold it. */
  @media(pointer:fine){
    .cal-page .cal-cell .cal-more{min-height:24px}
  }

  /* --- agenda ---
   * The same frame as the grid, one day per band, the date in a column of
   * its own and the day's jobs beside it. */
  .cal-page .cal-agenda{background:var(--panel);border:1px solid var(--outline);border-radius:14px}
  .cal-page .cal-agenda>p.muted{margin:0;padding:28px 20px;text-align:center}
  .cal-page .cal-agenda-card{position:relative;margin:0;padding:14px 20px 14px 176px;
    background:none;border:0;border-bottom:1px solid var(--line);border-radius:0;outline:0;box-shadow:none}
  .cal-page .cal-agenda-card:last-child{border-bottom:0}
  .cal-page .cal-agenda-date{position:absolute;top:14px;left:20px;display:flex;align-items:center;
    width:136px;height:52px;margin:0;font-size:14px;font-weight:600;color:var(--ink)}
  .cal-page .cal-agenda .cal-row{align-items:center;gap:12px;min-height:52px;margin:0 0 8px;
    padding:8px 8px 8px 14px;border-radius:10px;font-size:14px;line-height:1.35}
  .cal-page .cal-agenda .cal-row:last-of-type{margin-bottom:0}
  .cal-page .cal-agenda .cal-row[data-status="overdue"]{padding-left:11px}
  .cal-page .cal-agenda .cal-machine{font-size:12.5px}
  .cal-page .cal-agenda .cal-title{font-size:14.5px}
  .cal-page .cal-agenda .cal-more{min-height:36px;margin-top:6px;padding:4px 0;font-size:13.5px;font-weight:600;
    color:var(--accent-text)}

  /* Mark done: a small white button at the end of its row, with a drawn
     tick, rather than a full-width green bar under every job. It turns
     green under the pointer, which is the moment it means something. */
  .cal-page .cal-markdone{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:9px;
    width:auto;min-height:36px;padding:0 14px;background:var(--panel);color:var(--ink);
    border:1px solid rgba(16,24,40,.14);border-radius:var(--radius-sm);
    font-size:14px;font-weight:600;box-shadow:0 1px 2px rgba(16,24,40,.05)}
  .cal-page .cal-markdone::before{content:"";flex:none;width:9px;height:5px;margin-top:-3px;
    border-left:2px solid var(--cal-done-rail);border-bottom:2px solid var(--cal-done-rail);transform:rotate(-45deg)}
  .cal-page .cal-markdone:hover:not(:disabled){background:var(--cal-done-bg);border-color:#75e0a7;color:var(--cal-done-ink)}
  .cal-page .cal-markdone:active:not(:disabled){background:#dcfae6;border-color:#47cd89}
}

/* A drawn switch vanishes in forced-colours mode, which drops backgrounds:
   the native checkbox comes back there. */
@media screen and (forced-colors:active){
  .cal-page .cal-cadence-toggle input[type=checkbox]{-webkit-appearance:auto;appearance:auto;width:20px;height:20px}
  .cal-page .cal-cadence-toggle input[type=checkbox]::before{display:none}
}

@media screen and (max-width:640px){
  /* Phone: the month and its arrows, then the switch, the daily-cleaning
     toggle with its note, and the key, each on a line of its own. `.row` stacks its
     children at this width; these two rows are single controls. */
  .card.cal-page{grid-template-columns:minmax(0,1fr) auto;gap:10px 12px}
  .cal-page>#calMonthLabel{font-size:22px}
  .cal-header>.row{flex-direction:row}
  .cal-header>.row:nth-of-type(1){grid-row:2;grid-column:1/-1}
  .cal-page>#calCadenceToggle{grid-row:3;grid-column:1/-1}
  .cal-page>.cal-cadence-banner{grid-row:4;margin:-8px 0 0}
  .cal-page>.cal-legend{grid-row:5;grid-column:1/-1;justify-self:start}
  #calPrev,#calNext{width:44px;height:44px}
  .cal-page .cal-viewbtn{min-height:38px}
  #calSubscribeCard{margin-top:28px}

  .cal-page .cal-agenda-card{padding:14px}
  .cal-page .cal-agenda-date{position:static;display:block;width:auto;height:auto;margin:0 0 10px}
  .cal-page .cal-agenda .cal-row{padding:10px 10px 10px 12px}
  .cal-page .cal-agenda .cal-row[data-status="overdue"]{padding-left:9px}
  /* Too narrow to share a line with the task: the button takes the row's
     full width under it, at the 44px floor. */
  .cal-page .cal-agenda .cal-markdone{flex:1 1 100%;min-height:44px;margin-top:2px}
}

/* --- things that float ------------------------------------------------------
 * Solid panes. The sheets sit on a scrim, which is what separates them
 * from the page; the outline only finishes their open edge. */
.pm-sheet-backdrop,.ask-outcome-backdrop{background:rgba(16,24,40,.34)}
.pm-sheet,.ask-outcome-sheet{background:var(--panel);
  border:1px solid var(--outline);border-right:0;
  border-radius:var(--radius-lg) 0 0 var(--radius-lg);box-shadow:var(--shadow-lg)}
.toast{border-radius:12px;box-shadow:var(--shadow-lg)}

@media(max-width:900px){
  .dash-hero h1{font-size:40px}
}

@media(max-width:900px){
  /* minmax(0,1fr), not the 1fr the stacked unit page asks for: a bare 1fr
     track cannot shrink below its content, and at 320px "View this
     machine's repair history" held the column 18px wider than the screen. */
  .udet-hero-grid,.udet-content-grid{grid-template-columns:minmax(0,1fr)}
}

@media(max-width:760px){
  .settingsLayout{gap:20px}
}

@media(max-width:720px){
  /* The table becomes a stack of cards here, so its frame goes away. */
  .units-table-wrap{background:transparent;border:0;box-shadow:none}
  .units-table .units-record{background:var(--panel);border:1px solid var(--outline);
    border-radius:14px;box-shadow:none}
  /* The cells are the card's fields now: no padding of their own (the
     desktop padding above comes later in this file than the rule that
     zeroes it), and they wrap. The shared phone rule makes every table
     nowrap, which cut "Next periodic due 2026-09-18" off at the card edge. */
  .units-table{white-space:normal}
  .units-table td{padding:0}
  .units-table td::before,#planTable td[data-label]::before{font-size:12px;font-weight:500;
    letter-spacing:0;text-transform:none;color:var(--faint)}
}

@media(max-width:640px){
  :root{--gutter:12px}
  /* Phone: the destinations are the bottom bar, so the top bar is the
     wordmark and the person, with the kitchen on a line of its own under
     them -- where a slash would have nothing to follow. */
  header.top{align-items:center;gap:4px 12px;padding:12px var(--gutter)}
  body.signed-in #appScreen>header.top{margin-bottom:24px}
  .top .brand{font-size:17px}
  .top .brand__mark{height:26px}
  .top .pill{order:1;flex:1 1 100%;font-size:14px;white-space:normal}
  .top .pill::before{display:none}
  .accountMenu{margin-left:auto}
  .accountMenu>summary{min-width:44px;height:44px}
  .pageHeading h1{font-size:28px}
  .card{padding:18px}
  details.card>summary{padding:16px 18px}
  details.card>:not(summary){margin-left:18px;margin-right:18px}
  details.card>:not(summary):last-child{margin-bottom:18px}

  #view-dashboard>.dash-hero{padding:8px var(--gutter) 32px}
  .dash-hero .eyebrow{font-size:14px;margin-bottom:10px}
  .dash-hero h1{font-size:30px;letter-spacing:-.03em}
  .dash-ask{min-height:60px;margin-top:22px;padding:8px 8px 8px 16px;font-size:15px;border-radius:14px}
  .dash-ask__go{padding:0 16px;font-size:15px}
  /* At this width the words are cut to "or t…"; two lines read better. */
  .dash-ask__text{white-space:normal;line-height:1.35}
  .dash-starters{gap:8px;margin-top:16px}
  .dash-starter{min-height:44px;padding:0 14px;font-size:14px}
  #view-dashboard>.activity-kpis{margin-top:28px;gap:12px}
  /* Two tiles share a 296px row at 320px, and a figure like "$2045.00" at
     a fixed 32px is wider than its tile: the page scrolled sideways. The
     figure scales with the screen instead, and a longer one than that
     wraps rather than pushing the page. */
  .kpi{padding:14px;min-width:0}
  .kpi .v{font-size:clamp(22px,7vw,32px);overflow-wrap:anywhere}
  /* The 44px tap floor, centred where the text sat. */
  .dashboard-kpi-docs>.dashboard-kpi{padding-bottom:56px}
  .dashboard-kpi-action{left:14px;bottom:4px;min-height:44px}
  /* The cards stack on the figures' 12px step, and their rows take the
     phone card inset. */
  #view-dashboard>.dashboard-columns{margin-top:12px;gap:12px}
  #view-dashboard>details.card{margin-top:12px}
  .dashboard-columns>.card>*{padding-left:18px;padding-right:18px}
  .dashboard-columns>.card>.pageHeading{padding-top:18px;padding-bottom:14px}
  /* Too narrow for the time and kind to keep a column of their own: they
     drop to a third line under what happened, time first. */
  .dashboard-event{grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto auto;
    grid-template-areas:"main main" "when type";gap:6px 0}
  .dashboard-event-when{text-align:left;line-height:20px}
  .dashboard-event-when::after{content:"·";content:"·" / "";margin:0 6px}
  .dashboard-event-type{justify-self:start;align-self:start}

  #authScreen>.centerCard{margin:24px auto 0;padding:22px}
  /* A table that still scrolls sideways here keeps the compact cells it
     had, so as much of it as possible is on screen before the swipe. */
  table{font-size:13px}
  th,td{padding:9px 8px}
  /* A long label wraps inside its button rather than pushing the button,
     and the card around it, past the edge of the screen. */
  .btn{white-space:normal}
  /* The phone rule `.btn.sec{margin-left:auto}` is for the old header's
     Sign out; on the unit header it flung Rename and Add photo to the far
     edge, away from the name and photo they act on. */
  .udet-header .btn.sec{margin-left:0}
  /* A date field's calendar icon and padding gave it a min-content wider
     than the 140px filter column, so From ran over Through. The column
     may shrink; the field fills whatever it gets. */
  .activity-filters label{grid-template-columns:minmax(0,1fr)}
  .activity-filters input,.activity-filters select{min-width:0}
  /* The pills are slimmer than 44px; the floor still applies. */
  .settingsNav button{min-height:44px}
  /* The side rail (Settings, Equipment) turns into a row here, and Settings'
     four sections are 554px of unwrappable labels in a 294px pane: the
     sections wrap and share each row instead. The Schedule bar is
     excluded; its two words fit. */
  .settingsLayout:not(.settingsLayout--bar) .settingsNav{flex-wrap:wrap;gap:4px}
  .settingsLayout:not(.settingsLayout--bar) .settingsNav button{flex:1 1 auto;text-align:center;
    background:#f9fafb;border-bottom:0}
  .settingsLayout:not(.settingsLayout--bar) .settingsNav button.on{background:#eaecf0}
  .settingsLayout--bar .settingsNav button{border-bottom:0}
  /* The agenda's row buttons measured 34px: two short lines of text, and a
     button sized to them. The row is the tap target, so it takes the 44px
     floor and centres its two lines in it. */
  .cal-agenda .cal-open,.cal-today-card .cal-open{min-height:44px;justify-content:center}
  /* Sheets rise from the bottom here, so the rounded edge and the outline
     move to the top. */
  .pm-sheet,.ask-outcome-sheet{border:1px solid var(--outline);border-bottom:0;
    border-radius:var(--radius-lg) var(--radius-lg) 0 0}
  /* The bottom bar is solid white with a hairline along its top edge. The
     current tab is a tinted pane rather than a 2px rule: shape plus ink,
     not ink alone. */
  body.signed-in .mobilenav{background:var(--panel);
    border-top:1px solid var(--outline);box-shadow:none}
  .mobilenav button{border-top:0;font-size:11px;font-weight:500}
  .mobilenav button.on{background:#eef4ff;color:var(--accent-text);font-weight:600}
  body.signed-in .morepanel:not(.hide){background:var(--panel);
    border-color:var(--outline);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg)}
  .morepanel button{background:var(--panel);border-color:var(--outline)}
}

/* ==========================================================================
 * CLEAN, carried through (2026-09-30)
 * ==========================================================================
 *
 * The 2026-09-28 pass restyled the chrome, Today, and the Schedule and
 * Equipment pages. The screens it did not reach -- the Settings panels,
 * Locations, Ask, the unit page and the plan's forms -- still drew their
 * content straight onto the page, or in the older type. These rules put them
 * on the same panes, headings and rows. Like the rest of CLEAN they only
 * change how things look.
 */

/* Settings panels (Technicians, Staff names, Reminders, Kitchen profile)
   write a bare <h2> and their fields straight into the panel. Machines and
   Manuals sit in panes; so do these now. A panel that opens with a heading is
   the loose kind -- the others already bring their own .card. */
.settingsPanel:has(> h2:first-child){background:var(--panel);border:1px solid var(--outline);
  border-radius:var(--radius-lg);padding:24px}
.settingsPanel>h2:first-child{font-family:var(--sans);font-size:18px;font-weight:600;
  letter-spacing:-.01em;line-height:1.3;margin:0 0 6px;color:var(--ink)}
.settingsPanel>h2:first-child+p{margin-top:0;color:var(--dim);font-size:14px}
.settingsPanel>p{font-size:14px;color:var(--dim);max-width:72ch}
.settingsPanel>label,.settingsPanel form>label{display:block;font-size:14px;font-weight:500;
  color:#344054;margin:18px 0 6px}
.settingsPanel>select,.settingsPanel>input{max-width:420px}

/* Locations: the same pane and table as Machines. */
.locations-card>p.muted{margin-top:-6px;margin-bottom:4px;color:var(--dim)}
.locations-card .activity-filters{align-items:end;margin-top:16px}
.locations-card .activity-filters .btn.sec{align-self:end}
.locations-card>.units-table-wrap{margin-top:8px;overflow:hidden}
.locations-table th{background:#f9fafb;padding:12px 18px;white-space:nowrap}
.locations-table td{padding:14px 18px}
.locations-table a{text-decoration:none;font-weight:500}
.locations-table a:hover{text-decoration:underline}
.locations-table td:first-child a{color:var(--ink);font-size:15px}
.locations-table td:last-child{text-align:right}
.locations-table th:last-child{text-align:right}
.settingsPanel>.locations-card+details.card{margin-top:16px}

/* --- Ask ------------------------------------------------------------------ */
#view-ask .cols{gap:20px}
#view-ask .ask-starters{gap:8px}

/* --- the unit page ---------------------------------------------------------
 * The header is the page's heading: the name at heading size, what it is
 * under it, and its figures as the same tinted pills Today uses. */
.udet-back{margin-bottom:8px}
.udet-hero-grid,.udet-content-grid{gap:20px}
.udet-content-grid{margin-top:20px}
.udet-column>.card+.card,.udet-column>.card+div.card{margin-top:20px}
.card.udet-header h3.udet-name,.udet-header .udet-name{font-size:28px;font-weight:700;letter-spacing:-.025em;line-height:1.15}
.udet-header .udet-sub{margin-top:4px;font-size:15px;color:var(--dim)}
.udet-header .udet-serial{margin-top:2px;font-size:13px;color:var(--faint)}
.udet-badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.udet-badge{display:inline-flex;align-items:center;padding:3px 10px;border-radius:var(--radius-pill);
  background:#f2f4f7;color:var(--dim);font-size:13px;font-weight:500;line-height:1.4}
.udet-badge[data-tone="overdue"]{background:var(--tone-overdue-bg);color:var(--tone-overdue-ink);font-weight:600}
.udet-badge[data-tone="today"]{background:var(--tone-today-bg);color:var(--tone-today-ink);font-weight:600}
.udet-header-row{gap:18px;align-items:center}
.udet-doors{justify-content:center;height:100%}
.udet-hero-grid>.card{display:flex;flex-direction:column;justify-content:center}
.udet-hero-grid>.udet-header{justify-content:flex-start}

/* Due on this unit: rows on hairlines inside the pane, the way Today's lists
   read, rather than a box per task with two full-width buttons under it.
   The task is a tinted status row; its two actions sit at its right end. */
.udet-section{margin-top:20px}
.udet-section-h{margin:0 0 4px;font-size:14px;font-weight:600;color:var(--ink)}
.udet-section-h .tiny{font-size:13px;font-weight:500;color:var(--faint)}
.udet-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;
  margin:0;padding:10px 0;background:none;border:0;border-top:1px solid var(--line);border-radius:0;box-shadow:none}
.udet-row:first-of-type{border-top:0}
.udet-row>*{grid-column:1/-1}
.udet-row>.cal-row{grid-column:1;margin:0;padding:8px 12px;font-size:14px;line-height:1.35;
  border-radius:8px}
.udet-row>.udet-task-actions{grid-column:2;grid-row:1;display:flex;gap:6px;margin:0}
.udet-row>.udet-task-actions{flex-wrap:nowrap}
.udet-row .udet-task-actions .btn{flex:none;width:auto;min-height:36px;padding:6px 12px;font-size:13.5px;font-weight:500}
.udet-row>.cal-row{flex-wrap:nowrap;align-items:flex-start;border:0;border-left:3px solid var(--cal-upcoming-rail,#98a2b3)}
.udet-row>.cal-row>svg{flex:none;margin-top:2px}
.udet-row .cal-title{flex:1;min-width:0;white-space:normal;overflow:visible;text-overflow:clip}
.udet-band-note,.udet-upcoming-date{margin:0 0 0 12px;font-size:13px;color:var(--faint)}
.udet-row.udet-band>.cal-row{opacity:1;background:none;border-left-color:var(--outline-strong)}
#udetSchedule>a,#udetSchedule .tiny>a{font-weight:500}

/* Manuals: each document is a quiet row; Delete is a text-sized control, not
   a full button under every file. */
.udet-manual-list{gap:10px}
.udet-manual-list .doc-review-row .btn.sec[data-doc-delete]{min-height:32px;padding:4px 12px;
  font-size:13px;font-weight:500;color:var(--dim)}
.udet-manual-list .doc-review-row .btn.sec[data-doc-delete]:hover{color:var(--tone-overdue-ink);border-color:#fecdca;background:var(--tone-overdue-bg)}

@media(pointer:coarse){
  .udet-row .udet-task-actions .btn,.udet-manual-list .doc-review-row .btn.sec[data-doc-delete]{min-height:44px}
}
@media(max-width:640px){
  .card.udet-header h3.udet-name,.udet-header .udet-name{font-size:24px}
  /* Too narrow for the actions beside the task: they drop under it. */
  .udet-row{grid-template-columns:minmax(0,1fr)}
  .udet-row>.udet-task-actions{grid-column:1;grid-row:auto}
  .udet-row .udet-task-actions .btn{flex:1 1 0}
}
/* Status on the unit page's rows: the calendar's CLEAN tints (.cal-page
   defines them only for the calendar itself), so an overdue task is the
   same red on every screen. */
.udet-row>.cal-row[data-status="overdue"]{background:var(--tone-overdue-bg);border-left:5px solid #d92d20;color:var(--tone-overdue-ink)}
.udet-row>.cal-row[data-status="today"]{background:var(--tone-today-bg);border-left-color:#dc6803;color:var(--tone-today-ink)}
.udet-row>.cal-row[data-status="done"]{background:#ecfdf3;border-left-color:#079455;color:#067647}
.udet-row>.cal-row[data-status="upcoming"]{background:#f9fafb}
.udet-row.udet-band>.cal-row,.udet-row:not(:has(>.udet-task-actions))>.cal-row{grid-column:1/-1}

/* --- the maintenance plan's forms ------------------------------------------
 * Short answers (a machine, a number, a date) in fields sized for them,
 * rather than every field the full width of a 1400px pane. */
.plan-form-card form{display:grid;grid-template-columns:repeat(2,minmax(0,280px));gap:14px 16px;align-items:end;max-width:600px}
.plan-form-card form>label{display:flex;flex-direction:column;gap:6px;margin:0;font-size:14px;font-weight:500;color:#344054}
.plan-form-card form>label:has([name="title"]),.plan-form-card form>label:has([name="unit_id"]){grid-column:1/-1}
.plan-form-card form>p,.plan-form-card form>div{grid-column:1/-1;margin:0}
.plan-form-card form>p:empty,.plan-form-card form>div:empty{display:none}
.plan-form-card form>button{grid-column:1;justify-self:start}
.plan-form-card>select{max-width:600px}
.plan-form-card>p.tiny{font-size:14px;color:var(--dim)}
#planTable .maintenance-task-open{min-height:32px;padding:4px 12px;font-size:13px;font-weight:500}
@media(max-width:640px){
  .plan-form-card form{grid-template-columns:minmax(0,1fr)}
}
.settingsPanel form>select,.settingsPanel select#tech-default{max-width:420px}
a.btn{text-decoration:none}
.locations-card>.units-table-wrap{overflow-x:auto}
@media(max-width:640px){
  /* Five columns do not fit a phone. The name already carries make and
     model, and the serial is one tap away on the machine itself. */
  .locations-table th:nth-child(3),.locations-table td:nth-child(3),
  .locations-table th:nth-child(4),.locations-table td:nth-child(4){display:none}
  .locations-table th,.locations-table td{padding:12px;white-space:normal}
  .locations-card .activity-filters{grid-template-columns:minmax(0,1fr)}
}

/* ==========================================================================
 * Loom review fixes (2026-09-30). One block, appended after CLEAN so it
 * wins on equal specificity. Each group names the review note it answers.
 * ========================================================================== */

/* "Make the You button blue." The account chip is the one filled control
   in the header now that Ask left the bar. */
.accountMenu>summary{background:var(--accent-text);color:#fff}
.accountMenu>summary:hover{background:var(--accent-dark)}
.accountMenu[open]>summary{background:var(--accent-dark)}

/* --- Today: the ask box is a real form ---------------------------------- */
form.dash-ask{cursor:text}
form.dash-ask:focus-within{border-color:var(--accent);
  box-shadow:0 0 0 4px var(--accent-soft),0 8px 24px -12px rgba(16,24,40,.14)}
form.dash-ask input.dash-ask__input{flex:1;width:auto;min-width:0;height:48px;min-height:0;margin:0;padding:0;
  border:0;border-radius:0;outline:0;background:transparent;font:inherit;font-size:17px;color:var(--ink);box-shadow:none}
form.dash-ask input.dash-ask__input::placeholder{color:var(--faint)}
form.dash-ask input.dash-ask__input:focus,form.dash-ask input.dash-ask__input:focus-visible{outline:0;box-shadow:none;border:0}
button.dash-ask__go{border:0;cursor:pointer}
button.dash-ask__go:hover{background:var(--accent-dark)}
.dash-ask-scope{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;max-width:760px;margin:14px auto 0}
.dash-ask-scope label{margin:0}
.dash-ask-scope select{width:auto;min-width:200px;max-width:100%;min-height:40px;padding:0 36px 0 14px;
  border-radius:var(--radius-pill);font-size:14.5px;background-color:var(--panel)}
button.dash-starter{cursor:pointer;font:inherit;font-size:15px}
/* The answer dialog: centred over a dimmed Today, its own scroll, the
   follow-up box pinned at the foot. A full-height sheet on a phone. */
body.modal-open{overflow:hidden}
.ask-modal-backdrop{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  padding:32px 16px;background:rgba(16,24,40,.52);animation:askFade .15s ease-out}
.ask-modal-backdrop.hide{display:none}
.ask-modal{display:flex;flex-direction:column;width:min(760px,100%);max-height:min(86vh,900px);
  background:var(--panel);border-radius:18px;box-shadow:0 24px 64px -12px rgba(16,24,40,.35);overflow:hidden;
  animation:askRise .18s ease-out}
.ask-modal__head{display:flex;align-items:flex-start;gap:16px;padding:20px 24px 16px;border-bottom:1px solid var(--outline)}
.ask-modal__titles{flex:1;min-width:0}
.ask-modal__machine{margin:0 0 4px;font-size:13px;font-weight:600;color:var(--accent-text)}
.ask-modal__head h2{margin:0;font-size:19px;line-height:1.35;font-weight:650;color:var(--ink);overflow-wrap:anywhere}
.ask-modal__close{flex:none;width:40px;height:40px;border:0;border-radius:var(--radius-pill);background:var(--tile);
  color:var(--dim);font-size:24px;line-height:1;cursor:pointer}
.ask-modal__close:hover{background:var(--tile-hover);color:var(--ink)}
.ask-modal__body{flex:1;min-height:120px;overflow-y:auto;padding:20px 24px;overscroll-behavior:contain}
.ask-modal__follow{display:flex;gap:10px;padding:14px 24px 18px;border-top:1px solid var(--outline);background:#fcfcfd}
.ask-modal__follow input{flex:1;min-width:0;margin:0}
.ask-modal__follow .btn{flex:none}
@keyframes askFade{from{opacity:0}to{opacity:1}}
@keyframes askRise{from{opacity:0;transform:translateY(12px) scale(.98)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.ask-modal-backdrop,.ask-modal{animation:none}}
@media(max-width:640px){
  .ask-modal-backdrop{padding:0;align-items:stretch}
  .ask-modal{width:100%;max-height:none;height:100%;border-radius:0}
  .ask-modal__head{padding:16px}
  .ask-modal__body{padding:16px}
  .ask-modal__follow{padding:12px 16px calc(12px + env(safe-area-inset-bottom))}
}
/* Ask mounted in the dialog: the answer takes the full width, and its own
   card chrome goes -- the dialog is the card. */
.cols.ask-embedded{display:block}
.cols.ask-embedded .card{padding:0;border:0;box-shadow:none;background:none;margin:0}
.cols.ask-embedded .card>h3{display:none}
@media(max-width:640px){
  .dash-ask-scope{flex-direction:column;align-items:stretch}
  .dash-ask-scope select{width:100%;min-width:0;min-height:44px}
  form.dash-ask input.dash-ask__input{font-size:16px;height:44px}
}

/* --- Calendar: which day is today, and tasks grouped by machine --------- */
.cal-today-pill{display:inline-flex;align-items:center;height:18px;padding:0 7px;border-radius:var(--radius-pill);
  background:var(--accent-text);color:#fff;font-size:11px;font-weight:600;letter-spacing:.01em}
.cal-page table.cal-month th.is-today{color:var(--accent-text);font-weight:700;
  box-shadow:inset 0 -2px 0 var(--accent-text)}
.cal-page .cal-cell.is-today{background:#f5f8ff;box-shadow:inset 0 0 0 2px var(--accent-text)}
.cal-page .cal-cell.is-today .cal-daynum{color:var(--accent-text);font-weight:700}
.cal-page .cal-agenda-card.is-today{background:#f5f8ff}
.cal-page .cal-agenda-card.is-today .cal-agenda-date{flex-direction:column;align-items:flex-start;
  justify-content:center;gap:4px;color:var(--accent-text)}
/* A machine with several tasks that day: its name once, its tasks under it. */
.cal-group{margin:0 0 4px;padding:4px 4px 2px;border-radius:8px;background:#f9fafb;border:1px solid var(--outline)}
.cal-group-name{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:0 4px 3px;
  font-size:11.5px;font-weight:600;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cal-group-count{flex:none;min-width:18px;height:16px;padding:0 5px;border-radius:var(--radius-pill);
  background:var(--outline);color:var(--dim);font-size:10.5px;font-weight:600;line-height:16px;text-align:center}
.cal-page .cal-group .cal-row{margin-bottom:2px}
.cal-page .cal-agenda .cal-group{padding:8px 8px 4px;margin-bottom:8px;border-radius:12px}
.cal-page .cal-agenda .cal-group-name{font-size:13px;padding:0 4px 6px}
.cal-location select{width:auto;min-width:180px;min-height:36px;margin-left:8px}

/* --- Maintenance plan: location, then machine, then its tasks ----------- */
#planTable tr.plan-loc th{padding:22px 12px 8px;background:none;border-bottom:1px solid var(--outline-strong);
  font-size:15px;font-weight:700;color:var(--ink);text-align:left;letter-spacing:-.01em}
#planTable tbody tr.plan-loc:first-child th{padding-top:6px}
#planTable tr.plan-loc th span,#planTable tr.plan-unit th span{margin-left:10px;font-size:12.5px;font-weight:500;color:var(--faint)}
#planTable tr.plan-unit th{padding:12px 12px 6px;background:#f9fafb;border-bottom:1px solid var(--outline);
  font-size:14px;font-weight:600;text-align:left;color:var(--ink)}
#planTable tr.plan-unit th a{color:var(--ink);text-decoration:none}
#planTable tr.plan-unit th a:hover{color:var(--accent-text);text-decoration:underline}
#planTable .plan-overdue{color:var(--tone-overdue-ink);font-weight:600}
#planTable tr.plan-task td:first-child{padding-left:24px}
@media(max-width:640px){
  #planTable tr.plan-loc,#planTable tr.plan-unit{display:block;padding:0;border:0;background:none;border-radius:0}
  #planTable tr.plan-loc th,#planTable tr.plan-unit th{display:block;padding:14px 2px 4px;background:none;border:0}
  #planTable tr.plan-unit th{padding-top:4px;font-size:13.5px;color:var(--dim)}
  #planTable tr.plan-loc th span,#planTable tr.plan-unit th span{margin-left:6px}
  #planTable tr.plan-task td:first-child{padding-left:0;padding-right:68px;font-weight:600;color:var(--ink)}
  #planTable tr.plan-task td:nth-child(2){font-weight:400;color:inherit}
  #planTable tr.plan-task td:nth-child(3){grid-column:auto}
}

/* --- Task details: the same facts, in the same rows, on every task ------ */
.pm-facts{display:grid;gap:8px;margin:0}
.pm-fact{display:grid;grid-template-columns:128px minmax(0,1fr);gap:12px;align-items:baseline}
.pm-fact dt{font-size:13px;color:var(--faint);font-weight:500}
.pm-fact dd{margin:0;font-size:14px;color:var(--ink);overflow-wrap:anywhere}
.pm-facts .pm-source-quote{margin:2px 0 0}
.pm-note-hint{margin:6px 0 12px}
@media(max-width:400px){.pm-fact{grid-template-columns:minmax(0,1fr);gap:2px}}

/* --- Manuals: grouped by location, then machine ------------------------- */
.doc-room{margin:0 0 20px}
.doc-room:last-of-type{margin-bottom:8px}
.doc-room-name{margin:0 0 8px;font-size:13px;font-weight:600;letter-spacing:.02em;color:var(--faint)}
.doc-group{margin:0 0 8px;border:1px solid var(--outline);border-radius:var(--radius);background:var(--panel)}
.doc-group>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:48px;
  padding:10px 14px;cursor:pointer;list-style:none;border-radius:var(--radius)}
.doc-group>summary::-webkit-details-marker{display:none}
.doc-group>summary::after{content:"";flex:none;width:8px;height:8px;margin-left:4px;
  border-right:2px solid var(--faint);border-bottom:2px solid var(--faint);transform:rotate(45deg) translateY(-2px);
  transition:transform .15s ease}
.doc-group[open]>summary::after{transform:rotate(-135deg) translateY(-2px)}
.doc-group>summary:hover{background:var(--tile)}
.doc-group-name{flex:1;min-width:0;font-weight:600;color:var(--ink);overflow-wrap:anywhere}
.doc-group-meta{flex:none;font-size:13px;color:var(--faint)}
.doc-group-none{color:var(--tone-today-ink);font-weight:500}
.doc-group-body{padding:4px 14px 14px;border-top:1px solid var(--outline)}
.doc-group-body .udet-manual-list{margin-top:10px}

/* The old glyphs are gone; the link says what it does. */
.udet-manual-go{color:var(--accent-text);font-size:13px;font-weight:600}
.udet-manual:hover .udet-manual-go{text-decoration:underline}

/* --- Invoices: an amount on every row, and a flag when one is missing --- */
.inv-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.inv-row>.inv-open{flex:1 1 220px;min-width:0}
.inv-amount{flex:none;min-height:36px;padding:0 12px;border:1px solid var(--outline);border-radius:var(--radius-pill);
  background:var(--panel);color:var(--ink);font:inherit;font-size:14px;font-weight:600;cursor:pointer;
  font-variant-numeric:tabular-nums}
.inv-amount:hover{border-color:var(--outline-strong)}
.inv-amount-missing{border-style:dashed;border-color:var(--tone-today-ink);background:var(--tone-today-bg);
  color:var(--tone-today-ink)}
.inv-amount-missing:hover{background:var(--tone-today-bg-hover);border-color:var(--tone-today-ink)}
.inv-amount-edit{display:inline-flex;align-items:center;gap:6px}
.inv-amount-edit input{width:110px;min-height:36px}
.inv-amount-edit .btn{min-height:36px;padding:0 12px}
.inv-row .inv-meta{flex-basis:100%}
.inv-missing-note{margin:0 0 10px;padding:8px 12px;border-radius:var(--radius);
  background:var(--tone-today-bg);color:var(--tone-today-ink)}
.inv-form-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
@media(max-width:480px){.inv-form-pair{grid-template-columns:minmax(0,1fr);gap:0}}

/* --- Logs: invoice rows ------------------------------------------------- */
.activity-invoice td:nth-child(5){color:var(--accent-text);font-weight:600}
.activity-amount-missing{display:inline-flex;align-items:center;min-height:28px;padding:0 10px;
  border:1px dashed var(--tone-today-ink);border-radius:var(--radius-pill);background:var(--tone-today-bg);
  color:var(--tone-today-ink);font-size:13px;font-weight:600;text-decoration:none;white-space:nowrap}
.activity-amount-missing:hover{background:var(--tone-today-bg-hover)}
.activity-missing{color:var(--tone-today-ink)}

/* --- Unit page photo: bigger and full resolution ------------------------ */
.unit-photo.udet-photo{width:96px;height:96px;border-radius:14px;background:#fff}
@media(max-width:640px){.unit-photo.udet-photo{width:72px;height:72px}}
