/* ============================================================================
   Zeved Boards — styles.css
   Identity comes from structure (frame, scratch-out, surface tone), never
   costume. UIUX marker map: UIUX §1 global, UIUX §2 tokens, UIUX §3
   geometry/calendar, UIUX §4 note, UIUX §7 menu, UIUX §8 motion, UIUX §9
   toast, UIUX §10 board list/rail, UIUX §13 typography.
   Comments state the rule that holds now and cite UIUX §x / B<n>; engine
   quirks the rule depends on stay; how a value got there lives only in
   The Docket/DECISIONS.md (AGENTS.md).
   ========================================================================== */

/* --- UIUX §13.1 The typeface: Montserrat Alternates, self-hosted, no CDN --
   Three weights, Latin-subset woff2, committed in fonts/ (B50). font-display:
   swap so capture is never blocked on a font load. */
@font-face {
  font-family: 'Montserrat Alternates';
  font-style: normal; font-weight: 400; font-display: swap;
  src: url(fonts/MontserratAlternates-400.woff2) format('woff2');
}
@font-face {
  font-family: 'Montserrat Alternates';
  font-style: normal; font-weight: 600; font-display: swap;
  src: url(fonts/MontserratAlternates-600.woff2) format('woff2');
}
@font-face {
  font-family: 'Montserrat Alternates';
  font-style: normal; font-weight: 800; font-display: swap;
  src: url(fonts/MontserratAlternates-800.woff2) format('woff2');
}

/* --- UIUX §2 Design tokens — one theme, dark-only (UIUX §2.1, B48) --------
   The page reads as one scene: water closing both ends of the sheet, the
   deep between, and the note as the one lit thing on the deep (UIUX §2.2,
   B58). The water is a field, not a value (UIUX §2.8). */
:root {
  --deep:            #020812;   /* the canvas — the deepest surface on the page (B58) */
  --chrome:          #020812;   /* the room behind the page takes the deep's value (B52/B58) */
  --card:            #08152c;   /* the title compartment: its border separates, not its fill (UIUX §2.5) */
  --frame:           #698ebf;   /* the card's border and both full-width rules (UIUX §2.5, B61) */
  --note:            #a0d4da;   /* the note — the brightest thing on the page (UIUX §2.2, B58) */
  /* The water's fall, authored as opaque stops so the extremes are exactly
     these colours (UIUX §2.8). Its darkest reach meets the deep at 1.58, so
     the band's rule is load-bearing (UIUX §2.5, B58). */
  --water-top:       #34697f;
  --water-mid:       #255265;
  --water-bot:       #163646;
  --water-bot-a:     22 54 70;      /* the darkest stop as channels, for the sections' falloff */
  /* Ink — one per surface, not one per app (UIUX §2.3). The poles; the
     binding is .on-dark/.on-light below. */
  --ink-light:       #f4f5f1;
  --ink-dark:        #031019;
  /* :root binds the light pole for the dark-only scheme (B48). The binding
     must exist here: a token composed from --ink-a at :root (--hairline)
     resolves at its declaration element, and an undefined var() makes it
     invalid at computed-value time — unset everywhere it is read. */
  --ink:             var(--ink-light);
  --ink-a:           244 245 241;
  /* That surface's ink at the lowest alpha clearing 3:1 on it (UIUX §2.5). */
  --hairline:        rgb(var(--ink-a) / 0.4);
  /* Accents (UIUX §2.6, B52): text only on a near-black ground; anywhere else
     a fill carrying --ink-dark. --danger is the only warm hue among the
     accents — the one thing that destroys is not cool water (B58/B59). */
  --accent-restore:  #b6dee2;   /* the note's own hue and saturation, lifted */
  --accent-page:     #6d9cb0;   /* the water's hue — the primary fill about boards (B59) */
  --danger:          #E2A08C;
  /* The highlight wash (UIUX §2.6.1, B71): a per-note emphasis the user
     asserts, not a board-type signal — so it does not rotate with the ladder.
     Amber, apart from the cool note families, carrying --ink-dark at >12:1. */
  --highlight:       #F2D64B;
  /* The reminder fill (UIUX §2.6.2, B109/B126): a per-note state the user
     asserts, so it does not rotate with the ladder. The Learning ladder's
     --note rung, carrying --ink-dark at 11.84:1. */
  --reminder:        #e6c2c9;
  /* The note toolbar's four tab fills share one luminance rung — the note
     rung 0.5962 — each in its own hue family, each carrying --ink-dark at
     11.83–11.84:1 (UIUX §2.6.4, B127). Only --tab-complete rotates (B67). */
  --tab-complete:    #becce2;   /* the board's frame-family hue at the shared rung (rotates, B67/B127) */
  --tab-highlight:   #e7ca42;   /* the amber family at the shared rung */
  --tab-remind:      #e6c2c9;   /* = --reminder, the rung's reference value */
  --tab-delete:      #ebc2b6;   /* the warm danger family at the shared rung */
  /* The carried glow (UIUX §2.6.3, B110): the one status state beyond
     normal/complete, asserted by the record, so it does not rotate with the
     ladder. The highlight's amber one rung down — status sits below
     attention — carrying --ink-dark at 10.62:1. */
  --carried:         #E1BE26;
  --reminder-glow:   #c6a400;   /* the reminder card-frame bloom: the carried amber's hue, one rung down (UIUX §2.6.2, B132) */
  --elevation:       0 2px 8px rgb(0 0 0 / 0.45);
  --elevation-up:    0 -2px 8px rgb(0 0 0 / 0.45);   /* --elevation reflected for a bottom sheet: the lift lands on the top seam (UIUX §2.4) */
  --elevation-inset: inset -3px 0 8px rgb(0 0 0 / 0.16), inset 0 2px 4px rgb(0 0 0 / 0.10);
  /* The touch floor (UIUX §6) — = HIT_FLOOR in state.js. Read by every row and
     control that IS the floor by ruling (B68), not only by the hit collars. */
  --hit-floor:       44px;
  /* Motion (UIUX §8): one curve, two durations — = LEAVE_MS and SWAP_MS in
     state.js (PRD §9.5). */
  --ease:            cubic-bezier(0.22, 0.61, 0.36, 1);
  --leave-ms:        200ms;
  --swap-ms:         260ms;
}

/* The ink pole flips at the surface boundary and inherits downward
   (UIUX §2.3): everything downstream keeps reading var(--ink)/var(--ink-a).
   Channels are space-separated so they compose with slash alpha (CSS Color 4
   rejects comma channels there). */
.on-dark  { --ink: var(--ink-light); --ink-a: 244 245 241; }
.on-light { --ink: var(--ink-dark);  --ink-a: 3 16 25; }

/* --- UIUX §2.2.2 The ladder rotates with the board type (B67) -------------
   A board type is a whole scene, not a rung on one. The ladder's single axis
   stays luminance (UIUX §2.2.1 rule 1): every rung below reproduces the To-Do
   rung's WCAG relative luminance to the 4dp the record prints, so every ratio
   UIUX §2.2, UIUX §2.3, UIUX §2.5 and UIUX §2.7 publish holds on all five.
   Only the family's hue moves; UIUX §2.2.2 prints the residuals.

   Bound by rebinding the token names, never by overriding a background: every
   layer that draws a board reads var(), so it carries the new hue for free.

   --chrome does not rotate — the room behind the page is one room (B55).
   Nor do the ink poles (per surface, UIUX §2.3) or the accents (on chrome,
   UIUX §2.6).

   To-Do needs no block: it IS :root, which is also what a board shows before
   boot has stamped data-cat. A record with no category files under Notes —
   catOf()'s read-site default in state.js (B21) — and renders violet. */
#board[data-cat="idea"], #pane[data-cat="idea"] {
  --deep:        #000a06;
  --card:        #001a0e;
  --water-top:   #486b49;
  --water-mid:   #345439;
  --water-bot:   #1f3825;
  --water-bot-a: 31 56 37;
  --frame:       #52997f;
  --tab-complete: #b3d2c3;
  --note:        #b9d2b2;
}
/* Storage key 'unsorted'; the category's label is "Notes" (B78). */
#board[data-cat="unsorted"], #pane[data-cat="unsorted"] {
  --deep:        #0c0512;
  --card:        #1e0f28;
  --water-top:   #6d5b83;
  --water-mid:   #534769;
  --water-bot:   #382e47;
  --water-bot-a: 56 46 71;
  --frame:       #9d80b9;
  --tab-complete: #d3c6e0;
  --note:        #cec6ed;
}
/* Learning — the fourth ladder, a pale rose (UIUX §2.2.2, B74). The dark
   rungs sit at To-Do's luminance where the gamut leaves them almost no
   chroma; the light rungs are chosen for paleness within their ratio window. */
#board[data-cat="learning"], #pane[data-cat="learning"] {
  --deep:        #11040b;
  --card:        #260e12;
  --water-top:   #855562;
  --water-mid:   #6a414c;
  --water-bot:   #472a35;
  --water-bot-a: 71 42 53;
  --frame:       #b57a9b;
  --tab-complete: #dfc4ce;
  --note:        #e6c2c9;
}
/* The Calendar — the fifth ladder (UIUX §2.2.2, B95). #cal-view is a
   body-level section and nothing inside it carries data-cat (makeCalDay in
   boards.js builds plain divs), so the rebinding scopes exactly the calendar
   chrome. Linked boards opened from the calendar are #board elements, not
   children of this one, and keep their own data-cat scopes. */
#cal-view {
  --deep:        #110501;
  --card:        #251002;
  --water-top:   #815a42;
  --water-mid:   #694432;
  --water-bot:   #462d1a;
  --water-bot-a: 70 45 26;
  --frame:       #b48158;
  --tab-complete: #dbc8b3;
  --note:        #e3c6aa;
}
/* The sections' falloff (UIUX §2.8) — one token, three readers (#band-fill,
   #lot, #cal-band). Declared on the ladder ROOTS, not :root: a var() inside a
   custom property resolves where that property is declared, so on :root the
   token would carry the To-Do stop onto every ladder. */
#board, #cal-view {
  --water-falloff: radial-gradient(118% 76% at 50% 24%, rgb(0 0 0 / 0) 32%, rgb(var(--water-bot-a) / 0.72) 128%);
}
/* A list/rail card is a small rendering of what it names (UIUX §10), so it
   previews its board's hue. A section is a framed, tinted tray in its own
   family (B72): the water's upper fall for its cards, --card as the tray
   ground, --frame as the tray's inset frame. .card-drag-ghost is fixed to the
   viewport off document.body, outside its section's scope, so boards.js
   stamps data-cat onto the ghost itself; a single card with no tray, it takes
   only the two water stops. The picker button is the same tray idiom (B74,
   B89). To-Do must re-assert its own rung (B77): a tray/tile renders inside a
   board's data-cat scope and would otherwise inherit that board's family. */
.board-cat[data-cat="todo"],
.cat-button[data-cat="todo"] {
  --frame:       #698ebf;
  --card:        #08152c;
}
.board-cat[data-cat="idea"],
.cat-button[data-cat="idea"] {
  --frame:       #52997f;
  --card:        #001a0e;
}
.board-cat[data-cat="unsorted"],
.cat-button[data-cat="unsorted"] {
  --frame:       #9d80b9;
  --card:        #1e0f28;
}
.board-cat[data-cat="learning"],
.cat-button[data-cat="learning"] {
  --frame:       #b57a9b;
  --card:        #260e12;
}
.board-cat[data-cat="todo"],
.card-drag-ghost[data-cat="todo"],
.cat-button[data-cat="todo"] {
  --water-top:   #34697f;
  --water-mid:   #255265;
}
.board-cat[data-cat="idea"],
.card-drag-ghost[data-cat="idea"],
.cat-button[data-cat="idea"] {
  --water-top:   #486b49;
  --water-mid:   #345439;
}
.board-cat[data-cat="unsorted"],
.card-drag-ghost[data-cat="unsorted"],
.cat-button[data-cat="unsorted"] {
  --water-top:   #6d5b83;
  --water-mid:   #534769;
}
.board-cat[data-cat="learning"],
.card-drag-ghost[data-cat="learning"],
.cat-button[data-cat="learning"] {
  --water-top:   #855562;
  --water-mid:   #6a414c;
}

/* --- UIUX §1 Global rules ------------------------------------------------ */
* { box-sizing: border-box; }

html, body { margin: 0; height: 100%; width: 100%; }

/* The desk is not a surface and there is no desk token (UIUX §2.2): the page
   behind a desktop board swap keeps a plain black background. */
html { background: #000; }

body {
  color: var(--ink);
  font-family: 'Montserrat Alternates', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-feature-settings: "kern" 1;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  overscroll-behavior: none;
  touch-action: none;               /* the board never pans/zooms (UIUX §5, B12) */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* Text inside editable regions is selectable; nothing else is. */
[contenteditable] { -webkit-user-select: text; user-select: text; }

/* ============================================================================
   UIUX §3 Board view — layout geometry
   ========================================================================== */
#board-view {
  position: fixed;
  inset: 0;
  overflow: hidden;                   /* the desk behind it is html's black (UIUX §2.2) */
}

/* The single logical page: a fixed 900-unit reference width, height grown to
   fill the viewport (--logical-h, set in geometry.js), placed and scaled via
   --offx / --offy / --rs. transform-origin top-left keeps stored x,y
   truthful. */
#board {
  position: absolute;
  left: 0; top: 0;
  width: var(--logical-w, 900px);
  height: var(--logical-h, 1000px);
  transform-origin: 0 0;
  transform: translate(var(--offx, 0px), var(--offy, 0px)) scale(var(--rs, 1));
  /* The canvas is the deep (UIUX §2.2, B58): flat under the dither below. */
  background: var(--deep);
  touch-action: none;

  /* The band's geometry, declared once and derived from here (B35): every
     edge in UIUX §3 — both zones, the card, the rule, the lot — reads these,
     so the card resizes in one place and the columns follow. */
  --gutter:   2.6667%;                 /* 24/900: the sheet's one margin (B32) */
  --card-gap: 8px;                     /* card ↔ zone */
  /* 340/900, floored so each zone keeps a 100px runway: "Requirements" cannot
     wrap, so a narrower zone overflows the gutter instead of reflowing. */
  --card-w: min(37.7778%, 100% - 2 * (var(--gutter) + 100px + var(--card-gap)));
  --card-l: calc((100% - var(--card-w)) / 2);

  /* The band's vertical geometry is sized by the type it holds (UIUX §3.1,
     B37/B47): content hangs from --band-top, the rule is the band's bottom
     edge. --rule-y is set in updateBoardGeometry (geometry.js) from the
     tallest zone's line count; 61px is the two-line floor until it speaks. */
  --band-top: 14px;
  --rule-y:   61px;
}

/* Dither at anti-banding amplitude over the whole sheet (UIUX §2.8, B58).
   Never an event target: a pseudo-element is not in the DOM, so the recognizer
   still sees bare canvas beneath it. */
#board::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.055;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='4' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
}

/* ---- UIUX §3 / PRD §6.1 Anchors (furniture; render beneath notes) ------- */
.anchor {
  position: absolute;
  z-index: 1;
  box-sizing: border-box;
  padding: 2px 0 4px;
  min-height: var(--hit-floor);       /* long-pressable even when empty (PRD §6.1) */
  color: var(--ink);
  caret-color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: break-word;
  outline: none;
  line-height: 1.3;
}
/* Placeholder only when empty and unfocused; vanishes on focus with no
   select-and-delete; returns on empty blur (UIUX §3). It takes the ground's
   own ink pole (UIUX §2.3). */
.anchor:not(.filled):not(:focus)::before {
  content: attr(data-placeholder);
  color: var(--ink);
}

/* ---- UIUX §3.1 The top band -------------------------------------------
   Drawn furniture: content, then the full-width rule as the band's bottom
   edge, with each header hanging below it as a tab (B47, B76) — the mirror of
   the Parking Lot at the sheet's other end. Every edge derives from #board:
   --gutter / --card-w / --card-l across, --band-top / --rule-y down. ---- */
#band-fill {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: var(--rule-y);
  /* The band takes the water (UIUX §2.8, B58): light from above, darkening to
     the rule, under the sections' own falloff. */
  background:
    var(--water-falloff),
    linear-gradient(180deg, var(--water-top) 0%, var(--water-mid) 46%, var(--water-bot) 100%);
  z-index: 0;                          /* above the canvas, below the zones */
  pointer-events: none;
}
#band-rule {                           /* same weight and idiom as #lot-rule (B61) */
  position: absolute;
  left: 0; right: 0; top: var(--rule-y);
  height: 1px; background: var(--frame);
  /* Load-bearing: the band's water meets the deep under the 3:1 fill floor at
     this seam, so this rule is the separation (UIUX §2.5). Same z-plane as the
     anchors, DOM-later: it draws over a zone's overflow; the card occludes
     it. */
  z-index: 1;
}
/* The zone is a positioning frame only — pointer-events:none keeps it and its
   label out of the recognizer's hit-test, so the recognizer still sees .anchor
   and bare canvas in the rest of the band. */
.band-zone {
  position: absolute;
  /* The zone is the band's depth: content hangs from --band-top, the label
     sits on the rule at its bottom (B47). */
  top: 0; height: var(--rule-y);
  pointer-events: none;
  z-index: 1;
}
.band-zone .anchor {
  left: 0; right: 0; top: var(--band-top);
  width: auto;
  min-height: var(--hit-floor);        /* stays long-pressable when empty (PRD §6.1) */
  padding: 0;                          /* the box is the type: the band formula counts lines only (B47) */
  pointer-events: auto;
  font-size: 15px; font-weight: 600;
}

/* The linked To Do board's mirrored Requirements lines render in read state
   as the calendar's .cal-note species, in the Calendar ladder's literal rungs
   (UIUX §2.2.2, B163); hand lines below the span stay plain pre-wrap text.
   Read state only: pointer-events none, the anchor stays one surface, and
   enableEditing (interactions.js) restores the plain text before focus. */
.req-squares {
  display: grid;
  /* Phone tier: one chip per line in a single full-width column (B164); the
     desktop tier below restores the square tracks. */
  grid-template-columns: 1fr;
  gap: 6px;                            /* .cal-notes' own gap */
  pointer-events: none;
}
.req-square {
  /* The chip (phone tier, B164): the .cal-note species in every value except
     the box — one line tall, the section's full width, no aspect ratio. */
  outline: 2px solid #b48158;          /* the literal Calendar --frame rung (UIUX §2.2.2, B164): var(--frame) here is the To Do blue */
  outline-offset: -2px;
  border-radius: 4px;
  overflow: hidden;                   /* the fill clips to the 4px radius */
  background: #e3c6aa;                /* the literal Calendar --note rung (UIUX §2.2.2, B163): var(--note) here is the board's own blue */
  color: #031019;                     /* the note rung's approved ink pair (UIUX §2.3) */
  /* Does not inherit the anchor's 15px/600 — the square reads at the
     calendar's own measure (B163). */
  font-size: 12px; font-weight: 400; line-height: 1.4;
  padding: 4px 6px;
  /* One line: a name too long for the phone column is cut with "…" (B164);
     the whole name reads in the editor. */
  display: block;
  white-space: nowrap;
  text-overflow: ellipsis;
  pointer-events: none;
}
html.desktop .req-squares {
  /* The calendar's own square track at the desktop tier (B163). */
  grid-template-columns: repeat(auto-fill, 53px);
}
html.desktop .req-square {
  /* The desktop tier keeps the square (B163): aspect 1/1 in its 53px track,
     two lines read, a third never draws; -webkit-line-clamp needs
     -webkit-box. */
  aspect-ratio: 1 / 1;
  white-space: normal;
  text-overflow: clip;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow-wrap: break-word;
}
/* The header hangs below the rule as a tab in the rule's own ink (UIUX §3.1,
   B76): its top edge lands on the rule, so line + label read as one unit. The
   node carries .on-light, so --ink rebinds to --ink-dark (UIUX §2.5, B54). */
.band-label {
  position: absolute;
  top: 100%;                           /* the rule is the tab's top edge */
  left: 50%; transform: translateX(-50%);
  width: max-content;
  padding: 2px 6px;                    /* just enough to frame the type (UIUX §3.1) */
  text-align: center;
  font-size: 13px; font-weight: 600;
  color: var(--ink);                   /* --ink-dark, rebound by .on-light on the node */
  line-height: 1.3;
  background: var(--frame);            /* the rule's own colour — line + tab, one unit */
  border-radius: 0 0 3px 3px;          /* only the corners that exist below the rule */
}
/* Written from --card-l directly rather than from the card's right edge, so
   neither column depends on the card being centred (B35). */
#zone-components   { left: var(--gutter);
                     right: calc(100% - var(--card-l) + var(--card-gap)); }
#zone-requirements { left: calc(var(--card-l) + var(--card-w) + var(--card-gap));
                     right: var(--gutter); }

/* The card is a compartment (UIUX §3.1, B38): the sheet's top edge is its
   fourth side, so three sides draw and the box starts at 0. Always framed,
   empty or not — the one exception to PRD §6.2's "no empty frames": it is
   permanent furniture. Opaque, so it occludes the rule. */
#anchor-title {
  left: var(--card-l); width: var(--card-w);   /* centred by construction */
  /* Overhangs the band by 22px and occludes the rule (B38, B47). */
  top: 0; min-height: calc(var(--rule-y) + 22px);
  /* Column, not row: a contenteditable's line breaks arrive as sibling nodes,
     and row flex would lay a wrapped title out sideways. */
  display: flex;
  flex-direction: column; justify-content: center;
  padding: calc(var(--band-top) + 6px) 12px 12px;
  /* The card does not separate from the band by fill, so this border carries
     the whole separation (UIUX §2.5). */
  border: 2px solid var(--frame);
  border-top: 0;
  border-radius: 0 0 3px 3px;                  /* only the corners that exist */
  background: var(--card);
  text-align: center;
  /* The card is the hierarchy, not the type, so the title sets at the section
     headers' size (UIUX §3.1). */
  font-size: 15px; font-weight: 600; line-height: 1.3;
}

/* The linked To Do board's title seat (B133): the generated name, with the
   board's date beneath it; both render-time (render.js) and pinned, so this
   seat never holds a caret. The date rides the ground's own ink pole at a
   smaller size (UIUX §2.3) and fits the compartment's existing min-height. */
#anchor-title .title-pinned { font-size: 15px; font-weight: 600; }
#anchor-title .title-date {
  font-size: 10px; font-weight: 400; line-height: 1.2;
  color: var(--ink);
}

/* The board-action row (UIUX §3.3, B83): the board-level actions as flat
   tabs above the Parking Lot, the mirror of the band's header tabs. They are
   the flat-tab species, not UIUX §14's tactile signature — none acts on a
   note, so they wear the sheet's furniture. --frame rotates with
   #board[data-cat] (UIUX §2.2.2, B67), so the row takes the board's hue.
   The container is a positioning frame only — pointer-events:none, exactly a
   .band-zone — so a press on bare canvas beside the tabs still reaches the
   recognizer; only the tabs (auto) are live. */
#board-actions {
  position: absolute;
  z-index: 1;                          /* furniture plane; notes (z-2) stay above */
  left: 0; right: 0;
  bottom: var(--lot-h, 122px);         /* flush on the lot's top edge, no gap (B87) */
  display: flex;
  justify-content: flex-start;         /* left-anchored, not centred (B88) */
  gap: 6px;                            /* the four-tab row's fit on a 390 phone (B95) */
  pointer-events: none;
}
/* Mobile only: at narrow embed widths the four-tab row wraps instead of
   clipping mid-button — same buttons, labels, order; desktop keeps one
   line. */
html:not(.wide) #board-actions {
  flex-wrap: wrap;
  row-gap: 4px;
}
.board-action {
  position: relative;                  /* the UIUX §6 hit collar hangs off this */
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 6px;                    /* vertical is the finger box (B86); sides trimmed for the four-tab row (B95) */
  border: none;
  border-radius: 3px;                  /* symmetric — it hangs beneath no rule */
  background: var(--frame);            /* the band tab's own fill (B76) */
  color: var(--ink);                   /* --ink-dark, rebound by .on-light on the button */
  font: inherit;
  font-size: 14px; font-weight: 600; line-height: 1.3;   /* finger size (B86) */
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
  transition: filter var(--leave-ms) var(--ease);
}
/* Drawn marks (UIUX §13.3): the GLYPH SVG in currentColor, sized to the tab's
   label rather than the menu's 16px type. */
.board-action .glyph { display: flex; }
.board-action .glyph svg { display: block; width: 16px; height: 16px; }   /* finger size (B86) */
/* Decoupled hit area (UIUX §6, B7): --hit is set on #board-actions in
   updateBoardGeometry (geometry.js) so hit x renderScale clears the floor,
   and the tab's own box never grows to meet it. Spent entirely upward, onto the
   canvas, because downward is the Parking Lot's own furniture; where a note
   (z-2) overlaps the collar the note wins. */
.board-action::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: 0;
  top: calc(-2 * var(--hit, 0px));
}
/* Interactive states without the tactile shadow: a transient brightness on
   hover/press (UIUX §8), and UIUX §2.7's ring on focus — state is never
   colour (UIUX §1). */
@media (hover: hover) {                /* bare :hover sticks after a touch */
  .board-action:hover { filter: brightness(1.1); }
}
.board-action:active { filter: brightness(0.92); }

/* ---- UIUX §3.2 / PRD §6.5 Parking Lot — a section, not a box -----------
   Full-bleed with its content on the gutter: the mirror of the band at the
   other end (B47). Height follows its measured contents from a two-row
   floor, set as --lot-h in lotH() (geometry.js, B73); bottom-anchored, so it
   grows upward. A ceiling of half the sheet stands so a runaway lot cannot
   swallow the canvas; content past it clips in #lot-items below. ---- */
#lot {
  position: absolute;
  z-index: 1;                          /* furniture plane; notes (z-2) stay above */
  left: 0; right: 0; bottom: 0;
  height: var(--lot-h, 122px);
  /* The lot takes the water too (UIUX §2.8, B58): the same fall and falloff
     as the band. Its lightest reach clears 3:1 on the deep, so this seam
     separates by fill and the rule above is a section mark (UIUX §2.5). */
  background:
    var(--water-falloff),
    linear-gradient(180deg, var(--water-top) 0%, var(--water-mid) 46%, var(--water-bot) 100%);
  background-size: 100% 100%;
}
/* The lot's own dither — the fall beneath it still ramps (UIUX §2.8). */
#lot::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0.05;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.86' numOctaves='4' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
}
#lot-rule {                            /* the same line at both ends of the
                                          sheet (B61): --frame, full width — a
                                          section mark here, since this seam
                                          separates by fill (UIUX §2.5) */
  position: absolute; left: 0; right: 0; top: 0;
  height: 1px; background: var(--frame);
  z-index: 1;
}
#lot-header {
  position: absolute; left: var(--gutter); top: 8px;
  font-size: 15px; font-weight: 600; color: var(--ink);
  z-index: 1;
}
#lot-items {
  position: absolute; left: var(--gutter); right: var(--gutter); top: 34px; bottom: 0;
  overflow: hidden;
  z-index: 1;
}
/* The All-Boards menu takes over the Parking Lot on mobile (UIUX §10, B74):
   the lot's own furniture steps aside while the water field beneath stays.
   #lot-items is only hidden; the lot's data is untouched. */
#lot.menu-open #lot-rule,
#lot.menu-open #lot-header,
#lot.menu-open #lot-items { display: none; }
/* Fills the whole lot as a 2x2 grid of category tiles, gutter-inset like the
   lot's own content. GRID_ORDER (state.js) lays the four buttons row-major. */
#lot-menu {
  position: absolute;
  left: var(--gutter); right: var(--gutter); top: 8px; bottom: 8px;
  z-index: 2;                          /* above the lot's own furniture plane */
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 8px;
}
/* Unframed text lines — the one place text is never framed (UIUX §4.4 / PRD §6.5). */
.lot-item {
  position: relative;
  min-height: var(--hit-floor);        /* full-width hit row (UIUX §6) */
  display: flex;
  align-items: center;
}
.lot-text {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  font-size: 16px; line-height: 1.45;
  color: var(--ink);
  caret-color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: break-word;
  outline: none;
  padding: 2px 0;
}
/* ============================================================================
   UIUX §4 The note component
   ========================================================================== */
.note {
  position: absolute;
  transform-origin: top left;          /* stored x,y stays truthful (UIUX §4.1) */
  z-index: 2;                          /* above anchors; DOM order = z-order */
  touch-action: none;
}
/* Decoupled hit collar to the 44px floor (UIUX §6, B7): --hit is set per note
   in JS so hit·scale·renderScale ≥ 44px physical. */
.note::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--hit, 0px));
}
.note-text {
  position: relative;
  z-index: 1;
  display: block;
  box-sizing: border-box;
  width: max-content;
  /* No predetermined width (B39): the var is set per note in JS — the distance
     from the note's left edge to the sheet's right edge. */
  max-width: var(--note-max-w, none);
  padding: 10px 12px;
  border: 2px solid var(--ink);        /* the only thing separating two
                                          overlapping notes (UIUX §2.5) */
  /* 3px holds the drawn reading against the 2px frame at every scale (B49);
     EXPORT_GEO.radius in export.js mirrors it by hand. */
  border-radius: 3px;
  background: var(--note);             /* the note takes its colour (B49) */
  color: var(--ink);
  caret-color: var(--ink);
  font-size: 17px; line-height: 1.4;
  /* Centred in its own frame (B62). Alignment moves glyphs, not the box: width
     stays max-content capped by --note-max-w, so B39's law holds. */
  text-align: center;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  outline: none;
}
/* Highlight (UIUX §2.6.1, B71): the surface takes the warm wash; border and ink
   stay var(--ink), so the toggle changes only the fill. */
.note.highlight .note-text {
  background: var(--highlight);
}
/* Frame draws itself on the first character; no empty frames ever (PRD §6.2) —
   the wash waits for the first character exactly as the default fill does. */
.note-text:empty,
.note.highlight .note-text:empty {
  border-color: transparent;
  background: transparent;
}

/* ---- UIUX §4 The note's Title tab (B120) --------------------------------
   A header-style tab peeking from behind the card's top-left corner. A child of
   the note, so it scales with the card's transform; .note-text paints at
   z-index 1 over this tab's z-index 0, so only the strip above the top edge
   shows. Rendered only when the record carries a title. */
.note-title {
  position: absolute;
  top: -19px;                          /* the peek above the card's top edge */
  left: -4px;                          /* juts past the corner — slid up from behind */
  z-index: 0;                          /* under the card body, over the hit collar */
  max-width: calc(100% + 4px);
  padding: 2px 10px;
  border: 2px solid var(--ink);        /* the card's own frame hand (UIUX §4.5) */
  border-bottom: 0;                    /* the tucked edge continues behind the card */
  border-radius: 3px 3px 0 0;
  background: var(--frame);
  color: var(--ink);
  font-size: 13px; font-weight: 600; line-height: 1.3;   /* the band-label tab's ink (UIUX §3.1) */
  white-space: nowrap;                 /* a single-line Title (B120) */
  overflow: hidden; text-overflow: ellipsis;
  text-align: left;
  outline: none;
}

/* ---- UIUX §4.5 The note's action toolbar (B84) --------------------------
   A note is always at least a toolbar wide. Floors only non-empty notes: an
   empty note keeps no frame (PRD §6.2) and shows no row, so it stays its true
   (zero) width. = NOTE_MIN_W in state.js — also the wrap-cap and drag/resize floor. */
.note-text:not(:empty) { min-width: 132px; }   /* = NOTE_MIN_W in state.js (UIUX §4.5) */

/* Four flat tabs on the note's bottom edge — Complete/Restore · Highlight ·
   Remind · Delete (B114, B126). A child of the note, so it scales with it and
   is never wider than the note. Styled as the band-label tab (UIUX §3.1):
   13px/600, 3px radius — not a pill. Drawn always; shown only on select/focus.
   Flush on the bottom edge, centred on the card (B120). */
.note-toolbar {
  position: absolute;
  top: 100%;                           /* flush on the note's bottom edge, no gap (B120) */
  left: 50%;                           /* centred on the card (B120) */
  transform: translateX(-50%);
  display: flex;
  gap: 3px;
  z-index: 3;                          /* above the note's text and scratch */
  opacity: 0; visibility: hidden;      /* shown by the select/focus rule below */
  transition: opacity var(--leave-ms) var(--ease), visibility 0s var(--leave-ms);
}
/* The row never sits inside the card (B139): with no room below,
   reflectToolbarSeat in render.js raises the note itself so the flush-below
   seat fits. */
/* An empty note shows no row (PRD §6.2). The row follows the text in DOM, so
   the general-sibling selector reaches it without :has(). */
.note-text:empty ~ .note-toolbar { display: none; }

.note-tb-btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 7px 6px;                    /* the band-label tab (UIUX §3.1), grown to finger size (B86) */
  border: 0; border-radius: 3px;
  background: var(--tab-complete);     /* the shared note rung; frame-family hue, rotates per type (UIUX §2.6.4, B67/B127) */
  color: var(--ink);                   /* --ink-dark, rebound by .on-light (B76) */
  font-size: 13px; font-weight: 600; line-height: 1.3;
  cursor: pointer;
}
/* Finger-size glyph: the mark grows from its intrinsic 16px (B86). */
.note-tb-btn svg { width: 18px; height: 18px; }
/* The destructive tab wears the warm hue as a fill, never as text (UIUX §2.6);
   its mark stays --ink-dark, so it differs by fill + glyph, not colour alone
   (UIUX §1). Delete last. */
.note-tb-delete { background: var(--tab-delete); }
/* Highlight wears its own identity fill: amber is button chrome (which action),
   never the note's state (UIUX §2.6.4). The highlight state stays the wash +
   the inset rule below. */
.note-tb-highlight { background: var(--tab-highlight); }
/* Highlight's "on" state is a geometry, not colour alone (UIUX §1): an inset
   border while the note wears the wash. The wash is the real signal; this
   labels the toggle, and updateNoteToolbar in render.js flips its aria-label. */
.note.highlight .note-tb-highlight { box-shadow: inset 0 0 0 2px var(--ink-dark); }
/* Decoupled hit collar (UIUX §6, B7): the tab keeps a 44px-tall tap target. It
   grows downward only — the row sits flush below the card, so an upward reach
   would land over the note's last text line (B139). The seat law (TB_ROW_H in
   state.js) reserves the collar inside the sheet. */
.note-tb-btn::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0; bottom: -12px;   /* 44px tap target under the tab (B86, B139) */
}
/* Shown by the same state that draws the resize frame, hidden while the note is
   picked up. Desktop selects; mobile engages by tap or by focus (an active note
   edits, a completed one takes frame focus — either way :focus-within). */
html.desktop .note.selected:not(.pressed) .note-toolbar,
html:not(.desktop) .note.engaged:not(.pressed) .note-toolbar,
html:not(.desktop) .note:focus-within:not(.pressed) .note-toolbar {
  opacity: 1; visibility: visible;
  transition: opacity var(--leave-ms) var(--ease), visibility 0s;
}
/* A selected note rises so its toolbar clears any note overlapping its bottom
   edge; a stacking change, not a write (B22, B90). */
html.desktop .note.selected { z-index: 3; }
html:not(.desktop) .note.engaged { z-index: 3; }   /* the engaged note rises without a write (B90) */

/* ---- UIUX §4.7 The reminder clock (B104/B109, B126) ---------------------
   The Remind tab: one tap sets or clears the reminder — no picker, no dialog,
   no time stored (B104). Resting it is a plain tab; active it takes the pink
   identity fill with no bloom — the glow is the card's, not the button's
   (B132). Metrics come from .note-tb-btn; only the identity state differs. */
.note.reminder .note-clock {
  background: var(--tab-remind);       /* the tab fill's own token (UIUX §2.6.4, B127) — fill only, never a glow */
  color: var(--ink-dark);
}

/* The card glow (UIUX §2.6.2, B132): a reminder-active card wears a soft bloom
   on its frame — the carried status's border + bloom grammar (UIUX §4.8) in the
   reminder's own quieter rung. The class is the record field's shadow, set at
   render by setReminderUi in render.js; a completed note never wears it. */
.note.reminder .note-text {
  border-color: var(--reminder-glow);
  box-shadow: 0 0 8px 0 var(--reminder-glow);   /* the soft bloom, one token */
}

/* The surfaced echo (B109): a reminder-active note's render-time reference on
   today's To Do. It keeps its source board's note hue — data-src-cat rebinds
   --note for the echo alone, the ladder's own discipline (UIUX §2.2.2); a
   To-Do source needs no rule (the default). Tap, drag, pinch and edit find no
   record here and stay inert; the clock and the menu write the source record
   through. The echo engages like no other note, so its row shows always. */
.note.surfaced .note-toolbar { opacity: 1; visibility: visible; transition: none; }
.note.surfaced[data-src-cat="idea"]     { --note: #b9d2b2; }
.note.surfaced[data-src-cat="unsorted"] { --note: #cec6ed; }
.note.surfaced[data-src-cat="learning"] { --note: #e6c2c9; }

/* ---- UIUX §4.8 The carried indicator (B104/B110) ------------------------
   A note carried onto its board today (`carriedOn` equals the today key, B108)
   wears its frame in --carried plus a soft bloom of the same token — never the
   whole-card fill and never the highlight layer (UIUX §2.6.3): the wash fills
   the text surface, the status dresses the frame, and a highlighted carried
   note wears both. The class is the record field's shadow, set at render by
   setCarriedUi in render.js; completing clears `carriedOn`, so a completed
   note never glows. The pressed rule below still owns a held card (UIUX §4.2). */
.note.carried .note-text {
  border-color: var(--carried);
  box-shadow: 0 0 8px 0 var(--carried);   /* the soft bloom, one token (B110) */
}

/* Pressed (drag/pinch engaged): border thickens to 3px, padding compensates so
   nothing reflows — the "picked up" confirmation (UIUX §4.2). */
.note.pressed .note-text {
  border-color: var(--ink);
  border-width: 3px;
  padding: 9px 11px;
}

/* ---- UIUX §4.3 The scratch-out (complete) ------------------------------ */
.note-scratch,
.lot-scratch {
  position: absolute;
  inset: 0;
  z-index: 2;                          /* above the opaque text so it truly destroys it */
  border-radius: 3px;                  /* tracks the note's (B49) */
  pointer-events: none;
  opacity: 0;                          /* active: invisible */
  background:
    repeating-linear-gradient(  8deg, var(--ink) 0 5px, transparent 5px 8px),
    repeating-linear-gradient(-14deg, var(--ink) 0 4px, transparent 4px 7px),
    repeating-linear-gradient( 79deg, var(--ink) 0 3px, transparent 3px 5px);
}
/* The veil and the burial are one decision — 0.62 over 0.12, never moved
   separately (UIUX §4.3, B53): a mark on every ground the note can land on, and
   the buried text a smudge that says something was here with the words gone. */
.note.complete .note-scratch,
.lot-item.complete .lot-scratch { opacity: 0.62; }
.note.complete .note-text,
.lot-item.complete .lot-text { color: rgb(var(--ink-a) / 0.12); }

/* ============================================================================
   UIUX §7 The menu (elevation = "temporary, above the page")
   ========================================================================== */
#menu {
  position: fixed;
  z-index: 1000;
  min-width: 184px;
  padding: 6px 0;
  background: var(--chrome);           /* summoned over the page, on the room's own ground (B52) */
  border-radius: 8px;
  overflow: hidden;                    /* clip the item rows to the rounded corners */
  box-shadow: var(--elevation);
  opacity: 0;
}
#menu.show { opacity: 1; }
#menu button {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 48px;                    /* UIUX §6 */
  padding: 0 16px;
  background: none;
  border: none;
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  text-align: left;
  cursor: pointer;
}
/* The marks are drawn, not typed (UIUX §13.3): inline SVG in currentColor at
   the note's own stroke weight and corner radius, from the GLYPH map in state.js. */
#menu .glyph { flex: 0 0 20px; display: flex; align-items: center; justify-content: center; }
#menu .glyph svg { display: block; }
#menu .danger { color: var(--danger); }      /* an accent as text, on chrome (UIUX §2.6) */
#menu .sep { height: 1px; margin: 6px 0; background: var(--hairline); }

/* ============================================================================
   UIUX §9 The undo toast
   ========================================================================== */
#toast {
  position: fixed;
  left: 50%;
  bottom: var(--toast-bottom, 12px);
  transform: translateX(-50%) translateY(8px);
  z-index: 1100;
  display: flex;
  align-items: center;
  gap: 16px;
  max-width: calc(100vw - 24px);
  padding: 8px 8px 8px 16px;
  background: var(--chrome);           /* the room's ground, under elevation (B52) */
  color: var(--ink);
  border-radius: 8px;
  box-shadow: var(--elevation);
  opacity: 0;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
#toast .msg { font-size: 15px; }
#toast button {                        /* Undo: distinguished by underline, not colour alone (UIUX §1) */
  min-width: 48px; min-height: 48px;
  padding: 0 12px;
  background: none; border: none;
  color: var(--accent-restore);        /* an accent as text, on chrome (UIUX §2.6) */
  font: inherit; font-size: 16px; font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

/* ============================================================================
   UIUX §10 Board list view (the only other screen)
   ========================================================================== */
/* The list pages its categories and never scrolls, so press-and-move is free
   to mean "drag this board to a category" and MOVE_THRESHOLD in state.js is
   the whole discriminator; touch-action: none is the board's own stance
   (B12, B44). */
#list-view {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--chrome);           /* the room's ground (B52) */
  overflow: hidden;
  touch-action: none;
  display: flex;
  flex-direction: column;
}
/* On mobile the drilled category is a panel rising from the Parking Lot to a
   third of the viewport, the board still visible above it (UIUX §10, B82). The
   height is measured in JS, never vh (B28/B32); the top edge is the lot's own
   --frame rule (B61). */
html:not(.desktop) #list-view {
  top: auto;                           /* releases inset:0's top edge */
  height: var(--list-panel-h, 280px);  /* = listPanelH() in geometry.js; px fallback, never vh */
  border-top: 1px solid var(--frame);
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  /* --elevation reflected upward: the panel is flush to the viewport floor, so
     the lift belongs on the top edge, the one seam that adjoins the board
     (UIUX §2.4). */
  box-shadow: var(--elevation-up);
  transform: translateY(100%);         /* parked below the fold */
  transition: transform var(--leave-ms) var(--ease);
}
html:not(.desktop) #list-view.show { transform: translateY(0); }
/* While the panel is risen the board behind it is context, not a capture
   surface (B81's stray-capture guard). Tied to .show, which drops the instant
   a dismissal starts, so the board is live again before the fall ends. */
html:not(.desktop):has(#list-view.show) #board { pointer-events: none; }
/* The primary species (UIUX §14, B59): the accent about boards behind
   --ink-dark, with the offset shadow and press-translate that make a control
   feel like a control. Worn by every category's New board control (.cat-add). */
.primary-btn {
  min-height: var(--hit-floor);        /* UIUX §6 */
  padding: 0 18px;
  background: var(--accent-page);
  color: var(--ink-dark);
  border: 2px solid var(--ink-dark);
  border-radius: 0.4em;
  box-shadow: 0.1em 0.1em var(--ink-dark);
  font: inherit;
  font-size: 16px; font-weight: 800;
  cursor: pointer;
}
/* Guarded: a bare hover sticks after a touch. */
@media (hover: hover) {
  .primary-btn:hover {
    transform: translate(-0.05em, -0.05em);
    box-shadow: 0.15em 0.15em var(--ink-dark);
  }
}
.primary-btn:active {
  transform: translate(0.05em, 0.05em);
  box-shadow: 0.05em 0.05em var(--ink-dark);
}
#list-rows {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;                    /* categories page; nothing scrolls (UIUX §10) */
  display: flex;
  flex-direction: column;
  gap: 8px;                            /* = CAT_SEC_GAP in state.js */
  /* One gutter on four sides: with no page heading (B66) this is all that
     stands above the first head row. A constant, not env(safe-area-inset-top)
     (B66). */
  padding: 12px;
}
/* The All-Boards picker: four category tiles as a 2x2, the same shape and
   order as the mobile lot-grid, so the two surfaces read as one menu
   (UIUX §10, B74). */
#list-view.picker #list-rows {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 12px;
}
/* A category tile: the family's tray (B72) enlarged into a button and centred
   on its name. Ground and inset frame are the family's rungs, rebound per
   data-cat (UIUX §2.2.2). Shared by the desktop picker and the lot-grid
   (#lot-menu). */
.cat-button {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: var(--hit-floor);        /* the touch floor (UIUX §6) */
  padding: 12px;
  background: linear-gradient(180deg, var(--water-top), var(--water-mid));  /* the family's water field, as its list cards (B89) */
  outline: 2px solid var(--frame);     /* the family's inset frame (B72) */
  outline-offset: -2px;
  border: 0;
  border-radius: 6px;
  color: var(--ink);                   /* the ground's pole (UIUX §2.3) */
  font: inherit;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.cat-button:active { background: var(--frame); color: var(--ink-dark); }
/* A board card is a small rendering of what it names (UIUX §10): the field's
   upper fall as its fill, edged with the chrome's hairline. It can be carried
   to another category, so the edge, not the height, makes it a discrete
   object. */
.board-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  width: 100%;
  min-height: var(--hit-floor);        /* = HIT_FLOOR in state.js (UIUX §6, B68) */
  padding: 8px 16px;
  background: linear-gradient(180deg, var(--water-top), var(--water-mid));
  border: 1px solid var(--hairline);
  border-radius: 3px;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.board-row-wrap { display: flex; align-items: stretch; flex: 0 0 auto; }
.board-row .row-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 17px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;             /* truncation always indicated (UIUX §10) */
}
/* Untitled and dated take the ground's own pole (UIUX §2.3). */
.board-row .row-title.untitled { color: var(--ink); }
.board-row .row-date {
  flex: 0 0 auto;
  font-size: 13px;
  color: var(--ink);
}
/* The mobile drilled card is the panel's denser skin (B82): three to a row
   (.cat-cards), a two-line title, the date line pinned bottom-right. Taller
   than UIUX §6's floor; catPageCap() in boards.js budgets the panel against
   it. */
html:not(.desktop) .board-row {
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
  height: 76px;                        /* = LIST_CARD_H in state.js */
  padding: 6px 10px;
}
html:not(.desktop) .board-row .row-title {
  flex: 0 0 auto;
  font-size: 14px;
  line-height: 1.25;
  white-space: normal;                 /* wrap, then clamp */
  display: -webkit-box;                /* -webkit-line-clamp needs -webkit-box */
  -webkit-line-clamp: 2;               /* two lines, then "…" (UIUX §10, B82) */
  -webkit-box-orient: vertical;
  overflow: hidden;
}
html:not(.desktop) .board-row .row-date {
  margin-top: auto;                    /* pinned to the card's bottom edge */
  align-self: flex-end;                /* bottom-right (B82) */
  font-size: 10px;
  line-height: 1.2;
  text-align: right;
}
/* On mobile the linked board's cal date leaves the clamped title and rides the
   .row-date line, where the Last Updated stamp does not render (B82, B140). On
   desktop and tablet the title carries the colon-joined pair and the stamp
   stands. */
html:not(.desktop) .board-row .row-title-date { display: none; }
.row-board-date { display: none; }
html:not(.desktop) .board-row .row-board-date { display: inline; }
html:not(.desktop) .board-row .row-last-updated { display: none; }

/* ---- UIUX §10 Categories -- Four categories — To Do, Notes, Learning, Ideas —
   on both the mobile list and the desktop rail, from one DOM shape: head, add,
   cards, pager. Overflow turns pages, never scrolls (B42, B44, B63, B68).
   ---- */
/* Each section is a framed, tinted tray in its own family (B72): --card as
   the ground, --frame as an inset 2px frame, both rotated rungs (UIUX §2.2.2);
   --chrome stays the one room behind, showing through padding and gaps (B55).
   The frame is inset because the surface clips and an outset outline loses its
   side edges. Horizontal breathing room lives on .cat-cards, never here:
   padding on .board-cat would move the New board control off the right edge. */
.board-cat {
  flex: 1 1 0;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: var(--hit-floor) 1fr var(--hit-floor); /* = LIST_CAT_ROW in state.js, twice; catPageCap() spends them */
  grid-template-areas: "head add" "cards cards" "pager pager";
  align-items: center;
  column-gap: 8px;                     /* the label never touches the control */
  background: var(--card);             /* the family's tray ground (B72) */
  outline: 2px solid var(--frame);     /* the family's inset frame (B72) */
  outline-offset: -2px;
  border-radius: 6px;
}
/* An empty category collapses to its head row, exactly what catPageCap()
   reclaims; the label and its New board control survive, so it is still a
   place to create in and a target to drop onto (B68). */
.board-cat.empty {
  flex: 0 0 auto;
  grid-template-rows: var(--hit-floor) 0 0; /* = LIST_CAT_ROW; the head row alone */
}
/* The tablet rail's one-open accordion folds a section to its head row — the
   same reclaim as the empty collapse, so .folded rides the same two rules
   (B137). The head row is the toggle (.cat-fold); desktop and mobile never
   wear it. */
.board-cat.folded {
  flex: 0 0 auto;
  grid-template-rows: var(--hit-floor) 0 0; /* = LIST_CAT_ROW; the head row alone */
}
.cat-fold { cursor: pointer; }
/* Mid-drag landing frame: the section under the pointer is where the board
   will move on release. Inset because the section clips — an outset outline
   loses its side edges and reads as two stray rules. */
.board-cat.drop-target { outline: 2px solid var(--accent-page); outline-offset: -2px; }
.cat-head {
  grid-area: head;
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding-inline-start: 16px;          /* clears the tray's inset frame; aligns the
                                          label with the card titles' left edge (B129) */
  min-height: var(--hit-floor);        /* one box with the control beside it (B63) */
  font-size: 21px;                     /* the display step, sized so the longest
                                          category name keeps its whole self beside
                                          the control (UIUX §13.1, B63/B74) */
  font-weight: 600;
  line-height: 1;                      /* the row, not the type, sets the box */
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--ink);                   /* the ground's pole (UIUX §2.3) */
}
.cat-head span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;             /* truncation always indicated (UIUX §10) */
  white-space: nowrap;
}
/* The per-category create control (B63): the primary species sized to the
   head row it shares with the label; its label steps to 14px (UIUX §13.1).
   position: relative because the row has no slack (B68): the offset shadow and
   UIUX §2.7's focus ring fall on the cards track, and a positioned control
   paints above its in-flow siblings so the ring stays whole. */
.cat-add {
  grid-area: add;
  position: relative;
  min-height: var(--hit-floor);
  padding: 0 12px;
  font-size: 14px;
}
/* Three cards to a row on the phone (B82): a board's name does not need the
   sheet's whole width, so the horizontal axis buys back the density the
   third-of-viewport panel gives up. The rail keeps one column at PANE_W 300. */
.cat-cards {
  grid-area: cards;
  align-self: stretch;
  flex: 1 1 auto;                      /* desktop restacks the section as a column */
  min-height: 0;
  overflow: hidden;                    /* pagination, never a scrollbar */
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));   /* = LIST_CARD_COLS in state.js */
  align-content: start;                /* rows keep their height; the slack stays at the bottom */
  gap: 4px;                            /* = PANE_ROW_GAP in state.js */
  /* Clears the cards off the tray's inset frame (B72). Horizontal only: a
     padding-block here would shrink the cards track invisibly to catPageCap()
     and clip a row. */
  padding-inline: 6px;
}
html.wide .cat-cards { grid-template-columns: minmax(0, 1fr); }
/* The pager: the n/m indicator sits between ‹ and › because it says which page
   the arrows are on, and one page hides the whole strip (UIUX §10: no state,
   no statement). Centred on its own row below the cards (B63). */
.cat-pager {
  grid-area: pager;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0;
}
/* Adjacent buttons share their border, so the strip reads as one segmented
   control; -2px because the UIUX §14 construction draws a 2px ink border. */
.pager-btn + .pager-btn { margin-left: -2px; }
.cat-pages {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);                   /* the ground's pole (UIUX §2.3) */
  padding: 0 6px;
  font-variant-numeric: tabular-nums;
}
/* The pager button (UIUX §14): --accent-page carrying --ink-dark; disabled
   says so at opacity, never with a mid-grey (UIUX §2.3). */
.pager-btn {
  min-width: var(--hit-floor);         /* = HIT_FLOOR in state.js (PRD §5.3, UIUX §6) */
  min-height: var(--hit-floor);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-page);
  color: var(--ink-dark);
  border: 2px solid var(--ink-dark);
  border-radius: 0.4em;
  box-shadow: 0.1em 0.1em var(--ink-dark);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}
.pager-btn svg { display: block; }
.pager-btn:disabled { opacity: 0.4; cursor: default; }
/* Mid-drag: the origin row dims but holds its slot; a fixed clone of the card
   rides the pointer. pointer-events:none keeps the recognizer's hit-test seeing
   the categories under the pointer, never the ghost. */
.board-row-wrap.card-dragging,
.pane-row.card-dragging { opacity: 0.55; }
.card-drag-ghost {
  position: fixed;
  z-index: 1200;                       /* above everything: it belongs to the pointer */
  pointer-events: none;
  opacity: 0.92;
  box-shadow: var(--elevation);
}

/* ============================================================================
   UIUX §10 Desktop mode (html.wide). Everything here is gated on the class the
   JS mode flag owns; the media query is never restated.
   ========================================================================== */

/* ---- Furniture geometry: desktop keeps fixed px because its LOGICAL_W is
   derived per layout (B20), so the mobile percentages would drift with the
   window shape. The band's two inputs are all it restates (B32, B35). ---- */
html.wide #board { --gutter: 24px; --card-w: 340px; }

/* ---- The board rail (UIUX §10, B24): full-height, sunken into the sheet —
   embedded, not floating. Unscaled chrome: the sheet starts PANE_W to its
   right (--offx). ---- */
#pane { display: none; }
html.wide #pane {
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 300px;                        /* = PANE_W in state.js */
  padding: 16px 12px 10px;
  background: var(--chrome);           /* the room beside the page (B52) */
  border-right: 1px solid var(--hairline);
  box-shadow: var(--elevation-inset);  /* sunken, not floating (UIUX §2.4) */
  z-index: 400;                        /* under #list-view (500) and #menu (1000) */
}
#pane-cards {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;                    /* categories page; nothing scrolls (UIUX §10) */
  display: flex;
  flex-direction: column;
  gap: 8px;                            /* = CAT_SEC_GAP in state.js */
}

/* ---- The collapsible All-Boards rail (B118): on wide the rail boots
   collapsed to the calendar rail's 40px face — one rail-width law, two rails
   (B99). Collapsed face = #pane-rail; expanded = #pane-collapse + #pane-cards.
   The frame re-reads the rail width via setPaneCollapsed in geometry.js. */
html.wide #pane.rail-open {
  width: var(--cal-rail-w, 40px);
  padding: 0;
  align-items: stretch;
  justify-content: center;
  background: var(--card);             /* the collapsed face's own paper (B99) */
  border-right: 2px solid var(--frame); /* the calendar rail's line, mirrored; follows the board's ladder (UIUX §2.2.2, B99) */
  box-shadow: none;
}
html.wide #pane.rail-open #pane-cards,
html.wide #pane.rail-open #pane-collapse { display: none; }
#pane-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;                           /* the calendar rail's dot→title rhythm (B99) */
  width: 100%;
  height: 100%;
  border: none;
  background: transparent;
  color: var(--ink-light);
  font: inherit;
  cursor: pointer;
}
/* The rails' vertical label — one species on both rails. */
#pane-rail .vlabel,
#cal-rail .vlabel {
  writing-mode: vertical-rl;
  font-size: 12px; font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink-light);
}
#pane-rail:hover { filter: brightness(1.1); }
/* The pane's Go Back control sits at the pane's bottom-right corner; it
   renders last in #pane, after #pane-cards (B134, B151). */
#pane-collapse { align-self: flex-end; }

/* ---- Rail categories: the desktop skin of the shared UIUX §10 grid — same
   children and areas, only the row heights and the control step down for a
   pointer. The fixed heights pair with state.js (PANE_CAT_HEAD, PANE_PAGER_H);
   catPageCap in boards.js derives the card budget from them (B68). ---- */
html.wide .board-cat {
  grid-template-rows: 32px 1fr 32px;   /* = PANE_CAT_HEAD / PANE_PAGER_H in state.js */
}
html.wide .board-cat.empty {
  grid-template-rows: 32px 0 0;        /* = PANE_CAT_HEAD; the head row alone (B68) */
}
html.wide .board-cat.folded {
  grid-template-rows: 32px 0 0;        /* = PANE_CAT_HEAD; the accordion's folded section (B137) */
}
/* The rail's header and control step down so the longest category name is
   never crowded off its own row (UIUX §10, B63/B74). */
html.wide .cat-head { min-height: 32px; font-size: 15px; }
html.wide .cat-add {
  min-height: 32px;
  padding: 0 10px;
  font-size: 13px;
}
html.wide .cat-pager { gap: 8px; }
html.wide .cat-pages { font-size: 11px; letter-spacing: 0; padding: 0 2px; }
/* Above the 24px desktop hit floor (UIUX §6, B23); a fingertip's 44px would
   crowd the rail, and the rail has room to space them. */
html.wide .pager-btn { min-width: 36px; min-height: 28px; }
html.wide .pager-btn + .pager-btn { margin-left: 0; }

.pane-row {
  display: flex;
  align-items: stretch;
  flex: 0 0 auto;
}
/* A rail card is a small rendering of what it names (UIUX §10): the field's
   own upper fall as its fill behind the hairline edge; the active board
   holds the full-ink border. */
.pane-card {
  position: relative;                  /* anchors the calendar mark (below) */
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;              /* the drilled card's skin (B82): title
                                          leads, the stamp drops to a bottom-
                                          right line so it cannot starve the
                                          title */
  align-items: stretch;
  gap: 2px;
  height: 76px;                        /* = LIST_CARD_H in state.js; catPageCap
                                          budgets the rail against it */
  padding: 6px 12px 6px 16px;          /* room on the left before the title */
  background: linear-gradient(180deg, var(--water-top), var(--water-mid));
  border: 1px solid var(--hairline);
  border-radius: 3px;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
/* Structural active mark: ink weight, not color alone; padding compensates. */
.pane-card.active { border: 2px solid var(--ink); padding: 5px 11px 5px 15px; cursor: default; }
/* The drilled card's two-line title over a bottom-right stamp (B82).
   Truncation always indicated (UIUX §10). */
.pane-card .row-title {
  flex: 0 0 auto;
  min-width: 0;
  font-size: 15px;
  line-height: 1.25;
  white-space: normal;
  display: -webkit-box;
  -webkit-line-clamp: 2;               /* two lines, then "…"; needs -webkit-box */
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pane-card .row-title.untitled { color: var(--ink); }
.pane-card .row-date {
  margin-top: auto;                    /* pushed to the card's bottom edge */
  align-self: flex-end;                /* bottom-right (B82) */
  font-size: 10px;
  line-height: 1.2;
  text-align: right;
  color: var(--ink);
}
/* Delete is permanent on the active card only, a sibling at the card's right
   edge, never nested. Bare on chrome with --danger as its mark: an accent as
   text on the chrome ground (UIUX §2.6). */
.pane-del {
  flex: 0 0 var(--hit-floor);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: -1px;                   /* shares the hairline seam with the card */
  background: var(--chrome);
  border: 1px solid var(--hairline);
  border-radius: 0 3px 3px 0;
  color: var(--danger);
  font: inherit;
  font-size: 15px;
  cursor: pointer;
}
.pane-del svg { display: block; }

/* ---- Note links (UIUX §4.6, B91): one SVG layer in board space, a sibling of
   the notes, so endpoints are board-logical px. pointer-events:none, or a line
   would steal a note's tap from the recognizer's hit-test. z-index:1 sits
   below the z:2 notes and, appended after the furniture, above it — fractional
   z-index is invalid. 1px --frame follows the board type (B67); kept crisp at
   any scale by non-scaling-stroke. ---- */
#link-layer {
  position: absolute; left: 0; top: 0;
  width: var(--logical-w, 900px); height: var(--logical-h, 1000px);
  pointer-events: none; z-index: 1; overflow: visible;
}
#link-layer line {
  stroke: var(--frame); stroke-width: 1px; vector-effect: non-scaling-stroke;
}

/* ---- Selection chrome (B22): one overlay in board space. The container
   passes events through; only the ring bands and handles are interactive, so
   the note's interior stays clickable for a double-click's second click.
   Inherits renderScale, never note.scale. ---- */
#selection { position: absolute; z-index: 3; pointer-events: none; }
#selection.hidden { display: none; }
/* The ring wears the two-tone construction (UIUX §2.7) — no single hue works
   on every ground — and its radius is the note's 3 plus one outside (B49). */
#selection .sel-ring {
  position: absolute;
  inset: -4px;
  border: 2px solid var(--ink-light);
  border-radius: 4px;
  box-shadow: 0 0 0 2px var(--ink-dark);
  pointer-events: none;                /* visual only — bands below take the hits */
}
/* The frame is the resize grip: four thin bands along the edges, so the
   note's interior stays clickable. */
#selection .sel-edge { position: absolute; pointer-events: auto; cursor: nwse-resize; }
#selection .sel-edge.n { left: -6px; right: -6px; top: -8px;    height: 10px; }
#selection .sel-edge.s { left: -6px; right: -6px; bottom: -8px; height: 10px; }
#selection .sel-edge.w { top: -6px; bottom: -6px; left: -8px;   width: 10px; }
#selection .sel-edge.e { top: -6px; bottom: -6px; right: -8px;  width: 10px; }
#selection .sel-handle {
  position: absolute;
  width: 8px; height: 8px;
  background: var(--ink-light);        /* the ring's own two tones (UIUX §2.7) */
  border: 2px solid var(--ink-dark);
  pointer-events: auto;
  cursor: nwse-resize;
}
#selection .sel-handle.tl { left: -8px;  top: -8px; }
#selection .sel-handle.tr { right: -8px; top: -8px; }
#selection .sel-handle.bl { left: -8px;  bottom: -8px; }
#selection .sel-handle.br { right: -8px; bottom: -8px; }
/* The lot-row actions (UIUX §14, B25): accent fills under the ink border and
   the shared tactile signature. Copy takes plain chrome because it changes
   nothing — accents mark state changes (UIUX §2.6). */
.sel-btn {
  min-height: 36px;
  padding: 0 14px;
  border: 2px solid var(--ink-dark);
  border-radius: 0.4em;
  box-shadow: 0.1em 0.1em var(--ink-dark);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-dark);
  cursor: pointer;
}
.sel-btn.sel-complete { background: var(--accent-restore); }
.sel-btn.sel-copy     { background: var(--chrome); color: var(--ink-light); }
.sel-btn.sel-delete   { background: var(--danger); }

/* ---- Multi-selection (B41): the primary keeps the full #selection chrome;
   every other member wears an outline ring on the node itself, so drags and
   resizes track with no JS positioning. With two or more selected the overlay
   drops its resize grip: resize is single-selection only. ---- */
html.desktop .note.multi-selected .note-text {
  outline: 2px solid var(--ink-light);           /* the two-tone ring (UIUX §2.7) */
  outline-offset: 3px;
  box-shadow: 0 0 0 7px var(--ink-dark);
}
#selection.multi .sel-edge,
#selection.multi .sel-handle { display: none; }

/* ---- Lot rows on desktop (B25): the selected row draws a frame — a narrow
   override of "lot lines are never framed" (UIUX §4.4). Outline, not border:
   rows have no padding, so a border would shift the text; in ink, not the
   focus ring, since selection is not edit (UIUX §2.7). ---- */
html.desktop .lot-item.selected { outline: 2px solid var(--ink); outline-offset: 2px; }
.lot-actions {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  gap: 6px;
  z-index: 3;                          /* above the scratch so Restore stays clickable */
}
.lot-actions .sel-btn { min-height: 32px; padding: 0 10px; font-size: 13px; }

[hidden] { display: none !important; }

/* ============================================================================
   UIUX §8 Motion — the complete justified set; everything else is instant.
   One long deceleration, a water-like settle (UIUX §1.1). Paired constants:
   LEAVE_MS, SWAP_MS, TOAST_HIDE_MS in state.js (PRD §9.5).
   ========================================================================== */
#menu            { transition: opacity var(--leave-ms) var(--ease); }
.note-scratch,
.lot-scratch     { transition: opacity var(--leave-ms) var(--ease); }
#toast           { transition: opacity var(--leave-ms) var(--ease),
                               transform var(--leave-ms) var(--ease); }
.note.leaving,
.lot-item.leaving,
.board-row-wrap.leaving,
.pane-row.leaving  { transition: opacity var(--leave-ms) var(--ease); opacity: 0; }
/* Board swap: a crossfade, desktop only, sequenced by timeout in JS so
   reduced-motion degrades cleanly to an instant swap (UIUX §8). */
html.desktop #board { transition: opacity var(--swap-ms) var(--ease); }
#board.swapping     { opacity: 0; }

/* --- UIUX §3.4 The calendar view ------------------------------------------
   The third screen, on the room's own ground (B52): full-screen on phone
   (html:not(.wide)), the right-side panel on wide. The view is one fixed,
   bounded page; only #cal-stack scrolls (B161). */
#cal-view {
  position: fixed;
  inset: 0;
  z-index: 400;                        /* above the board's furniture, below the menu (500) */
  background: var(--chrome);
  display: flex;
  flex-direction: column;
  padding: 10px 12px 12px;
}
#cal-top {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;                    /* the exit row renders last, below the month view (B134) */
}
.cal-act {
  position: relative;                  /* the UIUX §6 hit collar hangs off this (B98) */
  display: inline-flex;
  flex-direction: row-reverse;         /* inert with the glyph span empty (B151); kept so no other surface shifts */
  align-items: center;
  gap: 8px;
  padding: 14px 16px;                  /* the visual frame clears the 44px floor as drawn (UIUX §6, B98) */
  border: none;
  border-radius: 3px;
  background: var(--frame);
  color: var(--ink);
  font: inherit;
  font-size: 15px; font-weight: 600; line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  transition: filter var(--leave-ms) var(--ease);
}
.cal-act .glyph { display: flex; }
.cal-act .glyph svg { display: block; width: 22px; height: 22px; }  /* legible at rest (B98) */
/* Decoupled hit area (UIUX §6, B7): --hit is set on #cal-top in renderCal
   (boards.js). The collar tops up the width where the row is tight and spends
   downward into the view's bottom padding, never upward into the month view. */
.cal-act::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: 0;
  bottom: calc(-2 * var(--hit, 0px));
}
/* Hover and press without the tactile shadow (B98); the UIUX §2.7 ring is
   grouped at the end of the file. Hover is gated so it cannot stick after a
   touch. */
@media (hover: hover) {
  .cal-act:hover { filter: brightness(1.1); }
}
.cal-act:active { filter: brightness(0.92); }
#cal-back  { margin-right: auto; }     /* anchored left (B134) */
#cal-today { margin-left: auto; }      /* anchored right (B148) */
#cal-stack {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 7px;
  min-height: 0;
  /* The stack alone scrolls, vertically only — the exception to the
     one-bounded-page law (UIUX §3.4, B161); band, Today body, month view
     and exit row stay fixed outside it. */
  overflow-y: auto;
  overflow-x: hidden;
  /* The scroll container spans the view's full width and restores the inset
     itself, so the weekend cards' margin: 0 -12px reaches the screen edge
     without the overflow clip cutting the fill short. */
  margin: 0 -12px;
  padding: 0 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--frame) transparent;
}
#cal-stack::-webkit-scrollbar { width: 6px; }
#cal-stack::-webkit-scrollbar-track { background: transparent; }
#cal-stack::-webkit-scrollbar-thumb {
  background: var(--frame);
  border-radius: 3px;
}
/* The face flows from the top of the view: band, Today body, stack, month
   view, exit row; the slack falls above the exit row (B153). */
#cal-stack { gap: 10px; }
/* The face's header is the boards' top band (UIUX §3.1) drawn in the
   calendar's own ladder, with no section zones and no label (B157): the bar
   over the water fall, the 1px rule at its bottom edge, and the date's title
   card centred on the rule, overhanging it by 22px (B38). The card is sized
   by the type it holds (B37) and is inert furniture; the band's one target
   is today's "+" at the bar's right (B158). */
#cal-band {
  flex: 0 0 auto;
  position: relative;
  height: 61px;                        /* --rule-y's two-line floor (UIUX §3.1) */
  margin: -10px -12px 0;               /* the view's own padding, spent — the bar runs edge to edge */
  background:
    var(--water-falloff),
    linear-gradient(180deg, var(--water-top) 0%, var(--water-mid) 46%, var(--water-bot) 100%);
}
#cal-view.panel #cal-band { margin: -12px -12px 0; }   /* the panel's 12px padding, spent the same way */
#cal-band-rule {                       /* same weight and idiom as #band-rule (B61) */
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--frame);
  z-index: 1;
}
#cal-band-card {
  position: absolute;
  top: 0; left: 50%; transform: translateX(-50%);
  width: max-content;
  max-width: calc(100% - 24px);
  min-height: calc(61px + 22px);       /* the 22px overhang past the rule (UIUX §3.1, B38) */
  display: flex;
  align-items: center; justify-content: center;
  padding: 20px 12px 12px;             /* --band-top + 6 / 12 / 12 — the boards' own paddings */
  border: 2px solid var(--frame);
  border-top: 0;                       /* three-sided: the view's top edge is the fourth */
  border-radius: 0 0 3px 3px;          /* only the corners that exist */
  background: var(--card);             /* the ladder's card rung (UIUX §2.2.2, B117) */
  z-index: 2;                          /* over the rule — the card occludes it */
  pointer-events: none;                /* not a target; the recognizer sees the ground */
}
#cal-band-add {                        /* today's adder, centred in the band's right-of-card section (B158) */
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);    /* the box's own centre lands on the section's centre */
  left: var(--cal-add-x, 75%);         /* measured per paint in boards.js — the card's width is max-content */
  z-index: 3;                          /* above the card — it sits beside it, never under it */
}
/* The Today board body (B158): today's card leaves the stack and becomes the
   full-width body directly below the band, in the title card's own fill, so
   band, card and body read as one little board. It stays seated whichever
   week the stack shows (B166). */
#cal-today-board {
  flex: 0 0 auto;
  position: relative;
  margin: 0 -12px 0;                   /* the view's side padding, spent; the card's overhang lands on the body */
  padding: 22px 6px 4px 6px;           /* the top clears the card's 22px overhang */
  background: var(--card);             /* the title card's own fill (B158) */
  border-bottom: 2px solid var(--frame); /* the title card's border weight closes the body (B167) */
  border-radius: 0 0 4px 4px;          /* only the corners that exist below */
}
#cal-view.panel #cal-today-board { margin: 0 -12px 0; }   /* the panel's 12px padding, spent the same way */
/* The body's grid is capped to the weekly card's arithmetic (84px compartment
   + 12px zone padding + 26px add slot) so today's squares are the weekly
   squares' own size, filling from the left (B158). */
#cal-today-board .cal-notes-grid { max-width: calc(100% - 122px); }
/* The body's fill must not reach the stack: the 22px clearance stays. */
#cal-stack { margin-top: 22px; }
/* The day head is the band card's title (B157): weekday, then month-day with
   the ordinal as superscript (B149), in the boards' own title-card type
   (UIUX §3.1) on one line — the card scales to it. */
#cal-dayhead {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);              /* --ink-light under #cal-view's .on-dark — the approved pair on the card rung (UIUX §2.3) */
  font-size: 15px; font-weight: 600; line-height: 1.3;   /* the boards' title-card type (UIUX §3.1) */
  white-space: nowrap;            /* one line, always — the card scales to it */
  text-align: center;
}
/* The day-note squares live inside the day cards (B153): each card's zone is
   one row of up to three squares and a ‹/› pager beyond three, in the .mo-nav
   grammar; the card's "+" sits under the date (B165). Today's row renders in
   the body below the band instead (B158). */
.cal-notes {
  position: relative;
  padding: 4px 6px;
  display: flex;
  align-items: stretch;
  gap: 6px;
  min-height: var(--hit-floor);         /* the row clears the touch floor even when empty (UIUX §6) */
}
.cal-notes-row {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: stretch;
  gap: 6px;
}
.cal-notes-nav {
  flex: 0 0 auto;
  align-self: center;
  min-width: 24px; min-height: 24px;    /* the .mo-nav pager grammar, on the card */
  padding: 0;
}
/* The squares centre vertically in the row (B154). Never `stretch`: it would
   recompute the square's width from the row height and shove the pager and
   "+" off the row. */
.cal-notes-grid {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));   /* three squares (B165) */
  gap: 6px;
  align-items: center;                   /* centred, not seated high (B154) */
}
.cal-notes-grid .cal-note { min-width: 0; }   /* a square never shoves the pager or the "+" off the row */

.cal-note {
  aspect-ratio: 1 / 1;              /* the block is square (UIUX §3.5) */
  outline: 2px solid var(--frame);      /* the family's inset frame (B72) — the fill must not erase it */
  outline-offset: -2px;
  background: transparent;              /* the view's ground shows through */
  color: var(--ink);
  border-radius: 4px;
  overflow: hidden;                     /* the fill clips to the 4px radius */
  display: flex; flex-direction: column;
  gap: 0;                               /* the fill meets flush inside the frame — no gutter of the view's ground */
  padding: 0;
}
/* One zone (B154): the fill is the whole body, in the Calendar ladder's note
   rung — the four boards' note-card fills rotated into the calendar's hue
   (UIUX §2.2.2, B158) — with the note rung's own ink pair (UIUX §2.3). */
.cal-note-text {
  flex: 1 1 auto;                       /* the fill reaches the frame's bottom edge */
  background: var(--note);              /* the calendar's note rung (B158) */
  color: var(--ink-dark);               /* the note rung's approved pair (UIUX §2.3) */
  font-size: 12px; font-weight: 400; line-height: 1.4;   /* plain text (B154) */
  padding: 4px 6px;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;   /* the clamp needs -webkit-box */
  overflow: hidden; overflow-wrap: break-word;   /* two lines read, a third never draws */
}
.cal-note-text[contenteditable] {
  outline: none;
  display: block; -webkit-line-clamp: unset;   /* the editor grows freely; the clamp is for the read, not the write */
  white-space: pre-wrap;
}
.cal-day {
  position: relative;
  border-radius: 4px;
  overflow: hidden;
  flex: 0 0 auto;                      /* the card is its content's size, not the viewport's */
  min-height: var(--hit-floor);
  display: grid;
  grid-template-columns: 84px 1fr;     /* date compartment left, one events zone */
}
.cal-day::before {                     /* the water fill, per UIUX §2.8's fall */
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, var(--water-top) 0%, var(--water-mid) 46%, var(--water-bot) 100%);
}
.cal-date {
  position: relative;
  background: var(--card);
  border-right: 1px solid var(--hairline);   /* the token (UIUX §2.5) — rebinding rides .on-dark */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 4px 2px;
}
.cal-d1 { font-size: 12px; font-weight: 600; color: var(--ink-light); line-height: 1.2; }
.cal-d2 { font-size: 9px; font-weight: 600; letter-spacing: .08em;
  color: var(--ink-light); opacity: .55; text-transform: uppercase; }
/* Every "+" is a bounded button: a visible border draws the click space
   around the glyph, centred in its box and in its row (B158). */
.cal-add {
  align-self: center;
  border: 2px solid var(--frame);       /* the bounded click space (B158) */
  border-radius: 3px;
  width: 28px; height: 28px;            /* one box, glyph centred in it */
  display: flex; align-items: center; justify-content: center;
  background: transparent;
  color: var(--ink-light); opacity: .55;
  font: inherit; font-size: 14px; font-weight: 600; line-height: 1;
  padding: 0;
  cursor: pointer;
  transition: filter var(--leave-ms) var(--ease);
}
/* The day card's "+" seats under the day/date in the compartment's own
   centred column (B165); the band's "+" keeps its --cal-add-x measurement. */
.cal-date .cal-add { margin-top: 4px; }
.cal-add:hover, .cal-add:focus-visible { opacity: 1; }
/* Attention recedes with distance from now: today lit, the rest receded by
   scale and luminance, never icons (UIUX §3.4). */
.cal-day.today { flex: 1.6 1 0; max-height: calc(21.05% - 9px); outline: 2px solid var(--ink-light); outline-offset: 1px;
  box-shadow: 0 0 0 3px var(--ink-dark); }   /* the cap is today's 1.6/7.6 flex share of the stack minus the gaps */
.cal-day.today .cal-d1 { font-size: 14px; }
.cal-day.near { opacity: .88; }
.cal-day.near::before { filter: brightness(.8); }
.cal-day.near .cal-date { background: #050b18; }   /* --deep lightened for near (B175) */
.cal-day.far  { opacity: .74; }
.cal-day.far::before { filter: brightness(.6); }
.cal-day.far .cal-date { background: #030813; }    /* --deep darkened for far (B175) */
/* The weekend cards keep the Calendar ladder, with the title card's deep fill
   as one flat fill edge to edge — the view's side padding spent, the date
   compartment's own fill dropped, the 1px divider kept (B160). Seated after
   .near/.far so their dark compartment overrides stand down; their brightness
   filters still apply to the ::before. Today is excluded: it renders in the
   body above (B158). */
.cal-day.weekend { margin-left: -12px; margin-right: -12px; }
.cal-day.weekend::before { background: var(--card); }
.cal-day.weekend .cal-date { background: transparent; }
/* A Sunday closes each visual week in the rolling stack (B168). */
.cal-day.sunday { border-bottom: 2px solid var(--frame); }

/* --- UIUX §3.4 The month view --------------------------------------------
   The compact month grid below the day stack: display plus the
   tap-to-week-swap, palette only — the current-week band is the ink-light
   wash at reading alpha, today wears the water fall, and no day wears a dot
   (B179). */
#cal-month {
  flex: 0 0 auto;
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.mo-head { display: flex; align-items: center; gap: 4px; }
.mo-nav {
  border: none; border-radius: 3px;
  background: transparent;
  color: var(--ink-light); opacity: .7;
  font: inherit; font-size: 18px; font-weight: 600; line-height: 1;
  min-width: 36px; min-height: 32px;
  cursor: pointer;
  transition: filter var(--leave-ms) var(--ease);
}
@media (hover: hover) { .mo-nav:hover { opacity: 1; } }
.mo-nav:active { filter: brightness(0.92); }
.mo-label { flex: 1; text-align: center; font-size: 12px; font-weight: 600; color: var(--ink-light); }
.mo-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  row-gap: 1px;
}
.mo-wd {
  text-align: center;
  font-size: 9px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-light); opacity: .55;
  padding-bottom: 2px;
}
.mo-cell {
  position: relative;
  border: none;
  background: transparent;
  color: var(--ink-light);
  font: inherit; font-size: 11px;
  min-height: 24px;
  padding: 0 2px 6px;
  cursor: pointer;
}
@media (hover: hover) { .mo-cell:hover { background: rgb(var(--ink-a) / .14); } }
.mo-cell:focus-visible {
  outline: 2px solid var(--ink-light);
  outline-offset: -2px;
}
/* The pale band across the current week's row: one class per cell, and no
   column gap, so the fills read as one continuous highlight. */
.mo-cell.wk { background: rgb(var(--ink-a) / .08); }
/* Today wears the ladder's water fall. */
.mo-cell.today {
  background: linear-gradient(180deg, var(--water-top) 0%, var(--water-mid) 46%, var(--water-bot) 100%);
  border-radius: 3px;
  font-weight: 600;
}

/* Wide: the calendar docks to the right edge as a real column and the board
   reflows beside it — applyLayout (geometry.js) narrows the board (B95). */
#cal-view.panel {
  left: auto;
  width: 320px;                        /* = CAL_PANEL_W in geometry.js */
  border-left: 2px solid var(--frame);
  padding: 12px;
}

/* --- UIUX §3.4 The standing calendar rail (B99) ---------------------------
   On wide the calendar is standing furniture: a slim rail pinned to the
   viewport's right edge, always visible, reading "Calendar Board" vertically
   with today's date and a dim dot. One tap expands it into the panel and the
   board reflows beside it; expand/collapse is in-room navigation (B81).
   Collapsed face = #cal-rail; expanded face = the band, body, stack, month
   view and exit row. Both rail faces are display:none on mobile, where the
   full-screen tab-driven view is the calendar (B95). */
#cal-view.rail-open {
  left: auto;
  width: var(--cal-rail-w, 40px);
  border-left: 2px solid var(--frame);
  padding: 0;
  justify-content: center;
  background: var(--card);             /* the collapsed rail's own paper */
  box-shadow: none;
}
#cal-view.rail-open.panel { width: auto; }   /* expanded wins over the collapsed width */
/* The collapsed rail shows only the rail face: every piece of the expanded
   face hides with it (B128). */
#cal-view.rail-open #cal-band,
#cal-view.rail-open #cal-top,
#cal-view.rail-open #cal-stack,
#cal-view.rail-open #cal-today-board,
#cal-view.rail-open #cal-month { display: none; }
#cal-rail {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 100%;
  padding: 12px 0;
  border: none;
  background: transparent;
  color: var(--ink-light);
  font: inherit;
  cursor: pointer;
}
#cal-rail .cdot, #pane-rail .cdot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--highlight);        /* the aperture: today is lit in the rail too */
  opacity: .35;                        /* dim; never lit (B179) */
}
#cal-rail .cdate {
  writing-mode: vertical-rl;
  font-size: 9px; font-weight: 600;
  letter-spacing: .1em;
  color: var(--ink-light);
  opacity: .55;
}
#cal-rail:hover { filter: brightness(1.1); }
/* Wide (desktop ∪ tablet) has no All-Boards tab: the left board rail (B24)
   is the one All-Boards surface there (B100, B119). Mobile keeps the tab as
   its only way into the lot-grid picker (B74). */
html.wide #action-boards { display: none; }
html.wide #action-calendar { display: none; }   /* the rail replaces the tab (B99) */
html.wide:not(.has-cal-rail) #cal-view { display: none; }
html.wide #board.swapping { transition: opacity var(--swap-ms) var(--ease); }

.row-cal-mark { display: flex; }
.row-cal-mark svg { width: 14px; height: 14px; display: block; }
html.wide .pane-card .row-cal-mark svg { width: 12px; height: 12px; }
/* On the rail card the mark is a third flex child the column skin would wrap
   onto its own line — anchored to the card's top-right corner instead, out of
   the bottom-right stamp's way (B129). */
.pane-card > .row-cal-mark { position: absolute; top: 8px; right: 12px; }

/* ============================================================================
   UIUX §2.7 Focus — the two-tone ring, written once per variant. No single
   hue reads on every ground; --ink-light outside --ink-dark clears 3:1 on
   all of them by construction. Grouped LAST in the file so each ring wins
   its cascade ties — the primary controls' :active shadow precedes it.
   ========================================================================== */
/* The ring, offset outward: editing surfaces (:focus) and the controls that
   stand clear of their neighbours (:focus-visible). */
.anchor:focus,
.lot-text:focus,
.note-text:focus,
.note-title:focus,
.board-action:focus-visible,
.note-tb-btn:focus-visible,
#toast button:focus-visible,
.cat-button:focus-visible,
#pane-rail:focus-visible,
.cal-act:focus-visible,
.mo-nav:focus-visible,
#cal-rail:focus-visible {
  outline: 2px solid var(--ink-light);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--ink-dark);
}
/* The inset ring: rows and cards whose neighbours sit flush, so the ring is
   drawn inside the box (UIUX §2.7, "-2px on inset rows"). */
#menu button:focus-visible,
.board-row:focus-visible,
.pane-card:focus-visible,
.pane-del:focus-visible {
  outline: 2px solid var(--ink-light);
  outline-offset: -2px;
  box-shadow: inset 0 0 0 4px var(--ink-dark);
}
/* The primary ring: the ring plus the control's own offset shadow (UIUX §14's
   tactile signature), so focus never erases the press-shadow. */
.primary-btn:focus-visible,
.pager-btn:focus-visible,
.sel-btn:focus-visible {
  outline: 2px solid var(--ink-light);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--ink-dark), 0.1em 0.1em var(--ink-dark);
}

/* Mandatory global kill-switch (UIUX §8). */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
