/* bseamer.com - the one stylesheet: Layout-mode pages, the R930 specification
   layout, and the admin surface. Tokens are the R930 page's: IBM Plex, paper
   and ink, light and dark by preference. Raw-mode pages carry their own styles. */

/* ---- fonts: the six self-hosted faces are in plex.css, linked only when the
   Fonts folder ships; the layouts load the family from Google until then. ---- */
/* Metric-compatible fallback so the serif headings do not jump when the face arrives. */
@font-face { font-family: "Plex Serif Fallback"; src: local("Georgia"); size-adjust: 96%; ascent-override: 98%; descent-override: 28%; line-gap-override: 0%; }

/* ---- tokens ---- */
:root {
  color-scheme: light dark;
  --paper:      #EFF1F3;
  --surface:    #FFFFFF;
  --panel:      #E7EAEE;
  --ink:        #1A2028;
  --ink-soft:   #5A6472;
  --ink-faint:  #636D7B;
  --rule:       #D6DBE1;
  --accent:     #A64A22;
  --accent-dim: #F0E2DA;
  --signal:     #23655A;
  --signal-dim: #DCEEE8;
  --danger:     #A63D2F;
  --on-fill:    #FFFFFF;
  --field:      #7B8591;
  --serif: "IBM Plex Serif", "Plex Serif Fallback", Georgia, "Times New Roman", serif;
  --sans:  "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono:  "IBM Plex Mono", ui-monospace, "Cascadia Code", Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #12161B;
    --surface:    #191F26;
    --panel:      #1F262E;
    --ink:        #E4E8EC;
    --ink-soft:   #9AA4AF;
    --ink-faint:  #7C8793;
    --rule:       #2A323C;
    --accent:     #D8784A;
    --accent-dim: #3A2A22;
    --signal:     #5FB8A0;
    --signal-dim: #1E3A32;
    --danger:     #E0705F;
    --on-fill:    #12161B;
    --field:      #667180;
  }
}

/* ---- base ---- */
* { box-sizing: border-box; }
html { background: var(--paper); color: var(--ink); font-family: var(--sans); line-height: 1.55; -webkit-text-size-adjust: 100%; }
body { margin: 0; }
.page { max-width: 42rem; margin: 0 auto; padding: 2.25rem 1.25rem 3rem; }
.page.wide, .page:has(main.wide) { max-width: 68rem; }
/* The rail (SDD-018 §12.5): on a wide screen a reading page keeps its 42rem column and gains a second column
   for the outline, or the front page's cards; below that the rail follows the article and the outline is
   hidden, the site navigation being enough on a phone. */
@media (min-width: 68rem) {
  .page:has(main.with-rail) { max-width: 64rem; }
  main.with-rail { display: grid; grid-template-columns: minmax(0, 42rem) 16rem; column-gap: 3rem; align-items: start; }
  main.with-rail > * { grid-column: 1; }
  /* span, not "1 / -1": with no explicit rows -1 is line 1, so the rail sat in the first row alone and a
     draft banner above the article was stretched to the rail's height (homelab SDD-018 §18). */
  main.with-rail > .rail { grid-column: 2; grid-row: 1 / span 9; position: sticky; top: 1.5rem; }
  /* A page with neither a rail nor the wide column (the 404, a short page) keeps its 42rem text and gets the
     same masthead as a rail page: at 42rem the brand and eight links wrapped onto two rows (§18). */
  .page:not(.wide):has(> main:not(.with-rail):not(.wide)) { max-width: 64rem; }
  .page:not(.wide) > main:not(.with-rail):not(.wide) { max-width: 42rem; }
}
@media (max-width: 67.99rem) { .outline { display: none; } }
.rail .cards { margin-top: 0; }
.outline h2 { font-family: var(--mono); font-weight: 400; font-size: .85rem; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: .35rem 0 .5rem; }
.outline ol { list-style: none; padding: 0; margin: 0; border-left: 2px solid var(--rule); }
.outline li { margin: 0; }
.outline a { display: block; padding: .3rem .9rem; color: var(--ink-soft); text-decoration: none; font-size: .92rem; line-height: 1.35; }
.outline a:hover { color: var(--accent); }
main:focus { outline: none; }

.skip { position: absolute; left: -999px; top: 0; z-index: 10; }
.skip:focus { left: 1rem; top: 1rem; background: var(--surface); color: var(--ink); padding: .5rem 1rem; border: 1px solid var(--rule); border-radius: 4px; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---- header, nav, footer ---- */
header.site { display: flex; flex-wrap: wrap; align-items: baseline; gap: .75rem 1.5rem; border-bottom: 1px solid var(--rule); padding-bottom: 1.1rem; margin-bottom: 2rem; }
header.site .brand { font-family: var(--serif); font-weight: 600; font-size: 1.35rem; color: var(--ink); text-decoration: none; }
header.site nav { display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin-left: auto; font-size: .95rem; }
header.site nav a { color: var(--ink-soft); text-decoration: none; padding: .15rem 0; border-bottom: 2px solid transparent; }
header.site nav a:hover { color: var(--accent); }
header.site nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }
header.site nav .tag { font-size: .8em; }

footer { margin-top: 2.5rem; padding-top: 1.1rem; border-top: 1px solid var(--rule); color: var(--ink-faint); font-family: var(--mono); font-size: .8rem; }
footer .ok { color: var(--signal); }
footer .rev { color: var(--ink-faint); }

/* ---- typography ---- */
h1 { font-family: var(--serif); font-weight: 600; font-size: 2rem; margin: 0 0 1rem; letter-spacing: -.01em; line-height: 1.2; }
h2 { font-family: var(--serif); font-weight: 600; font-size: 1.35rem; margin: 2rem 0 .65rem; line-height: 1.3; }
h3 { font-weight: 600; font-size: 1.05rem; margin: 1.75rem 0 .5rem; }
/* No single word stranded under a heading on a phone (§18). */
h1, h2, h3 { text-wrap: balance; }
p { margin: 0 0 1rem; }
a { color: var(--accent); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent); text-decoration-thickness: 1px; text-underline-offset: .15em; }
a:hover, a:focus-visible { text-decoration-color: var(--accent); }
a[target="_blank"]::after { content: "" / "opens in a new tab"; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible, summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
ul, ol { padding-left: 1.2rem; margin: 0 0 1rem; }
li { margin-bottom: .45rem; }
code { font-family: var(--mono); font-size: .9em; background: var(--panel); padding: .1em .35em; border-radius: 3px; }
pre { background: var(--panel); padding: 1rem; border-radius: 6px; overflow-x: auto; font-family: var(--mono); font-size: .85rem; line-height: 1.45; }
pre code { background: none; padding: 0; font-size: inherit; }
mark { background: var(--accent-dim); color: inherit; padding: 0 .15em; }
img, svg { max-width: 100%; height: auto; }
figure { margin: 1.5rem 0; }
figcaption { color: var(--ink-soft); font-size: .9rem; }
/* A drawn view (an ArchiMate diagram from the media library) is a card in the prose that opens the view in a
   dialog for the reader who wants it (SDD-021 §11.10): the card carries a pictogram, a line and one action; the
   dialog shows the file at its own width in a pane that scrolls, so it pans on a phone and fits on a desktop. */
.view-card { display: grid; grid-template-columns: auto 1fr; gap: .3rem 1rem; align-items: start; background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: 1.1rem 1.25rem; margin: 1.5rem 0; }
.view-card svg { width: 2.25rem; height: 2.25rem; color: var(--signal); grid-row: span 3; margin-top: .15rem; }
.view-card p { margin: 0; }
.view-card .button { margin-top: .4rem; }
/* a column: bar, pane, caption; the pane is the only thing that scrolls, so the dialog never grows a scrollbar of its own */
dialog.view-dialog { width: min(96vw, 1440px); max-height: 94vh; padding: 0; border: 1px solid var(--rule); border-radius: 8px; background: var(--surface); color: var(--ink); overflow: hidden; }
dialog.view-dialog[open] { display: flex; flex-direction: column; }
dialog.view-dialog:has(img.fit) { height: 94vh; }
dialog.view-dialog::backdrop { background: rgba(10, 14, 18, .72); }
.view-bar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: .6rem 1rem; border-bottom: 1px solid var(--rule); }
.view-bar .tag { margin: 0; }
.view-actions { display: flex; gap: .5rem; }
.view-pane { overflow: auto; flex: 1 1 auto; min-height: 0; }
.view-stage { position: relative; display: inline-block; min-width: 100%; }
.view-pane img { display: block; width: 100%; min-width: 1305px; height: auto; }
.view-pane img.fit { min-width: 0; max-height: 100%; width: auto; max-width: 100%; margin: 0 auto; }
/* Fitted, the stage must SHRINK-WRAP the picture, because the hotspot overlay is positioned
   against the stage and a stage sized to the pane puts every hotspot in the wrong place. It
   was 335x526 over a 335x427 drawing on a phone - every piece of the picture off by a fifth of
   its height, which only appeared once fitting became the default there (SDD-021 §11.13). */
/* The parts of a drawing, by name, for a screen too narrow to carry the rail. The drawing is
   still there and still tappable; this is for the pieces whose hit target is three millimetres
   across at phone width. Hidden wherever the rail is shown - a desktop reads the drawing. */
.part-index { display: block; margin: -.75rem 0 2rem; }
.part-index ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.part-index li { margin: 0; }
.part-index a { display: inline-block; font-family: var(--mono); font-size: .8rem; line-height: 1.3; color: var(--ink-soft); background: var(--surface); border: 1px solid var(--rule); border-radius: 999px; padding: .35rem .7rem; text-decoration: none; }
.part-index a:hover, .part-index a:focus-visible { color: var(--accent); border-color: var(--accent); }
@media (min-width: 76rem) { .part-index { display: none; } }
/* On a touch screen the index is the only readable way into a drawing's parts, so each name is a full target (§18). */
@media (pointer: coarse) { .part-index a { padding: .8rem .9rem; } }

.view-pane:has(img.fit) { text-align: center; }
.view-stage:has(img.fit) { display: inline-block; min-width: 0; height: auto; text-align: initial; }
.view-stage:has(img.fit) svg.hotspots { left: 0; transform: none; width: 100%; height: 100%; }
/* the overlay: one <a class="part"> per piece over the picture, invisible until hovered or focused (SDD-018 §13) */
svg.hotspots { position: absolute; inset: 0; width: 100%; height: 100%; }
svg.hotspots[hidden], .view-card svg.hotspots { display: none; } /* the hidden attribute does not hide an SVG root; the overlay belongs in the dialog only */
svg.hotspots a.part rect.hit { fill: transparent; stroke: transparent; stroke-width: 3; transition: stroke .12s; }
svg.hotspots a.part:hover rect.hit, svg.hotspots a.part:focus rect.hit { stroke: var(--accent); fill: color-mix(in srgb, var(--accent) 12%, transparent); }
svg.hotspots a.part:focus { outline: none; }
.view-caption { margin: 0; padding: .6rem 1rem; color: var(--ink-soft); font-size: .9rem; border-top: 1px solid var(--rule); }
.tag { color: var(--ink-soft); font-family: var(--mono); font-size: .9rem; margin: 0; }
.dateline { color: var(--ink-faint); font-family: var(--mono); font-size: .8rem; margin-top: 2rem; }
.card { background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: 1rem 1.1rem; margin: 1rem 0; }
.card p { margin: 0; }
.card .tag { margin-bottom: .35rem; }
.cards { margin-top: 2rem; }
.cards h2 { font-family: var(--mono); font-weight: 400; font-size: .85rem; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: 0 0 .25rem; }
.flash { padding: .75rem 1rem; border-radius: 6px; margin: 0 0 1.25rem; }
.flash.error { background: var(--accent-dim); color: var(--ink); }
.flash.ok { background: var(--signal-dim); color: var(--ink); }
.flash.draft { background: var(--panel); color: var(--ink-soft); font-family: var(--mono); font-size: .85rem; }
.flash.error:empty, .validation-summary-valid { display: none; }

/* ---- an interactive schematic (SDD-018 §13): an inline SVG drawn in the site's tokens, every part an <a class="part">
   whose rectangle is invisible until hovered or focused, linking to a section.part below; the section a visitor arrived
   at is marked. Works as links without script; the panel script may enhance it later. ---- */
.schematic { margin: 1.5rem 0; }
.schematic svg { display: block; width: 100%; height: auto; background: var(--surface); border: 1px solid var(--rule); border-radius: 8px; }
.schematic a.part rect.hit, .schematic a.part path.hit { fill: transparent; stroke: transparent; stroke-width: 2; transition: stroke .12s; }
.schematic a.part:hover rect.hit, .schematic a.part:focus rect.hit, .schematic a.part:hover path.hit, .schematic a.part:focus path.hit { stroke: var(--accent); fill: color-mix(in srgb, var(--accent) 12%, transparent); }
.schematic a.part:focus { outline: none; }
.schematic .label { font-family: var(--mono); font-size: 11px; fill: var(--ink-soft); }
.schematic .title { font-family: var(--serif); font-weight: 600; font-size: 15px; fill: var(--ink); }
.schematic .callout { font-family: var(--sans); font-size: 12px; fill: var(--ink); }
.parts { display: none; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 1rem; margin: 1rem 0 2rem; } /* the cards feed the dialog; noscript.css lays them out when there is no script */
section.part { background: var(--surface); border: 1px solid var(--rule); border-left-width: 5px; border-left-color: var(--signal); border-radius: 6px; padding: 1rem 1.1rem; scroll-margin-top: 1.5rem; }
section.part:target { border-left-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 35%, transparent); }
section.part h3 { margin: 0 0 .4rem; display: flex; align-items: center; gap: .6rem; }
section.part h3 svg { width: 1.5rem; height: 1.5rem; color: var(--signal); flex: none; }
section.part p { margin: 0 0 .5rem; }
section.part .tag { margin: 0 0 .3rem; }
section.part ul { margin: 0; padding-left: 1.1rem; }
/* the same card, opened in a dialog by the part clicked (the cards below are hidden while the script runs) */
dialog.part-dialog { width: min(92vw, 36rem); padding: 1.25rem 1.5rem 1rem; border: 1px solid var(--rule); border-left: 5px solid var(--signal); border-radius: 8px; background: var(--surface); color: var(--ink); }
dialog.part-dialog::backdrop { background: rgba(10, 14, 18, .6); }
.part-body h3 { margin: 0 0 .4rem; display: flex; align-items: center; gap: .6rem; }
.part-body h3 svg { width: 1.5rem; height: 1.5rem; color: var(--signal); flex: none; }
.part-body p { margin: 0 0 .5rem; }
.part-body .tag { margin: 0 0 .3rem; }
.part-body ul { margin: 0; padding-left: 1.1rem; }
.part-actions { margin: 1rem 0 0; text-align: right; }

/* ---- the same card in a rail beside the picture, where the screen is wide enough to hold both
   (SDD-018 §13.9 q3, answered 2026-09-07: the rail). Narrower than that it opens in the dialog above,
   which is the phone behaviour the design always specified. The script asks the stylesheet which one
   applies by reading the rail's computed display, so these widths are stated once, here. They differ
   because each picture runs out of a different thing: the view is portrait and fits by its height, so
   a 24rem rail costs it nothing down to about 66rem; the R930's drawing is landscape and its 11px
   labels are what runs out first, so its rail waits for 76rem and the drawing then breaks out of the
   reading column to keep them at least as large as they are with no rail at all. ---- */
.view-rail, .part-rail { display: none; }
.rail-empty .tag { margin: 0 0 .35rem; }
.rail-empty p:last-child { color: var(--ink-soft); margin: 0; }
/* the piece being read is marked on the picture, which in the rail stays in view beside its card */
svg.hotspots a.part[data-current] rect.hit,
.schematic a.part[data-current] rect.hit,
.schematic a.part[data-current] path.hit { stroke: var(--accent); fill: color-mix(in srgb, var(--accent) 12%, transparent); }

@media (min-width: 66rem) {
  dialog.view-dialog { width: min(98vw, 1720px); }
  dialog.view-dialog[open] {
    display: grid; height: 94vh;
    grid-template-columns: minmax(0, 1fr) 24rem;
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas: "bar bar" "pane rail" "cap rail";
  }
  dialog.view-dialog .view-bar { grid-area: bar; }
  dialog.view-dialog .view-pane { grid-area: pane; }
  dialog.view-dialog .view-caption { grid-area: cap; }
  .view-rail {
    display: block; grid-area: rail; overflow: auto; min-height: 0;
    border-left: 1px solid var(--rule); padding: 1.1rem 1.25rem 1.5rem;
  }
}

@media (min-width: 76rem) {
  .schematic[data-rail] {
    display: grid; grid-template-columns: minmax(0, 1fr) 19rem; column-gap: 1.5rem; align-items: start;
    width: min(98vw, 1720px); margin-left: calc(50% - min(49vw, 860px));
  }
  .part-rail {
    display: block; position: sticky; top: 1.5rem; max-height: calc(100vh - 3rem); overflow: auto;
    background: var(--surface); border: 1px solid var(--rule); border-left: 5px solid var(--signal);
    border-radius: 6px; padding: 1rem 1.1rem;
  }
}

/* ---- the strip on a page that belongs to a set: the parent and the pages under it (SDD-018 §14).
   Mono and small, like a dateline, so it reads as location rather than as another navigation. ---- */
.section-nav { display: flex; flex-wrap: wrap; gap: .35rem 1rem; font-family: var(--mono); font-size: .85rem; margin: -.4rem 0 1.75rem; }
.section-nav a { color: var(--ink-soft); text-decoration: none; padding: .15rem 0; border-bottom: 2px solid transparent; }
.section-nav a:hover { color: var(--accent); }
.section-nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

/* ---- the bill (SDD-022): live figures rendered into an ordinary reading page.
   The Manage grammar holds on a public page too - a status is a word beside a
   colour, so every row says "measured" or "a fixed price" in words and the pip
   only tints it. ---- */
.cost { margin: 1.5rem 0 2rem; }
.cost-service { margin: 0 0 2rem; }
.cost-service h3 { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .25rem 1rem; margin: 0 0 .25rem; padding-bottom: .3rem; border-bottom: 2px solid var(--rule); }
.cost-service.platform h3 { border-bottom-color: var(--accent); }
.cost-service h3 .total { font-family: var(--mono); font-size: .85rem; font-weight: 400; color: var(--ink-soft); white-space: nowrap; }
.cost-service .blurb { margin: .4rem 0 .6rem; color: var(--ink-soft); font-size: .92rem; }
.cost-free { margin: 0 0 1.5rem; }
.cost-net { margin: 1.5rem 0 0; padding-top: .9rem; border-top: 2px solid var(--rule); }
.cost .checked { display: block; color: var(--ink-faint); font-size: .85rem; margin-top: .2rem; }
.cost .amount { font-family: var(--mono); white-space: nowrap; text-align: right; }
.cost .qty { display: block; color: var(--ink-soft); font-family: var(--mono); font-size: .8rem; margin-top: .15rem; }
.cost .note { display: block; color: var(--ink-soft); font-size: .88rem; margin-top: .3rem; }
.cost tbody td { font-size: .92rem; }
.cost tbody strong { font-weight: 600; font-size: .95rem; }
.cost .saving .amount { color: var(--signal); }
.cost tfoot th, .cost tfoot td { border-bottom: 0; border-top: 2px solid var(--rule); padding-top: .7rem; }
.cost tfoot th { color: var(--ink); font-weight: 600; white-space: normal; }
.cost tfoot td:last-child { color: var(--ink-soft); font-size: .88rem; }
.cost .cost-once { margin: 1.25rem 0 0; }
.cost .dateline { margin-top: .9rem; }
.cost-missing { color: var(--ink-soft); }
@media (max-width: 40rem) {
  /* Cells are blocks at this width, so a right-aligned amount would sit against the far edge, away from its label. */
  .cost .amount { text-align: left; }
  .cost tfoot tr { border-bottom: 0; }
}

.cost-admin { margin: 0 0 2.5rem; }
.cost-admin h2 { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; margin-bottom: .3rem; }
.cost-admin h2 .total { font-family: var(--mono); font-size: .85rem; font-weight: 400; color: var(--ink-soft); }
.cost-admin .blurb { color: var(--ink-soft); margin: 0 0 .6rem; }
.cost-admin .note { color: var(--ink-soft); font-size: .92rem; margin: 0 0 .6rem; }

/* ---- the watch feed (SDD-004): the television's home page, so the cards are large and the
   grid is generous. Private tier only. ---- */
/* The feed as rows (SDD-027 phase 3): the picture left, the words right, the picture the tap that
   plays in place, the controls small under the words. One column at a phone's width; the rows
   themselves widen and pair up on a desktop. */
/* Start-aligned: stretched to a pair's height, a row's controls sank under its neighbour's player (the second read, 2026-09-14). */
.watch-list { display: grid; gap: .65rem; margin: 1rem 0 2rem; align-items: start; }
.watch-item { display: grid; grid-template-columns: 8.5rem minmax(0, 1fr); grid-template-rows: auto auto 1fr; column-gap: .7rem; row-gap: .15rem; align-items: start; min-width: 0; }
.watch-item > .row-pic { grid-row: 1 / span 3; display: block; min-width: 0; }
/* The row's own display outranks the browser's rule for the hidden attribute, so the kind switches, the
   chips and a mark made in place hid nothing until this said so (the second read, 2026-09-14). */
.watch-item[hidden] { display: none; }
.watch-item a { text-decoration: none; color: inherit; }
.watch-item img, .watch-item .row-nopic { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 4px; background: var(--panel); display: block; }
.watch-item h2 { font-family: var(--sans); font-size: .9rem; font-weight: 600; line-height: 1.3; margin: 0; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.watch-item .meta { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: .76rem; }
.watch-item img { object-position: top; }
/* A game's picture: the two sides' colours and crests (the _GamePic partial). */
.watch-item .game-pic { width: 100%; aspect-ratio: 16 / 9; border-radius: 4px; display: block; background: var(--panel); }
.game-pic-at { fill: #fff; font-family: var(--mono); font-size: 11px; font-weight: 600; paint-order: stroke; stroke: rgba(0,0,0,.35); stroke-width: 2px; }
.watch-item h2 a:hover { color: var(--accent); }
/* The clamp's overflow clips a ring drawn outside the text, so the row title's focus is drawn inside it, and underlined. */
.watch-item h2 a:focus-visible { outline-offset: -2px; text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 2px; }
.watch-item .row-nopic { border: 1px solid var(--rule); box-sizing: border-box; }
.watch-item .row-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin: .15rem 0 0; align-self: end; }
.watch-item .row-actions form { margin: 0; }
/* On a phone the two controls do not fit beside the picture and stacked under the words, four rows to a
   screen; they take the row's full width under both instead, side by side (the second read, 2026-09-14). */
@media (max-width: 40rem) {
  .watch-item { grid-template-rows: auto 1fr auto; }
  .watch-item > .row-pic { grid-row: 1 / span 2; }
  .watch-item .row-actions { grid-column: 1 / -1; align-self: start; flex-wrap: nowrap; margin-top: .4rem; }
  .watch-item .row-actions > * { flex: 1 1 0; min-width: 0; }
  .watch-item .row-actions button { width: 100%; }
}
@media (min-width: 40rem) {
  .watch-list { grid-template-columns: repeat(auto-fill, minmax(30rem, 1fr)); gap: 1rem 2rem; }
  .watch-item { grid-template-columns: 10rem minmax(0, 1fr); }
}
.watch-item .meta { margin: 0; color: var(--ink-soft); font-family: var(--sans); font-size: .82rem; }
/* The age in the line's own face: in mono, "7 h" read as a double space. */
.watch-item .meta time { font-family: inherit; font-size: inherit; }
/* The watch area's own navigation, under the heading. Mono and small, so it
   reads as a place-marker rather than competing with the page's own controls. */
.watch-nav { display: flex; flex-wrap: wrap; gap: .25rem 1.1rem; margin: -.35rem 0 1.25rem; font-family: var(--sans); font-size: .92rem; }
/* The hub's shell (SDD-027 phase 7): the wordmark, the page navigation, wrapping rather than scrolling its last pages out of sight, and a
   small way back to admin. On a phone the navigation takes its own line under the wordmark. */
header.site.hub { align-items: center; flex-wrap: nowrap; gap: .5rem 1.25rem; padding-bottom: .7rem; margin-bottom: 1.25rem; }
header.site.hub .brand { flex: 0 0 auto; }
header.site.hub .watch-nav { margin: 0; flex: 1 1 auto; flex-wrap: wrap; gap: .25rem 1rem; padding-bottom: .15rem; }
header.site.hub .hub-admin { flex: 0 0 auto; margin-left: auto; font-family: var(--mono); font-size: .75rem; color: var(--ink-faint); text-decoration: none; }
header.site.hub .hub-admin:hover { color: var(--accent); }
.hub h1 { font-size: 1.5rem; margin: 0 0 .75rem; }
@media (max-width: 40rem) {
  header.site.hub { flex-wrap: wrap; gap: .35rem .75rem; }
  header.site.hub .watch-nav { flex-basis: 100%; order: 3; flex-wrap: wrap; white-space: normal; overflow: visible; gap: .25rem .9rem; }
}
.watch-nav a { color: var(--ink-soft); text-decoration: none; padding: .15rem 0; border-bottom: 2px solid transparent; }
.watch-nav a:hover { color: var(--accent); }
.watch-nav a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--accent); }

/* Watched, but still on the page while "show what you have watched" is on: dimmed
   rather than hidden, so the difference is visible without the row moving. */
.watch-item.seen > .row-pic img { opacity: .45; }
.watch-item.seen h2 { color: var(--ink-soft); }
/* The week's board (SDD-004 §5.5, the dashboard): a card per game in the shape the popular boards
   share, what is on first. A game on carries the signal colour beside its word, a final is faint with
   its winner in full ink, a team in the red zone is said in words on the card and marked by the rule. */
.scores { margin: 0 0 1.5rem; }
.scores .scores-head { font-family: var(--sans); font-size: 1rem; font-weight: 600; margin: 0 0 .4rem; display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; }
.scores .scores-next { font-family: var(--sans); font-size: .82rem; font-weight: 400; color: var(--ink-soft); }
.scores .scores-age { font-family: var(--mono); font-size: .75rem; font-weight: 400; color: var(--ink-faint); margin-left: auto; }
.scores .scores-group { font-family: var(--sans); font-size: .78rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: .9rem 0 .4rem; }
.scores-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: .7rem; }
/* Every game not on is one row: when, the two sides, the broadcaster. Sixteen cards took the page. */
.scores-list { background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: 0 .8rem; }
/* The board folds under its heading; more than one game to a line on a desktop (SDD-027, consolidated). */
.scores-fold > summary { list-style: none; cursor: pointer; }
.scores-fold > summary::-webkit-details-marker { display: none; }
.scores-fold > summary .scores-head::before { content: "+"; content: "+" / ""; font-family: var(--mono); color: var(--ink-faint); margin-right: .5rem; }
.scores-fold[open] > summary .scores-head::before { content: "\2212"; content: "\2212" / ""; }
@media (min-width: 40rem) {
  .scores-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); column-gap: 1.5rem; padding: 0 1rem; }
  .game-row { grid-template-columns: 4.5rem minmax(0, 1fr) auto; }
}
.game-row { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) auto; align-items: center; gap: .6rem; padding: .35rem 0; border-bottom: 1px solid var(--rule); font-size: .9rem; min-width: 0; }
.game-row:last-child { border-bottom: 0; }
.game-row .row-when, .game-row .row-on { font-family: var(--mono); font-size: .78rem; color: var(--ink-soft); white-space: nowrap; }
.game-row .row-on { text-align: right; }
.game-row .row-teams { display: flex; align-items: center; gap: .45rem; min-width: 0; }
.game-row .row-between { font-family: var(--mono); font-size: .72rem; color: var(--ink-faint); }
.game-row .side { display: inline-flex; align-items: center; gap: .3rem; }
.game-row .side .abbr { font-family: var(--mono); font-size: .82rem; font-weight: 600; }
.game-row .side .score { font-family: var(--mono); font-size: .95rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.game-row .crest { width: 1.6rem; height: 1.6rem; object-fit: contain; display: block; }
.game-row .side .score { font-size: 1.05rem; }
.game-row .swatch { width: .6rem; height: .6rem; display: block; }
.game-row .swatch rect { stroke: var(--rule); stroke-width: .6; }
.game-row.final { color: var(--ink-soft); }
.game-row.final .side.winner { color: var(--ink); }
.game-row.final .side.loser .score { color: var(--ink-faint); font-weight: 500; }
/* A game that is over opens its review from its scores (2026-09-14): the row looks the same until pointed at. */
.game-row a.row-teams { color: inherit; text-decoration: none; }
.game-row.final a.row-teams:hover .side .score, .game-row.final a.row-teams:focus-visible .side .score { color: var(--accent); text-decoration: underline; text-underline-offset: .15em; }
@media (max-width: 40rem) { .game-row { grid-template-columns: 4.6rem minmax(0, 1fr) auto; gap: .4rem; } }
.game { background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: .6rem .8rem .7rem; min-width: 0; }
.game.live { border-left: 4px solid var(--signal); padding-left: .65rem; }
.game.live.red-zone { border-left-color: var(--danger); }
.game p { margin: 0; }
.game .game-status { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; font-family: var(--mono); font-size: .78rem; color: var(--ink-soft); margin-bottom: .35rem; }
.game .game-status time { color: var(--ink); }
.game.live .game-clock { color: var(--signal); font-weight: 600; }
.game .game-on { margin-left: auto; }
.game .game-teams { display: grid; gap: .2rem; }
.game .team { display: grid; grid-template-columns: 1.6rem 2.6ch minmax(0, 1fr) auto auto auto auto; align-items: center; gap: .4rem; min-width: 0; }
.game .team > :first-child { width: 1.6rem; display: flex; justify-content: center; }
.game .crest { width: 1.6rem; height: 1.6rem; object-fit: contain; display: block; }
.game .swatch { width: .7rem; height: .7rem; display: block; }
.game .swatch rect { stroke: var(--rule); stroke-width: .6; }
.game .abbr { font-family: var(--mono); font-size: .8rem; font-weight: 600; }
.game .team-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.game .record, .game .timeouts { font-family: var(--mono); font-size: .72rem; color: var(--ink-faint); }
.game .timeouts { letter-spacing: .05em; }
.game .ball { color: var(--signal); font-size: .7rem; line-height: 1; }
.game .score { font-family: var(--mono); font-size: 1.3rem; font-weight: 600; line-height: 1; min-width: 2ch; text-align: right; font-variant-numeric: tabular-nums; }
.game.live .team.has-ball .team-name { font-weight: 600; }
.game .game-line { margin-top: .4rem; font-size: .85rem; overflow-wrap: anywhere; }
.game .game-line.faint { color: var(--ink-faint); font-size: .8rem; }
.game .game-actions { margin-top: .55rem; gap: .45rem; }
.game .game-actions .button, .game .game-actions button { font-size: .8rem; min-height: 2rem; padding: .2rem .65rem; }
/* A game that is on: the one card whose thing is happening now, marked by the signal colour beside its word (SDD-004 §5.5). */
.watch-item.live > .row-pic { box-shadow: -4px 0 0 var(--signal); }
.watch-item .meta .pip { margin-right: .15rem; }
.watch-item .meta .tag { font-family: var(--sans); font-size: inherit; }
.watch-item .mark-seen button { font-size: .85rem; min-height: 2.5rem; padding: .3rem .7rem; }
.seen-toggle { margin: 0 0 1rem; }

/* One switch per kind of thing in the feed. A switched-off kind is said in words
   beside the colour and never by colour alone (SDD-020), so the pip inside the
   button carries "on" or "off" and this only softens the button around it. */
.kind-switches { margin: 0 0 1rem; align-items: center; }
.kind-switches button { font-size: .82rem; min-height: 2.1rem; padding: .25rem .7rem; }
.who button[aria-pressed="true"], .kind-switches button[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); font-weight: 600; }
.kind-switches button .pip { margin-left: .35rem; }
/* Three kinds on one line at a phone's width; at the desktop's padding the third wrapped to a line of its own. */
@media (max-width: 40rem) { .kind-switches button { padding: .25rem .55rem; } .kind-switches button .pip { margin-left: .25rem; } }
/* One height for a switch, whichever group it is in, and room under the profile switch before the next block. */
.who { margin: 0 0 .6rem; }
.who button { min-height: 2.1rem; padding: .25rem .8rem; font-size: .9rem; }
/* Forced colours drop the pressed state's border colour and shadow; an outline in the system's highlight survives. */
@media (forced-colors: active) { button[aria-pressed="true"] { outline: 2px solid Highlight; outline-offset: -4px; } }
/* The admin navigation takes its own line on a phone, rather than leaving View site alone on a third. */
@media (max-width: 40rem) { body.admin:not(.hub) header.site nav { margin-left: 0; flex-basis: 100%; gap: .25rem .8rem; font-size: .9rem; } }
/* The editor's state line sits apart from the Title field it had read as the caption of. */
.tag + form[data-editor] { margin-top: 1rem; }
.row-sub .keep { white-space: nowrap; }

/* The player takes the thumbnail's place in the card, at the same size, so the
   grid does not reflow when something starts playing. */
.watch-item .player { grid-column: 1 / -1; grid-row: 1; width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: 6px; background: #000; display: block; }
.watch-item.playing > .row-pic { display: none; }
.watch-item.playing > h2, .watch-item.playing > .meta, .watch-item.playing > .row-actions { grid-column: 1 / -1; }

/* ---- the R930 specification layout (a .r930 block in a Layout page, wide) ---- */
.r930 .eyebrow { color: var(--ink-soft); font-family: var(--mono); font-size: .85rem; letter-spacing: .04em; text-transform: uppercase; margin: 0 0 .5rem; }
.r930 .standfirst { font-size: 1.15rem; color: var(--ink-soft); max-width: 44rem; }
/* 9rem, not 11: a 393px phone's column is 353px, which missed two tiles across by 15px (§18). */
.r930 .specs { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 1rem; margin: 2rem 0; }
.r930 .spec { background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; padding: 1rem 1.1rem; }
.r930 .spec .label { color: var(--ink-soft); font-family: var(--mono); font-size: .8rem; margin: 0 0 .25rem; }
.r930 .spec .value { font-family: var(--serif); font-size: 1.35rem; font-weight: 600; margin: 0; }
.r930 dl.machine { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1.5rem; margin: 1.5rem 0; }
.r930 dl.machine dt { color: var(--ink-soft); font-family: var(--mono); font-size: .85rem; }
.r930 dl.machine dd { margin: 0; }
.r930 .layers { display: grid; gap: .75rem; margin: 1.5rem 0; }
.r930 .layer { display: grid; grid-template-columns: 11rem 1fr; gap: 1rem; padding: .9rem 1.1rem; background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; }
.r930 .layer h3 { margin: 0; font-size: 1rem; }
.r930 .layer p { margin: 0; color: var(--ink-soft); }
.r930 .layer .chips { margin-top: .5rem; line-height: 1.9; }
.r930 .principles { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; margin: 1.5rem 0; }
.r930 .principle { border-top: 2px solid var(--accent); padding-top: .75rem; }
.r930 .principle h3 { margin: 0 0 .4rem; }
.r930 .chip { display: inline-block; font-family: var(--mono); font-size: .8rem; color: var(--signal); background: var(--signal-dim); padding: .1rem .5rem; border-radius: 999px; }
.r930 .colophon { color: var(--ink-faint); font-family: var(--mono); font-size: .8rem; margin-top: 2.5rem; }
@media (max-width: 40rem) { .r930 .layer { grid-template-columns: 1fr; } .r930 dl.machine { grid-template-columns: 1fr; } }

/* ---- admin ---- */
.table-wrap { overflow-x: auto; margin: 1rem 0 2rem; }
table { width: 100%; border-collapse: collapse; font-size: .95rem; }
th, td { text-align: left; padding: .55rem .6rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { color: var(--ink-soft); font-weight: 500; white-space: nowrap; }
.actions { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.actions form { margin: 0; }
.state { font-family: var(--mono); font-size: .8rem; padding: .1rem .5rem; border: 1px solid transparent; border-radius: 999px; background: var(--panel); color: var(--ink-soft); white-space: nowrap; }
.state.live { background: var(--signal-dim); color: var(--signal); }
.field { margin: 0 0 1.1rem; }
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 0 1.25rem; }
.field.check label { display: flex; align-items: center; gap: .5rem; font-weight: 400; }
.field.check input { width: auto; }
label { display: block; font-weight: 500; margin-bottom: .3rem; }
/* A control inside a table row sits beside its button, not above it: inputs are
   width:100% by default, which pushed every Rename and Move onto a second line
   and made the rows twice as tall as their content. */
td .buttons input, td .buttons select { width: auto; max-width: 12rem; flex: 0 1 auto; }
input, select, textarea { width: 100%; font: inherit; color: var(--ink); background: var(--surface); border: 1px solid var(--field); border-radius: 4px; padding: .5rem .65rem; min-height: 2.5rem; }
input[readonly] { color: var(--ink-soft); background: var(--panel); }
textarea { font-family: var(--mono); font-size: .9rem; line-height: 1.45; min-height: 24rem; resize: vertical; tab-size: 2; }
@media (hover: none) { textarea { font-size: 1rem; } }
/* The same disclosure for an add-form, a settings block and a box's facts: closed as rendered, opened by script where the viewport is a desktop's. */
.fold > summary { list-style: none; cursor: pointer; font-weight: 500; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "+"; content: "+" / ""; font-family: var(--mono); color: var(--ink-faint); margin-right: .5rem; }
.fold[open] > summary::before { content: "\2212"; content: "\2212" / ""; }
.fold[open] > summary { margin-bottom: .6rem; }
.fold { margin: 0 0 1.25rem; }
time.utc { white-space: nowrap; }
/* A sentence for a touch device, and one for a mouse; the stylesheet picks so the page need not. */
.touch-only, form.touch-only { display: none; }
@media (hover: none) and (pointer: coarse) { .touch-only { display: block; } .mouse-only, form.mouse-only { display: none; } form.touch-only { display: flex; } }
/* The hub's layout sets data-touch by the overlay's broader test, and it wins over the query above, which is
   only the fallback without script (the second read, 2026-09-14). */
html[data-touch="1"] .touch-only { display: block; }
html[data-touch="1"] .mouse-only { display: none; }
html[data-touch="0"] .touch-only { display: none; }
html[data-touch="0"] .mouse-only { display: block; }
small { display: block; color: var(--ink-soft); margin-top: .35rem; font-size: .85rem; }
.field-error { display: block; color: var(--danger); font-size: .9rem; margin-top: .3rem; }
.field-error:empty { display: none; }
button, .button { display: inline-block; font: inherit; font-weight: 500; color: var(--on-fill); background: var(--accent); border: 1px solid transparent; border-radius: 4px; padding: .45rem 1rem; min-height: 2.5rem; line-height: 1.4; cursor: pointer; text-decoration: none; }
/* The field's border, not the rule's: at the rule the most-used control on the tier was 1.07:1 against the paper (the second read, 2026-09-14). */
button.secondary, .button.secondary { color: var(--ink); background: var(--panel); border: 1px solid var(--field); }
button.danger, .button.danger { background: var(--danger); }
/* A destructive act on a list: the secondary look with the danger colour on its word; the filled red is the confirmation page's. */
button.danger-quiet, .button.danger-quiet { color: var(--danger); background: var(--panel); border: 1px solid var(--field); }
button:hover, .button:hover { filter: brightness(1.08); }
/* A quiet button hovers by its edge: brightened, its near-white fill read as the chosen one. */
button.secondary:hover, .button.secondary:hover, button.danger-quiet:hover, .button.danger-quiet:hover { filter: none; border-color: var(--ink-soft); }
.buttons { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; }
.buttons small { flex-basis: 100%; }
.upload { padding: 1rem 1.25rem; background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; margin-bottom: 1.5rem; }
.revisions { margin-top: 2.5rem; }
@media (max-width: 40rem) {
  table, thead, tbody, tr, th, td { display: block; }
  thead { position: absolute; left: -999px; }
  tr { border-bottom: 1px solid var(--rule); padding: .5rem 0; }
  td { border: 0; padding: .25rem 0; }
  td[data-label]::before { content: attr(data-label) ": "; color: var(--ink-soft); font-family: var(--mono); font-size: .8rem; }
  td[data-label="Actions"]::before { content: ""; }
}

/* ---- manage (SDD-020): the estate dashboard. Same tokens; status is always a
   word beside a colour, never the colour alone; a locked control is a disabled
   button with its reason on the page, not a styled link. ---- */
.estate-banner { padding: .85rem 1.1rem; border: 1px solid var(--rule); border-radius: 6px; background: var(--surface); margin: 0 0 1.5rem; }
.estate-banner.loud { border: 2px solid var(--danger); background: var(--accent-dim); }
.estate-banner .headline { font-family: var(--sans); font-weight: 600; font-size: 1.05rem; margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; }
.estate-banner.loud .headline { color: var(--danger); font-size: 1.25rem; }
/* An attention banner (no stamp, clock skew) keeps the distinction the cards draw: accent frame, ink headline, not the danger red. */
.estate-banner.loud.health-attention { border-color: var(--accent); }
.estate-banner.loud.health-attention .headline { color: var(--ink); }
.estate-banner .detail { margin: .35rem 0 0; color: var(--ink-soft); font-family: var(--mono); font-size: .82rem; }
.estate-banner .reason { margin: .5rem 0 0; }
.locked-reason { color: var(--ink-soft); font-size: .9rem; margin: 0 0 1.5rem; }
/* Rows with a disclosure (SDD-027 phase 6): the name and the facts on one line, the identifier
   and the actions under it when opened. Native <details>, no script. */
.rows { list-style: none; padding: 0; margin: 1rem 0 1.5rem; background: var(--surface); border: 1px solid var(--rule); border-radius: 6px; }
.rows .row { border-bottom: 1px solid var(--rule); }
.rows .row:last-child { border-bottom: 0; }
.rows summary { display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .75rem; padding: .55rem .8rem; cursor: pointer; list-style: none; }
.rows summary::-webkit-details-marker { display: none; }
.rows summary::before { content: "+"; content: "+" / ""; font-family: var(--mono); color: var(--ink-faint); width: 1ch; flex: 0 0 auto; }
.rows details[open] summary::before { content: "\2212"; content: "\2212" / ""; }
.rows .row-name { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.rows .row-sub { color: var(--ink-soft); font-size: .85rem; margin-left: auto; }
.rows .row-sub time { font-family: var(--mono); font-size: .8rem; }
.rows .row-more { padding: .2rem .8rem .8rem 2.3rem; display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: center; }
.rows .row-more form { margin: 0; }
.rows .row-more .buttons input, .rows .row-more .buttons select { width: auto; max-width: 12rem; flex: 0 1 auto; }
.rows .row-id { flex-basis: 100%; margin: 0; color: var(--ink-soft); font-size: .85rem; overflow-wrap: anywhere; }
.rows .row-id .tag { font-family: var(--sans); font-size: inherit; }
@media (max-width: 40rem) { .rows .row-sub { margin-left: 0; flex-basis: 100%; padding-left: 1.55rem; } }
/* An operator's notice on the hub: one line, a pip, a link to the page that owns it (SDD-027 phase 4). */
.notice { color: var(--ink-soft); font-size: .88rem; margin: 0 0 .75rem; }
.notice + .notice { margin-top: -.25rem; }
.concerns { margin: 0 0 1.5rem; }
.concerns p { margin: 0; color: var(--ink-soft); }
.concerns .lead { color: var(--ink); font-weight: 500; }
.concerns ul { list-style: none; padding: 0; margin: .4rem 0 0; display: flex; flex-wrap: wrap; gap: .4rem .9rem; }
.concerns li { margin: 0; }
.jump { display: flex; flex-wrap: wrap; gap: .5rem .9rem; margin: 0 0 .5rem; font-size: .92rem; }
.jump a { text-decoration: none; color: var(--ink); display: inline-block; padding: .15rem 0; }
.jump a:hover { color: var(--accent); }
.cards-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); gap: 1rem; margin: 1rem 0 2.5rem; align-items: start; }
.boxes-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: 1rem; margin: 1rem 0 2.5rem; align-items: start; }
.mcard, .box { background: var(--surface); border: 1px solid var(--rule); border-left-width: 5px; border-radius: 6px; padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: .65rem; min-width: 0; }
.health-ok { border-left-color: var(--signal); }
.health-attention { border-left-color: var(--accent); }
.health-urgent { border-color: var(--danger); border-left-color: var(--danger); background: var(--accent-dim); }
.health-unknown { border-left-color: var(--ink-faint); }
.mcard h3, .box h3 { margin: 0; display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; font-size: 1.05rem; }
.box h3 .role { font-weight: 400; color: var(--ink-soft); font-size: .88rem; }
.box h3 .name { overflow-wrap: anywhere; }
.mcard .headline { margin: 0; }
.pip { display: inline-block; font-family: var(--mono); font-size: .75rem; font-weight: 500; padding: .05rem .5rem; border: 1px solid transparent; border-radius: 999px; white-space: nowrap; vertical-align: middle; }
.pip.ok, .pip.fresh { background: var(--signal-dim); color: var(--signal); }
.pip.attention { background: var(--accent-dim); color: var(--ink); }
.pip.urgent { background: var(--danger); color: var(--paper); }
.pip.unknown { background: var(--panel); color: var(--ink-soft); }
h1 .pip { font-size: .8rem; }
.facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .3rem 1rem; margin: 0; font-size: .92rem; }
.facts > div { display: contents; }
.facts dt { color: var(--ink-soft); font-family: var(--mono); font-size: .8rem; padding-top: .15rem; }
.facts dd { margin: 0; overflow-wrap: anywhere; }
meter { display: block; width: 100%; height: .6rem; margin: .25rem 0 .15rem; }
.meter-text { font-family: var(--mono); font-size: .8rem; color: var(--ink-soft); }
.box .fold { margin: 0; }
.box .fold > summary { font-weight: 400; color: var(--ink-soft); font-size: .9rem; }
.box .fold[open] > summary { margin-bottom: .5rem; }
.box .backups { list-style: none; padding: .5rem 0 0; margin: 0; border-top: 1px solid var(--rule); font-size: .88rem; display: grid; gap: .2rem; }
.box .backups li { display: flex; justify-content: space-between; gap: .75rem; margin: 0; }
.box .backups .copy { color: var(--ink-soft); font-family: var(--mono); font-size: .8rem; }
.waiting { color: var(--ink-soft); font-style: italic; font-size: .9rem; margin: 0; }
.mcard .note { color: var(--ink-soft); font-size: .85rem; margin: 0; }
.mcard .link, .box .link { margin: 0; font-size: .92rem; }
.locked { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin-top: auto; padding-top: .65rem; border-top: 1px dashed var(--rule); }
.locked button[disabled] { background: var(--panel); color: var(--ink-soft); border: 1px dashed var(--ink-faint); cursor: not-allowed; min-height: 2.1rem; padding: .3rem .75rem; font-size: .9rem; }
.locked button[disabled]:hover { filter: none; }
.locked button[disabled].last-resort { border-color: var(--danger); color: var(--danger); }
.locked .lock-tag { flex-basis: 100%; font-family: var(--mono); font-size: .75rem; color: var(--ink-soft); } /* ink-faint fails AA on the urgent tint */
/* The panel's controls once they do something (SDD-020 s12). A live control is an
   ordinary button in a one-field form; a locked one keeps the dashed outline and now
   carries its own reason, because a page-wide footnote does not say WHICH button and why. */
.actions-row { display: flex; flex-wrap: wrap; gap: .4rem .5rem; align-items: flex-start; margin-top: auto; padding-top: .65rem; border-top: 1px dashed var(--rule); }
.actions-row form { margin: 0; }
.actions-row button { min-height: 2.1rem; padding: .3rem .75rem; font-size: .9rem; }
.actions-row .locked-control { display: flex; flex-direction: column; gap: .2rem; }
.actions-row .locked-buttons { display: flex; flex-wrap: wrap; gap: .35rem; }
.actions-row .locked-control button[disabled] { background: var(--panel); color: var(--ink-soft); border: 1px dashed var(--ink-faint); cursor: not-allowed; }
.actions-row .locked-control button[disabled]:hover { filter: none; }
.actions-row .locked-control button[disabled].last-resort { border-color: var(--danger); color: var(--danger); }
.actions-row .locked-control small { margin: 0; font-family: var(--mono); font-size: .72rem; color: var(--ink-soft); line-height: 1.3; max-width: 11rem; }
th[scope="row"] { color: var(--ink); font-weight: 500; white-space: normal; width: 12rem; }
@media (max-width: 40rem) {
  .cards-grid, .boxes-grid { grid-template-columns: 1fr; }
  /* Two columns still: the values are short, and label-over-value put the first box five screens down. */
  .facts { grid-template-columns: minmax(5rem, 38%) minmax(0, 1fr); gap: .15rem .6rem; }
  .facts dt { overflow-wrap: anywhere; }
  .box .backups li { flex-direction: column; gap: 0; }
  th[scope="row"] { width: auto; padding-bottom: 0; padding-left: 0; }
  th[scope="row"] + td { padding-top: .1rem; }
  /* The container page's facts stay two columns on a phone, as the boxes' do (the second read, 2026-09-14). */
  .facts-table tr { display: grid; grid-template-columns: minmax(5rem, 38%) minmax(0, 1fr); gap: 0 .6rem; }
  .facts-table th[scope="row"], .facts-table th[scope="row"] + td { padding: .1rem 0; }
}

/* The estate's own screen, embedded (homelab SDD-024). 16:9 so it holds its
   shape at any width, and it must scroll nothing sideways on a phone - the frame
   shrinks, the aspect stays. */
.screen-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin: 1rem 0 1.25rem;
  background: var(--panel);
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
}
.screen-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* The desktop, opened over whatever was clicked in the feed (SDD-024 §11).
   Nearly the whole viewport, because the point is to watch it.

   Deliberately NOT a <dialog>: a closed dialog is display:none, which unloads
   the frame's rendering and drops the stream, so the desktop could only start
   connecting on the click - about eight seconds of black. This is laid out and
   PAINTING the whole time and merely transparent, so the session is already up
   when the click comes (SDD-024 §11.4).

   Which is why the two properties below are load-bearing and neither may become
   display:none or visibility:hidden. `inert` on the element keeps it out of the
   focus order and the accessibility tree to match. */
.screen-overlay {
  position: fixed; inset: 0; z-index: 60;
  display: flex; flex-direction: column;
  background: var(--surface); color: var(--ink);
  opacity: 0; pointer-events: none;
  transition: opacity .12s ease-out;
}
.screen-overlay.open { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .screen-overlay { transition: none; } }
.screen-bar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: .5rem .85rem; border-bottom: 1px solid var(--rule); }
.screen-body { position: relative; flex: 1 1 auto; min-height: 0; background: #000; }
.screen-body iframe { width: 100%; height: 100%; border: 0; display: block; }
/* The player's own controls (SDD-025 §6.2) are a phone feature: the bar that drives the
   streamed player. A computer drives that player directly through the frame, so the bar does
   not exist there at all (his call, SDD-025 §14.7). It stays hidden until the script finds a
   touch device and marks the overlay `has-controls` - a media query missed a stylus phone that
   reports pointer:fine, so the test moved into the script (SDD-025 §14.9). */
.screen-controls { display: none; }
/* In fullscreen a computer keeps the top bar: hidden, it took Stop watching and the keyboard's focus
   with it, and nothing on the page said Escape twice (the second read, 2026-09-14). A touch device
   floats it and auto-hides it with the controls, so Close stays reachable (SDD-025 §14.7). */
/* The picture's own gesture target (the frame is a cross-origin desktop that forwards no touch
   here): a tap plays/pauses, a double-tap on a half skips ten seconds (SDD-025 §14.13). Shown
   only on a touch device; touch-action: manipulation stops the browser zooming on a double-tap. */
.screen-tap { position: absolute; inset: 0; z-index: 1; display: none; touch-action: manipulation; }
/* Nothing playing: the page on the screen takes the taps itself (SDD-004 §5.5, the screens). */
.screen-tap.through { pointer-events: none; }
/* On a touch device (overlay marked `has-controls`) the bar floats over the foot of the picture
   and slides away while the video plays (chrome-up), summoned by a tap, so it never obstructs
   the video (SDD-025 §14.6, §14.7). A slim, translucent strip so the picture shows through it
   (SDD-025 §14.11); the top bar joins the auto-hide in fullscreen. */
.screen-overlay.has-controls .screen-tap { display: block; }
.screen-overlay.has-controls .screen-controls {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: flex; align-items: center; gap: .45rem; padding: .3rem .5rem;
  background: var(--panel);
  background: color-mix(in srgb, var(--panel) 74%, transparent);
  opacity: 0; transform: translateY(100%); pointer-events: none;
  transition: opacity .2s ease, transform .2s ease;
}
.screen-overlay.has-controls .screen-controls[hidden] { display: none; }
.screen-overlay.has-controls.chrome-up .screen-controls { opacity: 1; transform: none; pointer-events: auto; }
.screen-overlay.has-controls:fullscreen .screen-bar {
  position: absolute; left: 0; right: 0; top: 0; z-index: 3; display: flex;
  background: var(--surface); opacity: 0; pointer-events: none; transition: opacity .2s ease;
}
.screen-overlay.has-controls:fullscreen.chrome-up .screen-bar { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .screen-controls { transition: none; } }
/* The gesture feedback: a brief centred flash of what a tap did - play, pause, or the skip
   (SDD-025 §14.13). The site's own tokens, over the picture. */
.screen-fb {
  position: absolute; left: 50%; top: 50%; z-index: 4;
  transform: translate(-50%, -50%) scale(.92);
  padding: .45rem 1rem; border-radius: 10px; border: 1px solid var(--rule);
  background: var(--panel);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  color: var(--ink); font: 600 1.05rem var(--sans); font-variant-numeric: tabular-nums; white-space: nowrap;
  pointer-events: none; opacity: 0; transition: opacity .15s ease, transform .15s ease;
}
.screen-fb.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
@media (prefers-reduced-motion: reduce) { .screen-fb { transition: none; } }
.pc-time { font: 500 .85rem var(--mono); color: var(--ink-soft); font-variant-numeric: tabular-nums; white-space: nowrap; }
.pc-seek { flex: 1 1 auto; min-width: 4rem; accent-color: var(--accent); height: 1.7rem; }
.watch-item .watch-there { font-size: .85rem; min-height: 2.5rem; padding: .3rem .7rem; }
/* With the board open a game on is its card; the row would be the same game twice. */
main:has(#scores-fold[open]) .watch-item.live { display: none; }
.watch-item.seen .meta .pip.ok { background: var(--panel); color: var(--ink-soft); }

/* The paste box (homelab SDD-025 §4): one field and its button on one line at any
   width, the answer underneath. The subscribe form it reveals is a flex row, and a
   flex row ignores the hidden attribute unless told to. */
/* The top margin is the gap below the profile picker, which has none of its own:
   the paragraph that followed it before brought a top margin, and this form does not. */
.take { margin: .3rem 0 1.25rem; }
@media (max-width: 40rem) { .who button { font-size: .82rem; min-height: 2.1rem; padding: .25rem .7rem; } }
/* Labels on the hub are words, not values: Sans. The pips, the times and the identifiers keep the mono (SDD-027 phase 2). */
.watch-nav, .who .tag, .take .tag, .concerns .tag, .seen-toggle .tag, .kind-switches .tag, .buttons > small.tag { font-family: var(--sans); }
.who .tag, .take .tag, .concerns .tag { font-size: .88rem; }
.take-row { display: flex; gap: .5rem; }
.take-row input { flex: 1 1 auto; min-width: 0; }
.take-row button { flex: 0 0 auto; }
.take-said { display: block; margin-top: .35rem; color: var(--ink-soft); font-size: .88rem; }
.take-said:empty { display: none; }
.take-subscribe { margin: -.6rem 0 1.25rem; }
.take-subscribe[hidden] { display: none; }
.take-subscribe select { width: auto; max-width: 100%; }

/* ---- the hub's Settings (2026-09-14): what was above the feed's rows, and the ways into the editors ---- */
/* Whose view a page is, in its heading: quieter than the title, the name a link to where it is chosen. */
/* The space before "as" is text, so the heading reads "Feed as Brandon" and not "Feedas Brandon". */
.as { font-family: var(--sans); font-size: .95rem; font-weight: 400; color: var(--ink-soft); margin-left: .2rem; white-space: nowrap; }
.as a { color: inherit; }
/* A page reached from Settings says so above its heading, one step back. */
.crumb { font-family: var(--sans); font-size: .88rem; margin: 0 0 .25rem; }
.crumb a::before { content: "\2190\00a0"; content: "\2190\00a0" / ""; }
.settings-links { list-style: none; padding: 0; margin: .5rem 0 1.5rem; border-top: 1px solid var(--rule); }
.settings-links li { margin: 0; border-bottom: 1px solid var(--rule); }
.settings-links a { display: flex; flex-wrap: wrap; align-items: baseline; gap: .15rem .75rem; padding: .65rem .2rem; color: var(--ink); text-decoration: none; }
.settings-links a:hover .row-name { color: var(--accent); }
.settings-links .row-name { font-weight: 600; }
.settings-links .row-sub { color: var(--ink-soft); font-size: .88rem; }
.hub section + section h2 { margin-top: 1.75rem; }

/* The House area (homelab SDD-026 §17.5): rows in the hub's register. A row is a name, what the
   device last reported as a word beside a colour, and an On and an Off; the one that holds is
   marked as the hub marks a pressed switch. */
.house-room h2 { font-family: var(--sans); font-size: 1rem; margin: 1.5rem 0 .25rem; }
.house-list { list-style: none; padding: 0; margin: 0 0 1.5rem; border-top: 1px solid var(--rule); }
.house-device { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .2rem .75rem; align-items: center; padding: .6rem .2rem; border-bottom: 1px solid var(--rule); }
.house-device[hidden] { display: none; }
.house-device .row-name { font-weight: 600; min-width: 0; overflow-wrap: anywhere; }
.house-device .house-state { grid-column: 1; grid-row: 2; }
.house-device .house-set { grid-column: 2; grid-row: 1 / span 2; margin: 0; flex-wrap: nowrap; gap: .4rem; }
.house-device .house-set button { min-width: 3.5rem; min-height: 2.75rem; }
.house-device .house-set button[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); font-weight: 600; }
.house-device .house-set button[disabled] { background: var(--panel); color: var(--ink-soft); border: 1px dashed var(--ink-faint); cursor: not-allowed; }
.house-device .house-set button[disabled]:hover { filter: none; }
.house-device .house-state .tag { font-size: .78rem; }
.house-device .house-value { font-family: var(--mono); font-size: .95rem; }
.house-device .house-value[hidden], .house-device .pip[hidden] { display: none; }
.house-device .locked-reason { grid-column: 1 / -1; margin: 0; }
header.site.hub .hub-admin a { color: inherit; text-decoration: none; }
header.site.hub .hub-admin a:hover { color: var(--accent); }
