  /* Tokens shared with the public site. --urgent is the one addition: red
     means overdue or imminent here and nothing else. */
  :root {
    color-scheme: light dark;
    --bg: #ffffff; --fg: #000000;
    --muted: #5f5f5f; --faint: #767676;
    --rule: #c8c8c8; --rule-strong: #000000;
    --link: #0000ee; --visited: #551a8b;
    --accent: #1a3a1a; --accent-link: #14532d;
    --field: #ffffff; --urgent: #a4161a; --tint: #f3f0e8;
    --scrim: rgba(0,0,0,.34);
    --cat-class: #000000; --cat-work: #1a3a1a; --cat-eco: #7a6a3f; --cat-personal: var(--faint);
    --serif: "Iowan Old Style", "Charter", "Bitstream Charter", "Sitka Text", Georgia, "Times New Roman", Times, serif;
    --mono: ui-monospace, "Cascadia Mono", "Segoe UI Mono", "SF Mono", Menlo, Consolas, "Courier New", monospace;
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --bg: #0f0f0f; --fg: #e9e6e0;
      --muted: #a6a29b; --faint: #8e8a84;
      --rule: #333330; --rule-strong: #6b6b66;
      --link: #8ab4f8; --visited: #c9a4e8;
      --accent: #9ec49e; --accent-link: #c6e5c6;
      --field: #171716; --urgent: #f08a8a; --tint: #1c1c19;
      --scrim: rgba(0,0,0,.62);
      --cat-class: #e9e6e0; --cat-work: #9ec49e; --cat-eco: #d3b872; --cat-personal: var(--faint);
    }
  }
  :root[data-theme="dark"] {
    --bg: #0f0f0f; --fg: #e9e6e0;
    --muted: #a6a29b; --faint: #8e8a84;
    --rule: #333330; --rule-strong: #6b6b66;
    --link: #8ab4f8; --visited: #c9a4e8;
    --accent: #9ec49e; --accent-link: #c6e5c6;
    --field: #171716; --urgent: #f08a8a; --tint: #1c1c19;
    --scrim: rgba(0,0,0,.62);
    --cat-class: #e9e6e0; --cat-work: #9ec49e; --cat-eco: #d3b872; --cat-personal: var(--faint);
  }
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }

  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    background: var(--bg); color: var(--fg);
    font-family: var(--serif); font-size: 17px; line-height: 1.6;
    max-width: 1180px; margin: 0 auto; padding: 30px 26px 92px;
  }
  a { color: var(--link); text-decoration: underline; }
  a:hover { color: var(--accent-link); }
  a:visited { color: var(--visited); }
  :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
  .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
  button.linkish {
    appearance: none; background: none; border: none; cursor: pointer; padding: 0;
    font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
    color: var(--accent); border-bottom: 1px solid var(--rule);
  }
  button.linkish:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  button.linkish:disabled { color: var(--faint); cursor: default; }

  .nameplate { text-align: center; padding-bottom: 4px; }
  .nameplate .name { font-size: clamp(30px, 6.6vw, 56px); font-weight: bold; letter-spacing: 0.055em; line-height: 1.02; }
  .nameplate .motto { font-family: var(--mono); font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--faint); margin-top: 8px; }

  .dayclock { position: relative; height: 3px; background: var(--rule); margin-top: 10px; overflow: hidden; }
  .dayclock-fill { position: absolute; top: 0; bottom: 0; left: 0; width: 0; background: var(--rule-strong); }
  .dayclock-noon { position: absolute; top: 0; bottom: 0; left: 50%; width: 1px; background: var(--bg); }
  .dayclock-hair { height: 1px; background: var(--rule-strong); margin-top: 2px; }

  .dateline {
    display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline;
    gap: 4px 20px; padding: 6px 0;
    font-family: var(--mono); font-variant-numeric: tabular-nums;
    font-size: 11px; letter-spacing: 0.05em; color: var(--faint);
  }
  .dateline .right { display: flex; flex-wrap: wrap; gap: 4px 16px; }
  .dateline .ok { color: var(--accent); }
  .dateline .bad { color: var(--urgent); }
  .dateline .wait { color: var(--faint); }
  .themebtn {
    appearance: none; background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; letter-spacing: inherit; color: var(--faint);
    text-transform: uppercase; border-bottom: 1px solid transparent;
  }
  .themebtn:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  .themebtn b { font-weight: normal; color: var(--muted); }

  /* ---- The Morgue: the pull button and the state lines (spec-dateline A, B).
     A 2px rule, then one line per state: the word, the sentence, at most one
     action. Red only where the reader must act now; the pull button never
     changes colour without changing its word. */
  .pullbtn[aria-busy="true"] { color: var(--faint); cursor: progress; }
  .pullbtn.failed { color: var(--urgent); }
  .pullbtn:disabled { color: var(--faint); cursor: default; border-bottom-color: transparent; }
  .stalebar { display: none; margin-top: 8px; border-top: 2px solid var(--rule-strong); font-family: var(--mono); font-size: 12px; letter-spacing: 0.05em; color: var(--fg); }
  .stalebar.on { display: block; }
  .notice { display: grid; grid-template-columns: 7.5em minmax(0,1fr) auto; gap: 2px 14px; align-items: baseline; padding: 7px 0; border-bottom: 1px solid var(--rule); }
  .notice .word { letter-spacing: 0.13em; text-transform: uppercase; white-space: nowrap; }
  .notice .text { color: var(--muted); }
  .notice.act { color: var(--urgent); }
  .notice.act .text { color: var(--urgent); }
  .notice .do { white-space: nowrap; }
  /* The editions picker, accepted verbatim from spec-dateline.html:151-155.
     A select shaped like the theme word: no box, no fill, one caret. */
  .pick { position: relative; display: inline-block; }
  .pick select { appearance: none; -webkit-appearance: none; background: none; border: none; padding: 0 1.1em 0 0; cursor: pointer; font: inherit; letter-spacing: inherit; text-transform: uppercase; color: var(--faint); border-bottom: 1px solid transparent; }
  .pick select:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  .pick select:disabled { cursor: default; }
  .pick::after { content: "\25BE"; position: absolute; right: 0; top: 0; pointer-events: none; color: var(--faint); }

  /* The rail (P3.3): the tablist scrolls sideways when six tabs will not fit,
     and the search word holds the right end without scrolling away with them.
     It is outside the tablist because a non-tab inside one is wrong ARIA. */
  .rail { display: flex; align-items: baseline; gap: 18px; border-top: 1px solid var(--rule-strong); border-bottom: 1px solid var(--rule-strong); }
  .rail-inner { flex: 1 1 auto; min-width: 0; display: flex; gap: 30px; white-space: nowrap; padding: 0 1px; overflow-x: auto; scrollbar-width: none; }
  .rail-inner::-webkit-scrollbar { display: none; }
  #search-open { flex: 0 0 auto; white-space: nowrap; }
  .tab {
    appearance: none; background: none; border: none; cursor: pointer;
    font-family: var(--mono); font-size: 12px; letter-spacing: 0.13em; text-transform: uppercase;
    color: var(--muted); padding: 9px 0 8px; margin-bottom: -1px; border-bottom: 3px solid transparent;
  }
  .tab:hover { color: var(--fg); }
  .tab[aria-selected="true"] { color: var(--fg); border-bottom-color: var(--accent); }
  .panel { display: none; }
  .panel.on { display: block; }

  .now { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 8px 30px; align-items: center; padding: 16px 0; border-bottom: 1px solid var(--rule); }
  .kicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint); margin-bottom: 5px; }
  .now-event { font-size: clamp(21px, 2.6vw, 28px); font-weight: bold; line-height: 1.18; }
  .now-meta { font-size: 15px; color: var(--muted); margin-top: 3px; }
  .now-count { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: clamp(26px, 3.6vw, 36px); line-height: 1; white-space: nowrap; text-align: right; }
  .now-count.soon { color: var(--urgent); }
  .now-count .unit { font-size: 14px; color: var(--muted); }
  .flags { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 4px 24px; font-size: 15px; }
  .flag { color: var(--urgent); }
  /* The index on page one (Hairline, proposal 1): section names as links, figures in ink, never red. */
  .inside { grid-column: 1 / -1; font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); }
  .inside b { font-weight: normal; color: var(--muted); font-variant-numeric: tabular-nums; }

  .spread { display: grid; grid-template-columns: minmax(0,1.6fr) 1px minmax(0,1fr); gap: 0 30px; align-items: start; }
  .colrule { background: var(--rule); align-self: stretch; min-height: 100%; }
  .well, .aside-col { min-width: 0; }

  .section { padding-top: 20px; }
  .section:first-child { padding-top: 14px; }
  .section-head { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; border-bottom: 1px solid var(--rule-strong); padding-bottom: 4px; margin-bottom: 3px; }
  .section-head h2 { font-family: var(--mono); font-size: 11px; font-weight: normal; letter-spacing: 0.13em; text-transform: uppercase; color: var(--accent); }
  /* display: block is load-bearing: the sheet's own .note rows are a grid
     (line below in the sheet block), and a section head's note is a span with
     the same class, so without this every note in every head is folded into a
     7.5em first column. */
  .section-head .note { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); display: block; }

  .brief { padding: 14px 0 4px; }
  .brief p { max-width: 36em; margin-bottom: 11px; }
  .brief p:first-child { font-size: 19px; line-height: 1.5; }
  .brief p:first-child::first-letter { float: left; font-size: 3.1em; line-height: 0.8; padding: 3px 7px 0 0; font-weight: bold; color: var(--accent); }

  .ag-row { display: grid; grid-template-columns: 132px minmax(0,1fr) auto; gap: 3px 14px; align-items: baseline; padding: 8px 0; border-bottom: 1px solid var(--rule); line-height: 1.35; }
  .ag-time { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }
  .ag-what { font-size: 16px; }
  .ag-where { font-size: 13px; color: var(--faint); margin-top: 2px; }
  .ag-dur { font-family: var(--mono); font-size: 11px; color: var(--faint); display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-top: 4px; }
  .ag-row.past .ag-time, .ag-row.past .ag-what, .ag-row.past .ag-where { color: var(--faint); }
  .ag-row.soft .ag-what { color: var(--muted); }
  /* Fifteen-minute blocks (Clock Tower, Z5). The squares are the graphic and
     the figure beside them is the word, so nobody has to count: PressTime
     writes both. An hour tick every fourth square, never on the last. */
  .blocks { display: flex; flex-wrap: wrap; gap: 2px; max-width: 74px; justify-content: flex-end; }
  .blk { display: block; width: 5px; height: 9px; background: var(--rule-strong); }
  .blk.hr { margin-right: 4px; }
  .blocks-fig { font-variant-numeric: tabular-nums; }
  .ag-row.past .blk { opacity: 0.4; }
  /* A free window is the same square, unfilled (Q3). The gap row is an agenda
     row with nothing booked in it, so it takes the agenda grid and says its
     length in words; once the window has passed it is not news any more. */
  .blk.free { background: transparent; border: 1px solid var(--rule-strong); }
  .ag-gap .ag-what { font-size: 14px; color: var(--faint); }
  .ag-gap.past { display: none; }
  .tag { font-family: var(--mono); font-size: 9px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); border: 1px solid var(--rule); padding: 1px 4px; margin-left: 7px; vertical-align: 1px; }
  .now-rule { display: grid; grid-template-columns: 132px minmax(0,1fr); gap: 14px; align-items: center; padding: 1px 0; }
  .now-rule .t { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--urgent); text-align: right; }
  .now-rule .line { height: 2px; background: var(--urgent); }

  /* ---- The Clock Tower (Quarter, Q2 and Q3): the week as rows, the day as
     columns. This is the one place on the page where hue carries meaning, and
     every hue carries its word in the legend and inside every bar's label.
     Position is the time of day, width or height is the duration. Today is
     weight plus the word "today", never red: red is overdue or imminent, and
     a Tuesday is neither. The only red here is the 2px edge on a clash, which
     is the bar outlining itself and not a rail on a row (contract 2.2), and
     the word "clash" always sits beside it. GRID_START and GRID_END live at
     the top of press-time.js; anything outside 7am to 10pm is clipped with a
     marker and a word, never dropped. */
  .wk { margin-top: 10px; }
  .wk-ruler { display: grid; grid-template-columns: 84px minmax(0,1fr) 64px; gap: 12px; align-items: center; padding-bottom: 4px; }
  .wk-row { display: grid; grid-template-columns: 84px minmax(0,1fr) 64px; gap: 12px; align-items: center; padding: 5px 0; border-bottom: 1px solid var(--rule); }
  .wk-ruler .wk-track { position: relative; height: 12px; }
  .wk-ruler span { position: absolute; font-family: var(--mono); font-size: 9px; color: var(--faint); transform: translateX(-50%); }
  .wk-day { font-family: var(--mono); font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .wk-day b { font-weight: normal; }
  .wk-day a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); }
  .wk-day a:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  .wk-day .word { display: block; font-size: 9px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--fg); }
  .wk-row.today .wk-day { color: var(--fg); }
  .wk-row.today .wk-day b { font-weight: bold; }
  .wk-row.today .wk-track { border-bottom: 1px solid var(--rule-strong); }
  .wk-track { position: relative; height: 16px; }
  .wk-n { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--muted); text-align: right; white-space: nowrap; }
  .wk-empty { font-family: var(--mono); font-size: 10px; color: var(--faint); letter-spacing: 0.05em; }
  .bar { position: absolute; top: 2px; height: 12px; appearance: none; border: none; padding: 0; cursor: pointer; background: var(--cat-personal); }
  .bar.cat-class { background: var(--cat-class); }
  .bar.cat-work { background: var(--cat-work); }
  .bar.cat-eco { background: var(--cat-eco); }
  .bar.cat-personal { background: var(--cat-personal); }
  .bar.soft { background: transparent; border: 1px solid var(--cat-eco); }
  .bar.soft.cat-personal { border-color: var(--cat-personal); }
  .bar.clash { border-top: 2px solid var(--urgent); border-bottom: 2px solid var(--urgent); }
  /* The cut end of a clipped bar, in the page's own ground: a notch, not a
     rail, so the no-rail rule holds and the bar reads as unfinished. */
  .bar.clipped { box-shadow: inset -3px 0 0 var(--bg); }
  .bar.allday { left: 0; width: 100%; top: 0; height: 2px; background: var(--rule-strong); cursor: default; }
  .now-tick { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--rule-strong); }
  .legend { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
  .legend .dot { display: inline-block; width: 7px; height: 7px; margin-right: 5px; vertical-align: 1px; background: var(--cat-personal); }
  .legend .dot.cat-class { background: var(--cat-class); }
  .legend .dot.cat-work { background: var(--cat-work); }
  .legend .dot.cat-eco { background: var(--cat-eco); }
  .legend .dot.cat-soft { background: transparent; border: 1px solid var(--rule-strong); }
  .legend .dot.cat-clash { background: transparent; border-top: 2px solid var(--urgent); border-bottom: 2px solid var(--urgent); }
  .legend .dot.cat-allday { background: var(--rule-strong); height: 2px; vertical-align: 3px; }

  /* The calendar columns. One quarter hour is --q, so an hour is 40px and a
     rectangle's height is honest to the minute: an 80-minute class is 53px,
     not rounded up to an hour. Hour hairlines, no boxes. */
  .cal { --q: 10px; display: grid; grid-template-columns: 52px repeat(7, minmax(0,1fr)); margin-top: 12px; }
  .cal-axis { position: relative; font-family: var(--mono); font-size: 9px; color: var(--faint); text-align: right; padding-right: 8px; }
  .cal-dayhead { font-family: var(--mono); font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; padding: 0 6px 6px; border-left: 1px solid var(--rule); }
  .cal-dayhead b { font-weight: normal; }
  .cal-dayhead a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--rule); }
  .cal-dayhead a:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  .cal-dayhead.today { color: var(--fg); }
  .cal-dayhead.today b { font-weight: bold; }
  .cal-dayhead .word { display: block; font-size: 9px; letter-spacing: 0.09em; text-transform: uppercase; }
  .cal-ad { min-height: 22px; padding: 2px 4px; border-left: 1px solid var(--rule); border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule-strong); }
  .cal-ad-axis { padding-top: 4px; border-bottom: 1px solid var(--rule-strong); }
  .cal-ad button { display: block; width: 100%; text-align: left; appearance: none; border: none; cursor: pointer; padding: 1px 4px; font-family: var(--mono); font-size: 9px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--bg); background: var(--cat-personal); }
  .cal-ad button.cat-class { background: var(--cat-class); }
  .cal-ad button.cat-work { background: var(--cat-work); }
  .cal-ad button.cat-eco { background: var(--cat-eco); }
  .cal-hours { height: calc(var(--q) * 60); }
  .cal-hours span { position: absolute; right: 8px; transform: translateY(-50%); }
  /* 7 AM has no hairline above it to sit on: the top of the grid is the
     line, so the first label hangs from it instead of straddling the
     all-day strip. The now figure is appended last and is never first. */
  .cal-hours span:first-child { transform: none; }
  .cal-hours .now-t { color: var(--fg); font-weight: bold; }
  .cal-col { position: relative; height: calc(var(--q) * 60); border-left: 1px solid var(--rule); }
  .cal-col.today { background: var(--tint); }
  .cal-line { position: absolute; left: 0; right: 0; height: 1px; background: var(--rule); }
  .cal-now { position: absolute; left: 0; right: 0; height: 1px; background: var(--rule-strong); }
  .cal-ev { position: absolute; left: 0; width: 100%; appearance: none; border: none; padding: 2px 4px; text-align: left; overflow: hidden; cursor: pointer; font-family: var(--serif); line-height: 1.2; color: var(--bg); background: var(--cat-personal); }
  .cal-ev.cat-class { background: var(--cat-class); }
  .cal-ev.cat-work { background: var(--cat-work); }
  .cal-ev.cat-eco { background: var(--cat-eco); }
  .cal-ev.soft { background: transparent; color: var(--fg); border: 1px solid var(--cat-eco); }
  .cal-ev.clash { border-top: 2px solid var(--urgent); border-bottom: 2px solid var(--urgent); }
  .cal-ev.clipped { border-bottom: 2px dotted var(--bg); }
  .cal-ev.lane-a { width: 50%; }
  .cal-ev.lane-b { left: 50%; width: 50%; }
  .cal-ev .t { display: block; font-family: var(--mono); font-size: 9px; font-variant-numeric: tabular-nums; letter-spacing: 0.03em; }
  .cal-ev .w { display: block; font-size: 12px; }
  .cal-ev .k { display: block; font-family: var(--mono); font-size: 8px; letter-spacing: 0.09em; text-transform: uppercase; }
  .cal-ev.soft .t, .cal-ev.soft .k { color: var(--muted); }
  .cal-ev.tight { padding: 1px 4px; white-space: nowrap; }
  .cal-ev.tight .t, .cal-ev.tight .w, .cal-ev.tight .k { display: inline; font-size: 9px; }
  .cal-ev.tight .w { font-family: var(--mono); }
  /* The clash word inside a filled rectangle cannot be red and legible at
     once, so it is underlined in the ground colour; on an outlined optional
     rectangle it takes the red it earns. */
  .cal-ev .k.clashword { text-decoration: underline; }
  .cal-ev.soft .k.clashword { color: var(--urgent); }


  /* ---- City Desk: the briefing line on the Wire and the cut-list word in the
     note. Done rows are faint through .row.done, the one row grammar (P3.1). */
  .brief-line { font-size: 15px; max-width: 36em; padding: 10px 0 6px; border-bottom: 1px solid var(--rule); }
  .brief-line:empty { display: none; }
  .brief-line .kicker { margin-bottom: 2px; }
  .section-head .note .linkish { margin-left: 8px; }

  .tally-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--rule); font-size: 14px; }
  .tally-row .n { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }

  .capture { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20; background: var(--bg); border-top: 1px solid var(--rule-strong); }
  .capture form { max-width: 1180px; margin: 0 auto; padding: 9px 26px; display: flex; align-items: baseline; gap: 12px; }
  .capture .caret { font-family: var(--mono); font-size: 14px; color: var(--accent); }
  .capture input { flex: 1; min-width: 0; font-family: var(--serif); font-size: 16px; background: transparent; color: var(--fg); border: none; padding: 3px 0; }
  .capture input::placeholder { color: var(--faint); }
  .capture .hint { font-family: var(--mono); font-size: 10px; letter-spacing: 0.08em; color: var(--faint); white-space: nowrap; }

  .search input {
    width: 100%; font-family: var(--serif); font-size: 16px; padding: 7px 0;
    background: transparent; color: var(--fg);
    border: none; border-bottom: 1px solid var(--rule-strong);
  }
  .search input:focus { outline: none; border-bottom-color: var(--accent); }

  .foot { margin-top: 30px; padding-top: 11px; border-top: 1px solid var(--rule); display: flex; flex-wrap: wrap; gap: 3px 20px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); }

  .gate { max-width: 30em; margin: 60px auto 0; text-align: center; }
  .gate p { margin: 14px 0; }

  /* ---- The Composing Room (Hairline): the entity row (contract 4.1, spec-hairline A).
     One wrapper, one click surface, one action span; a kind modifier on .row-open
     changes only the columns. No renderer emits it until rowHtml lands (P3.1). */
  .row { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 0 14px; align-items: baseline; border-bottom: 1px solid var(--rule); }
  .row-open {
    display: grid; grid-template-columns: minmax(0,15em) minmax(0,1fr) auto; gap: 4px 14px; align-items: baseline;
    width: 100%; min-width: 0; text-align: left; appearance: none; background: none; border: none;
    font: inherit; color: inherit; padding: 9px 0; line-height: 1.35;
  }
  /* Only a row that opens something takes the pointer and the tint: rowHtml
     draws a span until the row has an entity and openEntity exists. */
  button.row-open { cursor: pointer; }
  button.row-open:hover { background: var(--tint); }
  .row-name { font-weight: bold; font-size: 15px; }
  .row-org { color: var(--muted); font-size: 13px; margin-left: 5px; font-weight: normal; }
  .row-ask { font-size: 14px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .row-meta { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--faint); white-space: nowrap; text-align: right; }
  .row-meta.hot { color: var(--urgent); }
  .row-st { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); white-space: nowrap; }
  .row-st.ok { color: var(--accent); }
  .row-notes { font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
  .row-notes.none { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; }
  .row-act { display: flex; gap: 14px; padding: 9px 0; white-space: nowrap; }
  a.linkish { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--rule); }
  a.linkish:hover { color: var(--fg); border-bottom-color: var(--rule-strong); }
  .row.p1 .row-ask { color: var(--fg); }
  .row.done .row-open, .row.done .row-meta { color: var(--faint); }
  .row.done .row-name { font-weight: normal; }
  .row-open.ag { grid-template-columns: 132px minmax(0,1fr) auto; }
  .row-open.ag .row-time { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }
  .row-open.ag .row-name { font-weight: normal; font-size: 16px; }
  .row-open.ag .row-where { display: block; font-size: 13px; color: var(--faint); margin-top: 2px; }
  .row.past .row-open, .row.past .row-time, .row.past .row-where { color: var(--faint); }
  .row-open.app { grid-template-columns: minmax(0,15em) minmax(0,1fr) auto auto; }
  .row-open.task { grid-template-columns: minmax(0,1fr) auto auto; }
  /* The Assignments desk: who a task is on and what it takes, each on its own
     full-width line under the title, so the three columns above stay the
     title, the due word and the status. A resource is the reader's own words
     in a box, never a category. */
  .row-who, .row-res { grid-column: 1 / -1; min-width: 0; }
  .row-who { font-size: 13px; color: var(--muted); }
  .row-who .none { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--faint); }
  .row-res { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 3px; }
  .res { font-family: var(--mono); font-size: 10px; color: var(--faint); border: 1px solid var(--rule); padding: 1px 5px; }
  /* A name that filters reads as the name, not as a link: the underline and
     the caps of .linkish would shout on every row of the roster. */
  button.linkish.plain { font-family: inherit; font-size: inherit; letter-spacing: 0; text-transform: none; color: inherit; border-bottom: 1px solid transparent; }
  button.linkish.plain:hover { border-bottom-color: var(--rule-strong); }
  /* The team inside a club, indented under it; the picked row is the one the
     list is showing, marked by weight and a rule rather than a colour. */
  .tally-row.sub { padding-left: 14px; }
  .tally-row.sub button.linkish.plain { color: var(--muted); }
  .tally-row.picked, .row.picked { border-bottom-color: var(--rule-strong); }
  .tally-row.picked button.linkish.plain { font-weight: bold; border-bottom-color: var(--rule-strong); }
  .row.picked .row-name { text-decoration: underline; }
  .row-open.cap { grid-template-columns: minmax(0,1fr) auto; }
  .row-open.ed { grid-template-columns: 132px minmax(0,1fr) auto; }
  .row-open.ed .row-time { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); }
  .fold[hidden] { display: none; }
  /* The honest line (contract 5.2, part 7): what a list says when it has
     nothing to show, what a cut list says is still in the sheet, what the
     sheet says it does not know. One class, every panel, never coloured. */
  .state { font-size: 13px; color: var(--faint); font-style: italic; padding: 9px 0; }
  .state:first-child { padding-top: 11px; }

  /* ---- The Composing Room (Hairline): the sheet (contract 5.2, spec-hairline C).
     One dialog for every kind: a 1px box on a scrim, filled only by openSheet()
     from pass 3. The bordered words are scoped to the sheet so the row's own
     act span, which is mono and unboxed, keeps its shape. */
  dialog { padding: 0; border: none; background: none; max-width: none; max-height: none; }
  dialog::backdrop { background: var(--scrim); }
  .sheet { background: var(--bg); color: var(--fg); border: 1px solid var(--rule-strong); width: min(92vw, 620px); max-height: 82vh; overflow: auto; padding: 22px 24px 24px; }
  .sheet-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; border-bottom: 1px solid var(--rule-strong); padding-bottom: 8px; }
  .sheet h3 { font-size: 22px; line-height: 1.2; font-weight: bold; }
  .sheet .sub { font-size: 14px; color: var(--muted); margin-top: 2px; }
  .closex { appearance: none; background: none; border: none; cursor: pointer; font-family: var(--mono); font-size: 16px; color: var(--faint); padding: 6px; margin: -6px; }
  .closex:hover { color: var(--fg); }
  .sheet dl { margin-top: 12px; display: grid; grid-template-columns: max-content minmax(0,1fr); gap: 4px 16px; align-items: baseline; }
  .sheet dt { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); }
  .sheet dd { font-size: 15px; line-height: 1.45; max-width: 58ch; }
  .sheet dd .hot { color: var(--urgent); }
  .sheet .acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--rule); }
  .sheet .act { font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; border: 1px solid var(--rule-strong); padding: 7px 11px; text-decoration: none; color: var(--fg); background: var(--bg); cursor: pointer; }
  .sheet .act:hover { background: var(--fg); color: var(--bg); }
  .sheet .act:visited { color: var(--fg); }

  /* ---- The Rolodex Room (Overlay, R8): the inside of the sheet
     (spec-overlay B and F). Hairline's block above is the box; these are the
     parts a body puts in it: the label over a list, the Appears in line, and
     notes as dated lines. Nothing here is bordered on the left or the right
     and nothing carries colour except the one red word an Appears in line
     spends on a date that is here or past. */
  .lbl { display: block; font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); margin-top: 16px; }
  /* City Desk: the one line the application sheet writes (C14). A field with
     a rule under it and the word beside it, which is the same shape as the
     capture bar: nothing is boxed and nothing is a card. */
  .noteform { display: flex; gap: 10px; align-items: baseline; margin-top: 4px; }
  .noteform input { flex: 1; min-width: 0; font-family: var(--serif); font-size: 15px; background: transparent; color: var(--fg); border: none; border-bottom: 1px solid var(--rule-strong); padding: 5px 0; }
  .noteform input::placeholder { color: var(--faint); }
  .noteform input:focus { border-bottom-color: var(--accent); }
  .bl { display: grid; grid-template-columns: 9em minmax(0,1fr); gap: 10px; padding: 6px 0; border-bottom: 1px solid var(--rule); font-size: 14px; }
  .bl .where { font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); white-space: nowrap; padding-top: 3px; }
  .bl .hot { color: var(--urgent); }
  /* A name inside the sheet reads as a name, not as a mono verb, and still
     opens its own body through the one data-ent handler. */
  .bl .who { font: inherit; text-transform: none; letter-spacing: 0; color: var(--fg); }
  .notes { margin-top: 6px; }
  .note { display: grid; grid-template-columns: 7.5em minmax(0,1fr); gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--rule); font-size: 14px; line-height: 1.4; }
  .note .d { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--faint); padding-top: 3px; white-space: nowrap; }
  .note.import { display: block; }
  .note.import summary { cursor: pointer; font-family: var(--mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); list-style: none; }
  .note.import summary::before { content: "+ "; }
  .note.import[open] summary::before { content: "- "; }
  .note.import p { margin-top: 6px; color: var(--muted); }
  .notes .cap { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); padding-top: 5px; }

  /* ---- The Rolodex Room (Overlay, R12): the search dialog (spec-overlay D).
     The box is the whole head of the sheet and the list is under it, one row
     to a line, the kind in its own column. The selected option is the tint the
     rows use, not a colour: the arrow keys move a highlight, not a state. */
  .searchsheet { padding: 0; }
  .searchsheet input { width: 100%; font-family: var(--serif); font-size: 20px; padding: 16px 18px; border: none; border-bottom: 1px solid var(--rule-strong); background: var(--bg); color: var(--fg); }
  .results { max-height: 60vh; overflow: auto; }
  .res { display: grid; grid-template-columns: 7em minmax(0,1fr); gap: 12px; width: 100%; text-align: left; appearance: none; background: none; border: none; border-bottom: 1px solid var(--rule); font: inherit; color: inherit; padding: 9px 18px; cursor: pointer; }
  .res[aria-selected="true"], .res:hover { background: var(--tint); }
  .res .kind { font-family: var(--mono); font-size: 9px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); padding-top: 5px; }
  .res .n { font-size: 15px; display: block; }
  .res .s { font-size: 13px; color: var(--muted); display: block; }
  .res-note { display: grid; grid-template-columns: 5.5em minmax(0,1fr); gap: 8px; font-size: 13px; color: var(--muted); margin-top: 2px; }
  .res-note .d { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--faint); white-space: nowrap; }
  .res-cap, .res-empty { padding: 12px 18px; font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); }
  .res-empty { font-family: var(--serif); font-size: 14px; font-style: italic; letter-spacing: 0; }

  /* ---- The Rolodex Room (Overlay, R16): the note form in the person body
     (spec-overlay B). One line: an optional sentence and the word that logs
     the day. The field is a rule under the text, like the search box, and
     never a box: the sheet has no boxes in it. */
  .vh { position: absolute; left: -9999px; }
  .noteform { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--rule); }
  .noteform input { flex: 1; min-width: 12em; font-family: var(--serif); font-size: 15px; background: transparent; color: var(--fg); border: none; border-bottom: 1px solid var(--rule-strong); padding: 3px 0; }
  .noteform input::placeholder { color: var(--faint); }

  /* ---- The Rolodex Room (Overlay, R14): the prep packet under its agenda
     row (spec-overlay E). The word sits under the title, not in the duration
     column, because .ag-dur is hidden on a phone. The block is a plain rule
     under the row it belongs to: no card, no tint, nothing rounded. */
  .ag-prep { margin-top: 3px; }
  .prep { border-bottom: 1px solid var(--rule); padding: 8px 0 12px 146px; }
  .prep-body { display: grid; gap: 10px; }
  .prep-person { display: grid; gap: 3px; }
  .prep-who { font-size: 15px; }
  .prep-who .org { color: var(--muted); font-size: 13px; }
  .prep-who .who { font: inherit; text-transform: none; letter-spacing: 0; color: var(--fg); }
  .prep-line { font-size: 14px; color: var(--muted); line-height: 1.4; }
  /* .lbl is a block over a list everywhere else in the sheet; inside a packet
     line it is the first word of that line, so it says so here and nowhere
     else. */
  .prep-line .lbl { display: inline; margin: 0 8px 0 0; }
  .prep-line .d { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 11px; color: var(--faint); margin-right: 6px; }
  .prep-line .hot { color: var(--urgent); }
  .prep-count { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; color: var(--faint); }

  /* ---- The Composing Room (Hairline): the measure (spec-hairline D), for P4.2.
     Word, bar, number: the bar is rule-strong on rule and carries no colour. */
  .measure { display: grid; grid-template-columns: minmax(0,1fr) minmax(60px, 40%) auto; gap: 12px; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--rule); font-size: 14px; }
  .m-bar { height: 6px; background: var(--rule); position: relative; }
  .m-bar i { position: absolute; left: 0; top: 0; bottom: 0; background: var(--rule-strong); }
  .m-n { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); text-align: right; min-width: 3.5em; }
  .foot b { font-weight: normal; color: var(--muted); font-variant-numeric: tabular-nums; }
  .foot kbd { font-family: var(--mono); border: 1px solid var(--rule); padding: 0 4px; }

  @media print {
    .capture, .rail, .themebtn, .pullbtn, .pick, dialog, .row-act, button.linkish { display: none !important; }
    body { max-width: none; padding: 0; font-size: 11pt; }
    .panel { display: block !important; }
    .spread { grid-template-columns: 1fr; }
    .colrule { display: none; }
    /* On paper the week is the rows grid: seven columns of 40px hours do not
       survive a page break, and the rows say the same thing in one strip. */
    .cal { display: none; }
    .bar { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  }
  @media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
  @media (max-width: 900px) {
    .spread { grid-template-columns: minmax(0,1fr); gap: 0; }
    .colrule { display: none; }
    .aside-col { border-top: 1px solid var(--rule-strong); margin-top: 22px; padding-top: 4px; }
    /* Seven columns of hours need width the phone has not got. The rows grid
       under them draws the same week and stays (Q3, spec-quarter C). */
    .cal { display: none; }
  }
  @media (max-width: 620px) {
    body { padding: 22px 16px 86px; font-size: 16px; }
    .nameplate .name { letter-spacing: 0.03em; }
    .dateline { font-size: 10px; }
    .rail-inner { gap: 22px; }
    .now { grid-template-columns: minmax(0,1fr); gap: 6px; }
    .now-count { text-align: left; }
    .brief p:first-child::first-letter { font-size: 2.7em; }
    .ag-row, .now-rule { grid-template-columns: 96px minmax(0,1fr); }
    .ag-dur { display: none; }
    /* The packet loses the column it was indented past (R14). */
    .prep { padding-left: 0; }
    .capture form { padding: 9px 16px; }
    .capture .hint { display: none; }
    .notice { grid-template-columns: 6em minmax(0,1fr); }
    .notice .do { grid-column: 2; }
    /* Hairline: rows fold, the sheet drops to the bottom, targets grow
       (contract 7, spec-hairline F), verified in P3.5 against the rows that
       now exist. The wrapper goes to one column, so a row's verbs sit under
       its ask instead of squeezing the name into a third of the screen
       (spec-overlay 184-187); the status word keeps the right-hand column on
       every kind, not only applications. */
    .row { grid-template-columns: minmax(0,1fr); }
    .row-open, .row-open.app, .row-open.task, .row-open.ed { grid-template-columns: minmax(0,1fr) auto; min-height: 44px; align-content: center; }
    .row-open.ag { grid-template-columns: 96px minmax(0,1fr) auto; }
    .row-ask, .row-notes { grid-column: 1 / -1; white-space: normal; }
    .row-open .row-st { grid-column: 2; text-align: right; }
    .row-act { gap: 12px; align-items: center; min-height: 44px; padding-top: 0; }
    /* A finger is 44px and the words are 10px mono: the hit area grows around
       each verb without moving its underline off the text. The box is exactly
       the line's height, so it never reaches into the row above or below. */
    .row-act .linkish { position: relative; }
    .row-act .linkish::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 44px; transform: translateY(-50%); }
    dialog { margin: 0; position: fixed; inset: auto 0 0 0; width: 100vw; max-width: 100vw; }
    /* Measured, not guessed (P3.5): a fraction of the viewport cannot say
       "leave the masthead showing", because the chrome above is a fixed
       height and the screen is not. The drive measured the masthead at 143px
       on a 390px phone, so 170px clears the nameplate and the day clock with
       room to spare, and still leaves the sheet 80% of a tall screen. */
    .sheet { width: 100vw; max-height: calc(100vh - 170px); border-left: none; border-right: none; border-bottom: none; padding: 16px 16px 24px; }
    .sheet dl { grid-template-columns: minmax(0,1fr); gap: 2px; }
    /* The search dialog is the same bottom sheet; the list keeps the screen
       above the keyboard, so the first result is in view (R12). */
    .searchsheet input { font-size: 17px; padding: 14px 16px; }
    .res { grid-template-columns: 5em minmax(0,1fr); gap: 10px; padding: 11px 16px; min-height: 44px; align-content: center; }
    .results { max-height: 46vh; }
    .res-cap, .res-empty { padding: 10px 16px; }
    .sheet dt { margin-top: 8px; }
    .bl, .note { grid-template-columns: minmax(0,1fr); gap: 2px; }
    .measure { grid-template-columns: minmax(0,1fr) minmax(50px, 32%) auto; }
    /* The week grid narrows rather than scrolls: the day column loses its
       month, the figure keeps its word, and the bars grow to a finger. */
    .wk-row, .wk-ruler { grid-template-columns: 66px minmax(0,1fr) 46px; gap: 8px; }
    .wk-day, .wk-n { font-size: 10px; }
    .bar { top: 0; height: 16px; }
  }
