/*
 * The account site. Every colour here is a custom property out of tokens.css, which
 * tools/theme-audit.mjs generates from app/src/theme/tokens.ts — the TV's own palette. That is
 * the whole point of P113: a viewer scans a pairing code off their own TV and lands here, and a
 * page in a different colour family is the exact signal a phishing page gives. There are no hex
 * literals below, and the audit fails if one appears.
 *
 * The pairs, all measured by the audit at 7:1 for text and 3:1 for an edge (CLAUDE.md law 1):
 * body ink-body on surface-page 16.0:1, the note ink-soft on surface-raised 9.5:1, and a primary
 * button's white bold label on brand-cta 4.6:1 (large text, the audit's own darkening step).
 *
 * P180: the site wears the TV's blue (DESIGN.md §2). Gold (--meaning-accent) is retired from
 * decoration and survives only on a rule that means "needs attention" (.warn, .trouble) — the
 * audit fails any other rule that wears it. Blue is never body-size text: brand-blue on the card is
 * 4.1:1, under the 7:1 bar, so a link is ink-bright with a blue underline and blue icon, and blue
 * text is only ever large (the headline line 5.5:1, the price 5.1:1, both past the 4.5:1 bar).
 */

/* P230: Inter, the face of Joe's mockups and of the TV app (tokens.css --font-text). Served from this site's own
 * /fonts (latin subset, ~24 KB a weight, SIL OFL — /fonts/LICENSE.txt), never a font CDN: a third-party request
 * would tell someone else who is reading an account page. */
@font-face { font-family: Inter; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/inter-400.woff2") format("woff2"); }
@font-face { font-family: Inter; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/inter-500.woff2") format("woff2"); }
@font-face { font-family: Inter; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/inter-600.woff2") format("woff2"); }
@font-face { font-family: Inter; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/inter-700.woff2") format("woff2"); }
/* Outfit (Joe, 2026-09-29), the display face: headlines and the brand name only (tokens.css --font-display).
 * One variable file, weights 500–700, latin, ~32 KB, same origin — SIL OFL, /fonts/LICENSE-outfit.txt. */
@font-face { font-family: Outfit; font-style: normal; font-weight: 500 700; font-display: swap; src: url("/fonts/outfit.woff2") format("woff2"); }

* { box-sizing: border-box; }

body {
  margin: 0;
  padding: 1.25rem 1rem 6vh;
  background: var(--surface-page);
  color: var(--ink-body);
  font: 17px/1.55 var(--font-text);
}

.card {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.75rem 1.75rem 1.5rem;
  background: var(--surface-raised);
  border-radius: var(--radius-card);
}

/* Two faces, one job each: Outfit says what a page is and who made it (h1–h3, the footer sign-off);
   Inter carries everything read or pressed — body, buttons, links, labels. */
h1, h2, h3, .foot-sign { font-family: var(--font-display); }
h1 { margin: 0 0 1rem; font-size: 1.6rem; line-height: 1.2; font-weight: 600; }
h2 { margin: 0 0 .5rem; font-size: 1.05rem; font-weight: 600; }
/* P293: the one eyebrow, the TV kicker's own size, ink and tracking (tokens.ts type.eyebrow, eyebrowTracking). An h2 is a
   heading and says so in sentence case; a label over a heading or a column wears this class, and nothing else is uppercase. */
.eyebrow { font-family: var(--font-text); font-size: var(--type-eyebrow); font-weight: 700; letter-spacing: var(--eyebrow-tracking); text-transform: uppercase; color: var(--ink-muted); }

p { margin: 0 0 1rem; }
.lede { font-size: 1.15rem; font-weight: 400; color: var(--ink-soft); }
.who { color: var(--ink-bright); margin-top: -.5rem; }
.note { color: var(--ink-soft); font-size: .95rem; }
.ok { color: var(--ink-bright); }
.ok .icon { color: var(--brand-blue); }

/* A warning is a notice card (see .trouble below): panel, a border just warmed by gold, white words, a gold "!" circle. */
.warn {
  position: relative;
  margin: 0 0 1.25rem;
  padding: 1rem 1.1rem .3rem 3.4rem;
  background: color-mix(in srgb, var(--meaning-accent) 16%, var(--surface-card));
  border-radius: 16px;
  color: var(--ink-body);
}
.warn > p:first-child { color: var(--ink-bright); font-weight: 600; }
.warn h2 { color: var(--ink-bright); }
.warn h2 .icon { color: var(--meaning-accent); }
/* A card that brings its own icon (a heading glyph, the dashboard's circle) draws no second one. */
.warn:has(> h2, > .glyph, > p:first-child > .icon) { padding-left: 1.1rem; }
.warn:has(> h2, > .glyph, > p:first-child > .icon)::before { display: none; }
.warn > p:first-child > .icon { color: var(--meaning-accent); }

.row { display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: 1rem; }

input[type=email] {
  flex: 1 1 14rem;
  min-width: 0; max-width: 100%; /* P115: size="32" must not push the card wider than a zoomed phone */
  padding: .7rem .8rem;
  font: inherit;
  color: var(--ink-bright);
  background: var(--surface-night);
  border: 1px solid var(--line-edge);
  border-radius: var(--radius-card);
}
input[type=email]:focus { outline: 3px solid var(--brand-blue); outline-offset: 1px; }
input[type=email]::placeholder { color: var(--ink-soft); }

input[type=submit], button, .button {
  display: inline-block;
  max-width: 30rem;
  padding: .7rem 1rem;
  font: inherit;
  font-size: 1.17rem; /* 18.7 px bold: WCAG large text, the bar white-on-CTA is audited at */
  font-weight: 700;
  text-align: left;
  color: var(--ink-white);
  background: var(--brand-cta);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}
.warn button { margin-bottom: 1rem; }
a.button { text-decoration: none; display: inline-block; }

/* Sign out is a real form (button_to), so it has to be un-styled back into a link. */
form.link { margin-top: 1.5rem; }
form.link button {
  padding: 0;
  color: var(--ink-soft);
  background: none;
  font-size: 1rem;
  font-weight: 400;
  text-decoration: underline;
}

/* ---------------------------------------------------------------- the frame every page wears
   P095 put these rules in an inline <style> in the layout to keep out of another lane's way.
   P113 moved them here: one stylesheet is the point, and a second private block of rules is
   how the two surfaces drifted apart in the first place. */
.site, .foot { max-width: 34rem; margin: 0 auto; }
.site { display: flex; align-items: center; gap: .6rem 1rem; flex-wrap: wrap; margin-bottom: 1.25rem; padding: 0 .25rem; }
.site .mark { color: var(--ink-bright); font-size: 1.3rem; font-weight: 700; letter-spacing: .01em; text-decoration: none; }
.site .what { color: var(--ink-muted); font-size: .95rem; }
/* One quiet row under a hairline: the sign-off on the left, three plain links on the right (wrapping under
   it on a narrow phone). No icon on a link — Joe, 2026-09-29. The sign-off is the icon set's recliner,
   the same chair the demo remote wears. */
.foot { margin-top: 2rem; padding: 1rem .25rem 1.5rem;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem 1.5rem;
  color: var(--ink-muted); font-size: .9rem; }
/* Icon + name as one lockup (Joe, 2026-09-29: "a thinner line version that blends with that text"): the
   glyph is sized in em so it tracks the name, 1.35x the font so the chair's base sits on the baseline and
   its headrest clears the cap height; its stroke is thinned to ~the stem of Inter 500 at this size, and
   it is nudged up .06em because the drawing's mass sits low in its 24 box. */
.foot-sign { display: inline-flex; align-items: center; gap: .35em; font-weight: 500; letter-spacing: .01em; }
.foot-sign .icon { width: 1.35em; height: 1.35em; stroke-width: 1.5px; vertical-align: 0; transform: translateY(-.06em); }
.foot-links { display: flex; gap: 1.25rem; }
.foot a { color: var(--ink-muted); text-decoration: none; }
.foot a:hover { color: var(--ink-bright); text-decoration: underline; text-decoration-color: var(--brand-blue); }
.promise { margin-bottom: 1.5rem; }

/* A link inside the card was the browser's own blue on the card — 2.2:1, unreadable at arm's
   length and nothing to do with this site. The accent is 8.5:1 on the card. */
main a { color: var(--ink-bright); text-decoration-color: var(--brand-blue); text-decoration-thickness: 2px; text-underline-offset: .18em; }
main a .icon { color: var(--brand-blue); }
/* ...except on a link dressed as a button: there the icon is the label's own white (blue on the CTA vanished). */
main a.button .icon { color: inherit; }
/* P293: sentence case now, so a section heading carries its rank in size, a step over a card title (1.1rem). */
.pages-h2 { margin-top: 1.75rem; color: var(--ink-bright); font-size: 1.4rem; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---------------------------------------------------------------------------- P115: the icons
   Every glyph is inline SVG drawn in currentColor (server/app/helpers/icons_helper.rb, GENERATED
   from the TV's own app/src/ui/icons/paths.ts), so an icon is always exactly the colour of the
   text it sits beside and no pair can drift out of P113's palette. Nothing is fetched to draw one.
   Sized in `em` so it tracks its line rather than a fixed pixel size — at 200% browser zoom the
   glyph grows with the sentence, and with images blocked there is nothing to block. */
.icon {
  width: 1.05em;
  height: 1.05em;
  vertical-align: -.16em;
  flex: 0 0 auto;
}
h1 .icon, h2 .icon { vertical-align: -.1em; }
/* The lockup: the recliner, then the name, on one baseline. */
.site .mark { display: inline-flex; align-items: center; gap: .42rem; }
.site .mark .icon { width: 1.25rem; height: 1.25rem; vertical-align: 0; }
.site .what .icon { color: var(--ink-muted); }
.foot a .icon { opacity: .95; }
/* A button's icon is inside the button's own ink, so it needs no colour of its own. */
button .icon { vertical-align: -.16em; }
/* A link that carries an icon lays out as a row, so the space before the label is dropped and the
   underline runs under the words only, not under the gap beside the glyph. */
main a:has(> .icon), .foot a:has(> .icon) { display: inline-flex; align-items: center; gap: .35em; }

/* ---------------------------------------------------------------- the front page (P114)
   Every other page on this site is one 34 rem card of prose, and that is right for /privacy and
   /terms. The front page is not prose: it is a headline, a picture of the product, three cards and
   a price, and on a 1280 px viewport a 34 rem column left ~60% of the screen empty (measured off
   _shots/audit-home-desk.png, 2026-09-21). So the home page widens — and the frame above and below
   it widens with it, through :has(), rather than by giving the layout a second set of rules. */
body:has(.home) .site,
body:has(.home) .foot { max-width: 62rem; }

.home { max-width: 62rem; margin: 0 auto; }

/* The headline is the product. It is the only thing on the site allowed to be this big, and the
   accent line is the claim no competitor can make, so it is the line that is coloured. */
.hero h1 {
  margin: 0 0 .75rem;
  font-size: clamp(2rem, 6vw, 3.1rem);
  line-height: 1.08;
  letter-spacing: -.01em;
}
.hero .hl { color: var(--brand-blue); }
.hero .lede {
  max-width: 40rem;
  margin: 0 0 1.5rem;
  color: var(--ink-soft);
  font-size: clamp(1.05rem, 2.4vw, 1.3rem);
  font-weight: 400;
  line-height: 1.45;
}
/* The screenshot carries its own aspect ratio, so it never reflows the page as it loads. */
.hero .shot {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line-edge);
  border-radius: var(--radius-card);
  background: var(--surface-night);
}

/* Three cards, side by side the moment there is room for them — the plan's "not a 34 rem column".
   `auto-fit` + a 16 rem floor does that without a media query and without ever squeezing a card
   narrower than its own longest word. */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
  margin: 2rem 0;
  padding: 0;
  list-style: none;
  container-type: inline-size;
}
/* Persona sweep 2026-10-16 (leaver, a laptop at 200% zoom = a 720 px page; a 768 tablet too): where two cards fit
   and three do not, the third sat alone under the first with an empty card's width beside it. A third and last card
   in that band takes the whole row. 50rem = three 16rem cards + two 1rem gaps; .secrets below is 3 x 14 + 2 x .75. */
@container (max-width: 49.99rem) { .cards > li:nth-child(3):last-child { grid-column: 1 / -1; } }
/* P180: web-dashboard.png's card — flat and rounded, an edge instead of a glow, the icon in a tinted
   circle. The mockup's blur and shadow are left out on purpose (DESIGN.md §1 allows neither). */
.cards li {
  padding: 1.4rem 1.4rem 1.2rem;
  background: var(--surface-section);
  border: 1px solid var(--line-edge);
  border-radius: var(--radius-card);
}
.cards h2 {
  margin: .75rem 0 .5rem;
  color: var(--ink-bright);
  font-size: 1.1rem;
  letter-spacing: 0;
  text-transform: none;
  /* At 1280 the three columns are ~20 rem, and two headings left one word alone on line two. */
  text-wrap: balance;
}
.cards p { margin: 0; color: var(--ink-soft); font-size: 1rem; }

/* P117: each card leads with one of P115's glyphs (the .icon every page draws), set on its own line
   in the tinted circle. It paints in currentColor, so the accent is the colour set here.
   The tinted circle: brand-blue on brand-current is 4.2:1, an audited edge pair. */
.cards .icon {
  display: block;
  box-sizing: content-box;
  width: 1.6rem;
  height: 1.6rem;
  padding: .8rem;
  color: var(--brand-blue);
  background: var(--brand-current);
  border-radius: 50%;
}

/* The price is a line a viewer reads in one glance — the plan's words. It was on no page at all
   before this one, so "cheaper" was being asserted to nobody. */
.price {
  margin: 2rem 0;
  padding: 1.25rem 1.5rem 1rem;
  background: var(--surface-panel);
  border-left: 4px solid var(--brand-blue);
  border-radius: var(--radius-card);
}
.price .amount {
  margin: 0;
  color: var(--brand-blue);
  font-size: clamp(1.8rem, 5vw, 2.4rem);
  font-weight: 700;
  line-height: 1.1;
}
.price .terms { margin: .35rem 0 .5rem; color: var(--ink-body); font-size: 1.1rem; }
.price .note { margin: 0; }

.compare table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}
.compare th, .compare td {
  padding: .7rem .8rem;
  border-bottom: 1px solid var(--line-divider);
  vertical-align: top;
}
.compare tbody th { width: 14rem; color: var(--ink-bright); font-weight: 600; }
.compare td { color: var(--ink-soft); }
.compare .us th, .compare .us td { color: var(--ink-bright); background: var(--surface-panel); }
.compare .us th { color: var(--ink-bright); border-left: 3px solid var(--brand-blue); }
.compare .note { margin-top: 1rem; }

.where { margin-top: 2rem; }

/* Under ~34 rem a two-column table stops being a table: the row header and its cell each get about
   ten characters and every line wraps to one word. So it becomes a list of labelled blocks. */
@media (max-width: 34rem) {
  .compare thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .compare tr { display: block; margin-bottom: .75rem; border-radius: var(--radius-card); overflow: hidden; }
  .compare th, .compare td { display: block; width: auto; border-bottom: 0; }
  .compare tbody th { padding-bottom: .2rem; }
  .compare td { padding-top: 0; border-bottom: 1px solid var(--line-divider); }
}

/* Two things the first pass of P114 got wrong, both read off the rendered page rather than guessed:
   on a 430 px phone `.compare tbody th`'s 14 rem beat the block rule above it (0,1,2 over 0,1,1), so
   the highlighted Recliner row's background stopped two thirds across; and the guide screenshot at
   398 px wide is 1200 px of grid squeezed to a third — intact, and unreadable. Narrow screens get a
   legible LEFT CROP of the same image instead, which is the half with the channel names in it. */
@media (max-width: 34rem) {
  .compare tbody th { width: auto; }
  .hero .shot { aspect-ratio: 16 / 11; object-fit: cover; object-position: left center; }
}
/* P259: the trailer is framed with its captions in it — never cropped. P262: a phone is served the 6:5 cut
   (the <source media> in home.html.erb uses this same 34rem breakpoint). */
.hero video.shot { aspect-ratio: 16 / 9; object-fit: contain; }
@media (max-width: 34rem) {
  .hero video.shot { aspect-ratio: 6 / 5; }
}

/* Graded off the third pass's phone shot: "Apple TV is planned and not released." left "released."
   alone on its own line at 430 px. The plan bars a single orphan word anywhere on this page, so the
   browser balances the last lines of every paragraph instead of a hand-placed &nbsp; per sentence. */
.home p, .home td { text-wrap: pretty; }

/* ------------------------------------------------------------ the header: who, and where to go
   Joe, 2026-09-23: "it's unclear who I'm potentially logged in as". The email sits top right on every
   signed-in page, and the three places an account has are tabs under it. On a phone the tabs take their
   own full-width row, three equal thumbs wide; nothing in the header ever wraps mid-word. */
.site .me { margin-left: auto; display: flex; align-items: center; gap: .6rem; min-width: 0; }
.site .what { color: var(--ink-muted); font-size: .95rem; }
.who-chip {
  display: inline-flex; align-items: center; gap: .4em; min-width: 0; max-width: 15rem;
  padding: .3rem .7rem; border-radius: 999px;
  color: var(--ink-bright); background: var(--surface-card); font-size: .92rem;
}
.who-chip .addr { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who-chip .icon { color: var(--brand-blue); }
form.out { margin: 0; }
form.out button { padding: .3rem .1rem; color: var(--ink-soft); background: none; font-weight: 500; font-size: .92rem; }
form.out button:hover { color: var(--ink-bright); }
/* The header's Sign in is under the large-text size, so it is not a white-on-CTA fill: it wears the
   CURRENT pill instead (ink-bright on brand-current, 13.3:1), which is still blue and still a button. */
.button.small { padding: .45rem .9rem; font-size: .95rem; color: var(--ink-bright); background: var(--brand-current); }
.tabs { flex: 1 0 100%; display: flex; gap: .4rem; }
.tabs a {
  flex: 1 1 0; display: inline-flex; align-items: center; justify-content: center; gap: .4em;
  padding: .6rem .5rem; border-radius: 999px;
  color: var(--ink-soft); background: var(--surface-card); text-decoration: none; font-weight: 500; font-size: .98rem;
}
.tabs a:hover { color: var(--ink-bright); background: var(--surface-raised); }
.tabs a.on { color: var(--ink-bright); background: var(--brand-current); }
.tabs a.on .icon { color: var(--brand-blue); }
@media (min-width: 40rem) { .tabs { flex: 0 1 auto; order: 3; } }

/* ------------------------------------------------------------ the account pages' shared parts
   A page title row, then panels. A panel is the card's own surface one step down, so a page reads as
   a stack of things you can do rather than one long letter. */
.card h1 { display: flex; align-items: center; gap: .5rem; }
.panel {
  margin: 0 0 1rem; padding: 1.25rem 1.4rem;
  background: var(--surface-card); border-radius: 24px;
}
.panel h2 { display: flex; align-items: center; gap: .45rem; margin: 0 0 .45rem; color: var(--ink-bright); font-size: 1.05rem; font-weight: 600; text-transform: none; letter-spacing: 0; }
.panel p:last-child { margin-bottom: 0; }
.stat { display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem; margin: 0 0 1rem; }
.stat div { padding: .7rem .6rem; text-align: center; background: var(--surface-card); border-radius: 16px; }
.stat b { display: block; color: var(--brand-blue); font-size: 1.5rem; line-height: 1.1; }
.stat span { color: var(--ink-soft); font-size: .88rem; }
.pill { display: inline-block; padding: .1rem .55rem; border-radius: 999px; font-size: .85rem; font-weight: 600; background: var(--surface-strip); color: var(--ink-soft); }
.pill.good { color: var(--ink-bright); background: var(--brand-current); }

/* Forms on the account pages: a label over a full-width field. */
.form { display: grid; grid-template-columns: minmax(0, 1fr); gap: .55rem; }
.form label { font-weight: 600; margin-top: .25rem; }
.form input, .form select {
  width: 100%; padding: .7rem .8rem; font: inherit; color: var(--ink-bright);
  background: var(--surface-night); border: 1px solid var(--line-edge); border-radius: var(--radius-card);
}
.form input:focus, .form select:focus { outline: 3px solid var(--brand-blue); outline-offset: 1px; }
.form input::placeholder { color: var(--ink-muted); }
.form .hint { margin: -.2rem 0 .2rem; color: var(--ink-muted); font-size: .88rem; }
.form button { justify-self: start; margin-top: .35rem; }
/* P502: wraps, so "Stalker portal (MAC)" drops to its own row on a narrow phone instead of widening the card. */
.seg { display: flex; flex-wrap: wrap; gap: .4rem; }
.seg label { flex: 1 1 auto; margin: 0; white-space: nowrap; display: flex; align-items: center; justify-content: center; gap: .4rem; padding: .55rem; border-radius: 999px; background: color-mix(in srgb, var(--ink-bright) 8%, transparent); font-weight: 500; cursor: pointer; }
/* `.form input` is a text box's padding, fill, border and square focus ring, and a radio inherited all
   of it: a boxed dot. The segment draws its own round dot and wears the focus ring on the whole row. */
.seg input {
  appearance: none; flex: none; width: 1.1rem; height: 1.1rem; padding: 0; margin: 0;
  border: 2px solid var(--ink-muted); border-radius: 50%; background: transparent;
}
.seg input:checked { border-color: var(--brand-blue); background: radial-gradient(var(--brand-blue) 45%, transparent 50%); }
.seg input:focus { outline: none; }
.seg label:has(input:checked) { color: var(--ink-bright); background: var(--brand-current); }
.seg label:has(input:focus-visible) { outline: 3px solid var(--brand-blue); outline-offset: 1px; }

/* A list of things you own: a playlist, a TV. Name and detail on the left, the action on the right. */
.things { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: .55rem; }
.things li { display: flex; align-items: center; gap: .8rem; padding: .8rem 1rem; background: var(--surface-strip); border-radius: 16px; }
.things .body { flex: 1; min-width: 0; }
.things strong { display: block; color: var(--ink-bright); overflow-wrap: anywhere; }
.things .note { display: block; margin: 0; font-size: .88rem; overflow-wrap: anywhere; }
.things form { margin: 0; }
.things button, button.quiet { padding: .45rem 1rem; font-size: .9rem; color: var(--ink-bright); background: color-mix(in srgb, var(--ink-bright) 9%, transparent); }
.things .icon { color: var(--brand-blue); }
/* Notices (Joe, #tv 2026-10-06, twice: a gold line "hard to see, no icon", then "the yellow and red look bad").
   A notice is a card like every other card on the site — the stat cards, the two-secrets rows: a dark panel, a
   thin border, white words. Only its round icon carries the colour, exactly as the stat cards' circles carry blue:
   gold "!" = read this first (.warn), red "!" = that failed (.trouble). Words are never painted gold or red, and
   there is no thick colour bar. The icon is CSS (no data: SVG — P114/P115), so a script setting textContent keeps it.
   Pairs audited in tokens.ts: ink.bright on surface.panel, meaning.danger on surface.danger, meaning.accent on surface.alert. */
.trouble {
  display: flex; align-items: flex-start; gap: .7rem;
  margin: 0 0 1rem; padding: .8rem 1rem;
  color: var(--ink-bright); font-weight: 600; line-height: 1.45;
  background: color-mix(in srgb, var(--meaning-danger) 10%, var(--surface-card));
  border-radius: 16px;
}
.trouble::before, .warn::before {
  content: "!"; flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 1.6rem; height: 1.6rem; margin-top: -.05rem; border-radius: 50%;
  font: 800 .95rem/1 var(--font-text);
}
.trouble::before { background: color-mix(in srgb, var(--meaning-danger) 22%, var(--surface-danger)); color: var(--meaning-danger); }
.warn::before { position: absolute; left: 1rem; top: 1rem; background: color-mix(in srgb, var(--meaning-accent) 20%, var(--surface-alert)); color: var(--meaning-accent); }
.warn .trouble { margin-top: .25rem; }
/* The passphrase box the error is about turns red too, without a script: a .secret field beside a showing callout.
   A long form's other fields stay as they are — the sentence names the one. */
:has(> .trouble:not(:empty):not([hidden])) > input.secret,
:has(> .trouble:not(:empty):not([hidden])) > .row > input.secret { border-color: var(--meaning-danger); }
.trouble:empty, .trouble[hidden], .ok:empty { display: none; }

.card.narrow { max-width: 28rem; }
button.wide { width: 100%; text-align: center; padding: .85rem 1rem; }
.promise { margin-top: 1.5rem; }
.promise .lede { font-size: 1rem; margin-bottom: .5rem; }
/* P631: the sign-in page's no-email door. Its script flips `hidden` on buttons and blocks whose own rules set a
   display, so inside the door `hidden` wins outright. */
.door [hidden] { display: none !important; }
.door .seg { margin: 0 0 .8rem; }
.door .seg button { flex: 1 1 0; margin: 0; text-align: center; color: var(--ink-soft); }
.door .seg button[aria-pressed=true] { color: var(--ink-bright); background: var(--brand-current); border-color: var(--brand-blue); }
.door .warn { margin: .5rem 0 .2rem; padding-bottom: .8rem; }
.door .warn p { margin: 0 0 .5rem; }
.door .warn p:first-child { font-weight: 700; display: flex; align-items: center; gap: .4rem; }
.door .tick { display: flex; align-items: flex-start; gap: .55rem; margin: .3rem 0 0; color: var(--ink-bright); font-weight: 600; cursor: pointer; }
.door .tick input { flex: none; width: 1.25rem; height: 1.25rem; margin: .1rem 0 0; padding: 0; accent-color: var(--brand-blue); }
.door button.linkish { display: inline; padding: 0; margin: 0; font-size: inherit; font-weight: 600; color: var(--brand-blue); background: none; text-decoration: underline; }
/* Persona sweep 2026-10-15 (no-email-viewer): once the door has made the account, the viewer is signed in and reads
   "Save these now" - the email form, its note and the email-link panel around it ask them to sign in again. */
main:has(#np-saved:not([hidden])) > :is(.note, form, .or, .promise) { display: none; }
.door .saved h3 { display: flex; align-items: center; gap: .4rem; margin: 0 0 .4rem; color: var(--ink-bright); }
.door .saved code#np-saved-name { display: block; width: fit-content; margin: .35rem 0; padding: .4rem .6rem; color: var(--ink-bright); font-size: 1.15rem; white-space: nowrap; background: var(--surface-night); border-radius: var(--radius-card); }
.door button:disabled { opacity: .4; cursor: not-allowed; }
.door .small { margin: .9rem 0 0; font-size: .85rem; }
p.or { display: flex; align-items: center; gap: .7rem; margin: 1.2rem 0; color: var(--ink-muted); font-size: .9rem; }
p.or::before, p.or::after { content: ""; flex: 1; border-top: 1px solid var(--line-divider); }
@media print { body * { visibility: hidden; } #np-saved, #np-saved * { visibility: visible; } #np-saved .row { display: none; } }

/* A published hash is 64 characters with no break in it: its own block, broken anywhere. */
code.sha { display: block; margin: .3rem 0 0; padding: .5rem .65rem; background: var(--surface-night); border-radius: var(--radius-card); color: var(--ink-muted); font-size: .78rem; line-height: 1.45; word-break: break-all; }
.rename { display: flex; gap: .4rem; margin-top: .45rem; }
.rename input { flex: 1; min-width: 0; width: 100%; padding: .4rem .6rem; font: inherit; font-size: .92rem; color: var(--ink-bright); background: var(--surface-night); border: 1px solid var(--line-edge); border-radius: var(--radius-card); }
.things li.col { flex-wrap: wrap; }

/* ------------------------------------------------------------ P127: the signed-in shell
   DESIGN.md §4: a sidebar with the lockup and the account's places, and the page beside it. Under
   900 px it collapses to a top bar (lockup left, avatar and a menu right) and the menu opens the
   same places as a full-height sheet (§4b). The sheet is a <details>: no script on any signed-in page.
   Every pair below is one tokens.ts audits: ink-soft/ink-bright on surface-night and surface-section,
   ink-bright on brand-current (the CURRENT pill, never the white focus fill), white bold on brand-cta,
   brand-blue and line-edge as edges. */
body.shell { padding: 0; }
/* The column paints the night surface too, so the sidebar runs the full height of a long page. */
.frame { display: grid; grid-template-columns: 16.5rem minmax(0, 1fr); min-height: 100vh; }
.side {
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column; padding: 1.5rem 1rem 1.25rem; background: var(--surface-page);
}
.side .mark { display: block; margin: 0 .5rem 1.75rem; }
.mark img { display: block; width: 180px; max-width: 100%; height: auto; }
.sections { display: grid; gap: .3rem; }
.sections a {
  display: flex; align-items: center; gap: .75rem; min-height: 44px; padding: .6rem .9rem;
  color: var(--ink-soft); text-decoration: none; font-weight: 500; border-radius: 999px;
}
.sections a .icon { color: var(--ink-muted); width: 1.2em; height: 1.2em; }
.sections a:hover { color: var(--ink-bright); background: var(--surface-card); }
.sections a.on { color: var(--ink-bright); background: var(--brand-current); }
.sections a.on .icon { color: var(--brand-blue); }
.sections a:focus-visible, .cta:focus-visible, .more summary:focus-visible, .menu summary:focus-visible, .side-account form.out button:focus-visible,
main button:focus-visible, main a:focus-visible {
  outline: 3px solid var(--surface-focus); outline-offset: 2px;
}

.main { min-width: 0; padding: 2rem 2.5rem 2rem; }
.main .foot { max-width: none; margin: 2rem 0 0; }
.bar { display: none; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.bar .mark { display: none; }
.bar .me { margin-left: auto; display: flex; align-items: center; gap: .75rem; }
.bar .who-chip { color: var(--ink-bright); background: var(--surface-section); max-width: 22rem; }
.avatar {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 2rem; height: 2rem; border-radius: 50%;
  color: var(--ink-bright); background: var(--brand-current);
  font-weight: 600; font-size: .95rem;
}
.who-chip .avatar { width: 1.6rem; height: 1.6rem; font-size: .8rem; margin-left: -.35rem; }
.menu { display: none; }

/* The dashboard. Boxes are surface-section with an edge, so every text pair on them is audited. */
.dash { display: grid; gap: 1.1rem; }
.dash h1, .dash h2 { color: var(--ink-bright); }
.dash a:not(.cta) { color: var(--ink-bright); }
.dash a .icon, .dash h2 .icon, .dash .glyph .icon { color: var(--brand-blue); }
.box {
  padding: 1.25rem 1.4rem; background: var(--surface-card); border-radius: 24px;
}
.box h2 { display: flex; align-items: center; gap: .5rem; margin: 0; font-size: 1.1rem; font-weight: 600; letter-spacing: 0; text-transform: none; }
.box p { color: var(--ink-soft); }
.box p:last-child { margin-bottom: 0; }
.box .lede { color: var(--ink-body); font-size: 1rem; margin: .75rem 0 .5rem; }
.box-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .75rem; }
.more-link { display: inline-flex; align-items: center; min-height: 44px; font-size: .95rem; }
.dash p { text-wrap: pretty; }
.crypto-card p > a, .main .foot a { display: inline-flex; align-items: center; min-height: 44px; }

.hero-band {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding: 1.75rem 2rem; border-radius: 24px;
  background: var(--surface-card);
}
.hero-band h1 { margin: 0 0 .5rem; font-size: clamp(1.6rem, 3.4vw, 2.3rem); line-height: 1.15; }
.hero-band p { margin: 0 0 1.25rem; max-width: 34rem; color: var(--ink-soft); }
.hero-band .art { width: 140px; height: auto; flex: 0 0 auto; }
.cta {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem; min-height: 48px;
  padding: .7rem 1.4rem; border-radius: 999px;
  color: var(--ink-white); background: var(--brand-cta); font-weight: 700; font-size: 1.15rem; text-decoration: none;
}
.dash .cta .icon { color: var(--ink-white); }
.cta.wide { display: none; }
/* P629: the share box. The link is a readonly field a long press selects; Text and Email are the phone's own share. */
.share-box .share-link {
  width: 100%; box-sizing: border-box; min-height: 48px; padding: .6rem .8rem; border-radius: var(--radius-card);
  border: 1px solid var(--line-divider); background: transparent; color: var(--ink-bright); font: inherit; font-size: 1.05rem;
  max-width: 36rem; /* persona sweep 2026-10-14 (2560): a ~30-character link in a 1,380 px field */
}
.share-box .share-actions { display: flex; flex-wrap: wrap; gap: .75rem; margin: .9rem 0 .5rem; }
.share-box .cta.ghost { background: color-mix(in srgb, var(--ink-bright) 9%, transparent); color: var(--ink-bright); }

.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; margin: 0; padding: 0; list-style: none; }
.stat-card {
  display: grid; align-content: start; gap: .15rem; padding: 1rem 1.1rem;
  background: var(--surface-card); border-radius: 24px;
}
.stat-card .k { display: flex; align-items: center; gap: .5rem; color: var(--ink-soft); font-size: .95rem; }
.stat-card .v { color: var(--ink-bright); font-size: 1.7rem; line-height: 1.2; }
.stat-card .sub { color: var(--ink-muted); font-size: .88rem; }

.cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 24rem); gap: 1.1rem; align-items: start; }
/* Persona sweep 2026-10-15 (lost-everything, 2560, no TVs): the side cards span three rows and outgrow a short left column; with
   auto rows the slack was split into ~60 px gaps between Devices, Share and Pair. The last row takes it instead. */
.cols { grid-template-rows: auto auto 1fr; }
/* Persona sweep 2026-10-12 (Joe's desktop photo: a screen-wide hole under the devices box beside the tall side
   column). The left column stacks devices, share and pair while the side cards span all three rows, so neither
   column leaves the other a hole. Stacked below 1100 px the source order holds: devices, side cards, share, pair. */
.devices-box, .share-box, #pair { grid-column: 1; }
.side-cards { display: grid; gap: 1.1rem; grid-column: 2; grid-row: 1 / span 3; }
.cols > .share-box, .cols > #pair { margin: 0; }
.devices { list-style: none; display: grid; gap: .5rem; margin: 0 0 .75rem; padding: 0; }
.devices li { display: flex; align-items: center; gap: .75rem; min-height: 56px; padding: .7rem 1rem; background: var(--surface-strip); border-radius: 16px; }
.devices .body { flex: 1; min-width: 0; }
.devices .body > .glyph { float: left; margin: .15rem .75rem 0 0; }
.devices strong { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; color: var(--ink-bright); overflow-wrap: anywhere; }
/* flow-root, not block (P372): beside the floated TV picture a block note's second line ran back under the picture. */
.devices .note { display: flow-root; margin: .1rem 0 0; color: var(--ink-soft); font-size: .9rem; }
.chip { display: inline-block; padding: .05rem .55rem; border-radius: 999px; font-size: .78rem; font-weight: 600; }
.chip.this { color: var(--ink-bright); background: var(--brand-current); }
.dot { display: inline-block; width: .55rem; height: .55rem; margin: 0 .15rem 0 .1rem; border-radius: 50%; background: var(--line-absent); vertical-align: .05em; }
.dot.on { background: var(--brand-cyan); }
.pill.live { color: var(--ink-bright); background: var(--brand-current); }

/* The ⋮ on each device (§4b): rename, or sign that TV out. 44 px square, so a thumb finds it. */
.more { position: relative; flex: 0 0 auto; }
.more summary {
  list-style: none; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  color: var(--ink-soft); font-size: 1.4rem; line-height: 1; border-radius: var(--radius-card); cursor: pointer;
}
.more summary::-webkit-details-marker, .menu summary::-webkit-details-marker { display: none; }
.more[open] summary { color: var(--ink-bright); background: var(--surface-raised); }
.more .pop {
  position: absolute; right: 0; top: 46px; z-index: 5; min-width: 15rem; display: grid; gap: .25rem; padding: .4rem;
  background: var(--surface-raised); border-radius: 16px;
}
.more .pop a, .more .pop button {
  display: flex; align-items: center; min-height: 44px; width: 100%; max-width: none; padding: .5rem .75rem;
  color: var(--ink-body); background: none; font: inherit; font-weight: 600; text-decoration: none; white-space: nowrap; border-radius: var(--radius-small);
}
.more .pop form { margin: 0; }
.more .pop a:hover, .more .pop button:hover { color: var(--ink-bright); background: var(--surface-page); }

.account-card .who { display: flex; align-items: center; gap: .6rem; margin: 0 0 .75rem; color: var(--ink-bright); }
.account-card .who .addr { overflow-wrap: anywhere; }
.account-card .links { list-style: none; margin: 0; padding: 0; }
.account-card .links a { display: flex; align-items: center; gap: .6rem; min-height: 44px; text-decoration: none; }
.crypto-card .ok { color: var(--ink-body); }
.crypto-card .ok .icon { color: var(--brand-blue); }
.dash .warn { margin: 0; }

@media (max-width: 1100px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cols { grid-template-columns: minmax(0, 1fr); }
  .cols { grid-template-rows: none; }
  .devices-box, .share-box, #pair, .side-cards { grid-column: 1; grid-row: auto; }
}
/* Persona sweep 2026-10-12: on a desktop the TVs and Playlists bodies are working pages, not a 34rem phone card
   floating in 1,600 px of nothing. Prose pages (privacy, terms) keep the readable measure. */
@media (min-width: 1100px) {
  body.shell .card.wide { max-width: 56rem; }
}

/* Under 900 px: no sidebar. The top bar carries the lockup, the avatar and the menu (§4b). */
@media (max-width: 900px) {
  .frame { display: block; background: none; }
  .side { display: none; }
  .main { padding: .9rem 1rem 1.5rem; }
  .bar { display: flex; }
  .bar .mark { display: block; }
  .bar .mark img { width: 150px; }
  .bar .who-chip { padding: 0; border: 0; background: none; }
  .bar .who-chip .avatar { width: 2.5rem; height: 2.5rem; margin: 0; font-size: 1rem; }
  .bar .who-chip .addr, .bar .me > form.out { display: none; }
  .menu { display: block; }
  .menu summary {
    list-style: none; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px;
    border-radius: var(--radius-card); cursor: pointer; color: var(--ink-bright);
  }
  .burger, .burger::before, .burger::after { display: block; width: 22px; height: 2px; background: currentColor; border-radius: 2px; }
  .burger { position: relative; }
  .burger::before, .burger::after { content: ""; position: absolute; left: 0; }
  .burger::before { top: -7px; }
  .burger::after { top: 7px; }
  .menu[open] .burger { background: transparent; }
  .menu[open] .burger::before { top: 0; transform: rotate(45deg); }
  .menu[open] .burger::after { top: 0; transform: rotate(-45deg); }
  .menu[open] summary { position: relative; z-index: 21; }
  /* P292 (Joe's mobile-menu-sheet.png): a right-hand side sheet over the dimmed page, which still shows at
     the left edge. The open <summary> becomes the sheet's boxed close button, so no script is needed to close. */
  .sheet {
    position: fixed; top: 0; right: 0; bottom: 0; z-index: 20; width: min(88vw, 26rem); overflow-y: auto;
    padding: 1.25rem 1rem 2rem; border-radius: 24px 0 0 24px;
    background: var(--surface-card);
  }
  .menu[open]::before { content: ""; position: fixed; inset: 0; z-index: 19; background: color-mix(in srgb, var(--surface-night) 72%, transparent); }
  .menu[open] summary {
    position: fixed; top: 1.6rem; right: 1.1rem; z-index: 21; width: 48px; height: 48px;
    background: var(--surface-raised);
  }
  .sheet-account {
    display: flex; align-items: center; gap: .9rem; min-height: 64px; margin: .35rem 0 1rem; padding: 0 3.75rem 1.1rem .35rem;
  }
  .sheet-account .avatar { width: 3.1rem; height: 3.1rem; font-size: 1.3rem; }
  .who-lines { display: grid; min-width: 0; }
  .who-lines .addr { color: var(--ink-bright); font-size: 1.1rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .who-lines .sub { color: var(--ink-muted); font-size: .92rem; }
  .sheet .sections { gap: .45rem; }
  .sheet .sections a { min-height: 64px; padding: .7rem .9rem; font-size: 1.1rem; border-radius: 16px; }
  .sheet .sections a .icon { width: 1.55em; height: 1.55em; }
  .sheet .sections a.on { background: var(--brand-current); }
  .sheet .sections a.on .sub { color: var(--ink-soft); }
  .sheet form.sheet-out { margin-top: 1.1rem; padding-top: 1.1rem; }
  .sheet form.sheet-out button {
    display: flex; align-items: center; width: 100%; max-width: none; min-height: 60px; padding: .8rem 1rem;
    color: var(--ink-bright); background: var(--surface-raised); border-radius: 16px;
    font-size: 1.1rem; font-weight: 500; text-decoration: none;
  }
  .sheet form.sheet-out button .label { flex: 1; }
}

/* Under 600 px: one column. The hero keeps its words and loses its art and button; the full-width
   Pair a TV sits under the device list instead (§4b). The first three stats stay three across down
   to 360 px as one card; encryption has its own card below, so its stat steps aside. */
@media (max-width: 600px) {
  .hero-band { padding: 1.1rem; background: var(--surface-plate); }
  .hero-band .art, .hero-band .cta { display: none; }
  .hero-band p { margin: 0; }
  /* P292: three separate cards (mobile-dashboard-v2.png), the icon circle on top of each. */
  .stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; }
  .stat-card .k { font-size: .85rem; }
  .stat-card .v { font-size: 1.25rem; }
  .stat-card .sub { font-size: .78rem; }
  .stat-card[data-stat="crypto"] { display: none; }
  .cta.wide { display: flex; width: 100%; margin-top: .75rem; }
  .box { padding: 1rem; }
}
@media (max-width: 360px) {
  .stats { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------------------ P180: the page bodies inside the shell
   P127 drew the dashboard's boxes on surface-section with an edge, but the TVs and Playlists bodies kept
   P113's raised card — a second, lighter surface beside the first. Inside the shell a card is a box:
   same surface, same edge. Layout untouched. Every ink on it is an audited surface.section pair. */
body.shell .card { background: none; padding: 0; }

/* ------------------------------------------------------------ P181: the two secrets, in three panels
   One picture a regular person reads in ten seconds: sign in proves it's you, the library passphrase
   locks everything, we keep a box we cannot open. Three across when there is room, stacked on a phone,
   the icon in the same tinted circle as the front page's cards. Pairs: ink-bright / ink-soft on
   surface-section, brand-blue on brand-current (all audited). */
.two-secrets { margin: 2rem 0; }
.two-secrets .pages-h2 { margin-top: 0; }
.secrets {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .75rem;
  margin: 0 0 1rem; padding: 0; list-style: none; container-type: inline-size;
}
@container (max-width: 43.49rem) { .secrets > li:nth-child(3):last-child { grid-column: 1 / -1; } }
.secrets li {
  display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto 1fr; column-gap: .8rem; align-items: start;
  padding: 1rem 1.1rem; background: var(--surface-card); border-radius: 24px;
}
.secrets .disc {
  grid-row: span 2; display: inline-flex; align-items: center; justify-content: center;
  width: 2.6rem; height: 2.6rem; border-radius: 50%; background: var(--brand-current); color: var(--brand-blue);
}
.secrets .disc .icon { width: 1.3rem; height: 1.3rem; vertical-align: 0; }
.secrets b { color: var(--ink-bright); font-size: 1.05rem; }
.secrets li > span:last-child { color: var(--ink-soft); font-size: .98rem; }
.two-secrets .warn { margin: 0 0 .5rem; }
.two-secrets .warn p { margin: 0 0 .7rem; font-weight: 600; }
.card .two-secrets { margin: 0 0 1rem; }

/* ------------------------------------------------------------ P292: Joe's second round of phone mockups
   mobile-menu-sheet.png, mobile-dashboard-v2.png, mobile-devices-v2.png, mobile-home-v2.png (#tv 2026-09-27).
   Heroes and two cards sit on his pictures (server/public/brand/bg-living-room.webp, bg-earth.webp) under a
   left-to-right dark gradient, so every word over a picture is measured on the darkest AND the lightest pixel
   under it (tools/p292-web.mjs). Cards get a blue-tinted edge and a soft glow from the top. Tokens only. */

/* A picture behind a block: clipped to its corners, anchored right, under the gradient. */
.pictured { position: relative; overflow: hidden; isolation: isolate; }
.pictured > .backdrop {
  position: absolute; inset: 0; z-index: -2; width: 100%; height: 100%;
  object-fit: cover; object-position: right center; pointer-events: none;
}
.pictured::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(to right, var(--surface-night) 0%, color-mix(in srgb, var(--surface-night) 90%, transparent) 35%,
    color-mix(in srgb, var(--surface-night) 55%, transparent) 60%, transparent 85%);
}
/* The earth is bright across its whole right half, so its veil never quite lifts. */
.crypto-card.pictured::before, .price.pictured::before {
  background: linear-gradient(to right, var(--surface-night) 0%, color-mix(in srgb, var(--surface-night) 92%, transparent) 45%,
    color-mix(in srgb, var(--surface-night) 76%, transparent) 100%);
}
.crypto-card.pictured > .backdrop, .price.pictured > .backdrop { object-position: right top; }
.hero-band.pictured { min-height: 13rem; }
.hero-band .copy { max-width: 58%; }
/* On a wide band the picture is cropped to a strip; keep the lit TV in it rather than the wall above it. */
.hero-band.pictured > .backdrop { object-position: right 14%; }
.stat-card .v { font-size: 1.45rem; }
.hero-band h1 { color: var(--ink-white); }

/* The front page: the headline block on the living room, the price band on the earth. */
.hero-copy.pictured {
  margin: 0 0 1.25rem; padding: 2rem 1.75rem 1.5rem;
  border: 1px solid color-mix(in srgb, var(--brand-blue) 55%, var(--line-edge)); border-radius: var(--radius-card);
}
.hero-copy > h1, .hero-copy > .lede { max-width: 34rem; }
.hero-copy > .lede { margin-bottom: 0; color: var(--ink-soft); }
.hero-copy > .hero-go { margin: 1.25rem 0 0; }
.price.pictured { border: 1px solid color-mix(in srgb, var(--brand-blue) 55%, var(--line-edge)); border-left: 4px solid var(--brand-blue); }

/* Stat cards: the icon in a tinted circle beside the number (mobile-dashboard-v2.png). */
.stat-card { grid-template-columns: auto minmax(0, 1fr); column-gap: .9rem; align-items: center; }
.stat-card .glyph {
  grid-row: span 3; display: inline-flex; align-items: center; justify-content: center; width: 3rem; height: 3rem;
  border-radius: 50%; background: var(--brand-current); color: var(--brand-blue);
}
.stat-card .k, .stat-card .v, .stat-card .sub { grid-column: 2; }

/* "Before your first sync": the notice card laid out as a stat card, its key in a gold circle. */
.dash .warn {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 1rem; padding: 1.1rem 1.1rem .2rem;
}
.warn .glyph {
  grid-row: span 2; display: inline-flex; align-items: center; justify-content: center; width: 3rem; height: 3rem;
  border-radius: 50%; color: var(--meaning-accent); background: var(--surface-alert);
}
.dash .warn form { grid-column: 1 / -1; }
.dash .warn button { width: 100%; max-width: none; text-align: center; }

/* The sidebar keeps its one-line sections: the subtitle and chevron are the phone sheet's. */
.sections .label { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.sections .sub { color: var(--ink-muted); font-size: .9rem; font-weight: 400; }
.side .sections .sub, .side .sections .chev { display: none; }
.chev {
  flex: 0 0 auto; width: .6rem; height: .6rem; margin: 0 .35rem 0 .5rem;
  border-top: 2px solid var(--ink-soft); border-right: 2px solid var(--ink-soft); transform: rotate(45deg);
}

/* Devices: a picture of the platform (mobile-devices-v2.png, models rejected), and one row for the browsers. */
.art { flex: 0 0 auto; width: 56px; height: 40px; }
.art .scr { fill: var(--surface-night); stroke: var(--line-edge); stroke-width: 1.5; }
.art .glow { fill: var(--brand-blue); opacity: .6; }
.art .edge { stroke: var(--line-edge); stroke-width: 2; stroke-linecap: round; }
.art .body { fill: var(--surface-raised); stroke: var(--line-edge); stroke-width: 1.5; }
.art .edge-fill { fill: var(--line-edge); }
.art .dot-art { fill: var(--brand-blue); }
.art[data-platform="web"] .body { fill: none; stroke: var(--brand-blue); stroke-width: 2; }
.devices .body > .art { float: left; margin: .1rem .85rem 0 0; }
.devices li.browsers { display: block; padding: 0; }
.browsers summary {
  list-style: none; display: flex; align-items: center; gap: .85rem; min-height: 56px; padding: .6rem 0;
  color: var(--ink-soft); font-weight: 600; cursor: pointer;
}
.browsers summary::-webkit-details-marker { display: none; }
.browsers summary .line { flex: 1; min-width: 0; }
.browsers summary::after {
  content: ""; flex: 0 0 auto; width: .55rem; height: .55rem; margin-right: .9rem;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft); transform: rotate(45deg);
}
.browsers[open] summary::after { transform: rotate(225deg); }
.browsers summary:focus-visible { outline: 3px solid var(--surface-focus); outline-offset: 2px; }
.browsers ul { list-style: none; margin: 0 0 .4rem; padding: 0 0 0 4.3rem; }
.devices .browsers li { display: flex; align-items: center; justify-content: space-between; gap: .6rem; min-height: 48px; padding: .35rem 0; }
.devices .browsers li .note { margin: 0; }
.devices li > form, form.quiet-out { margin: 0; }
.devices li > form button, form.quiet-out button {
  min-height: 44px; padding: .45rem .75rem; font-size: .9rem; font-weight: 600; text-align: center;
  color: var(--ink-bright); background: color-mix(in srgb, var(--ink-bright) 9%, transparent);
}

@media (max-width: 600px) {
  /* /devices: the Sign out button drops under the name and rename field, so neither is squeezed to a stub. */
  .card .devices li { flex-wrap: wrap; row-gap: .5rem; }
  .card .devices li > .body { flex: 1 1 12rem; }
  .card .devices li > form { margin-left: calc(56px + .75rem); }
  .stat-card { grid-template-columns: minmax(0, 1fr); justify-items: center; text-align: center; padding: .85rem .35rem; }
  .stat-card .glyph { grid-row: auto; width: 2.6rem; height: 2.6rem; margin-bottom: .25rem; }
  .stat-card .k, .stat-card .v, .stat-card .sub { grid-column: 1; }
  .hero-band.pictured { min-height: 12rem; align-items: flex-start; }
  .hero-band .copy { max-width: 62%; }
  /* The front page's copy is six lines on a phone and would cross the lit TV, so there the room is a banner
     over the top of the block that fades into the night before the headline starts. */
  .hero-copy.pictured { padding: calc(50vw - 1rem) 1.1rem 1.2rem; }
  .hero-copy.pictured > .backdrop { height: calc(60vw); object-position: right top; }
  .hero-copy.pictured::before {
    background: linear-gradient(to bottom, transparent 0, transparent calc(28vw), color-mix(in srgb, var(--surface-night) 85%, transparent) calc(46vw), var(--surface-night) calc(58vw));
  }
  .hero-copy > h1, .hero-copy > .lede { max-width: none; }
  .browsers ul { padding-left: 1rem; }
}

/* ---------------------------------------------------------------- /antenna (P331)
   Numbered cards: a step list a viewer follows with the remote or the router app in the other hand.
   The number is ink-bright on brand-current with a blue edge, the pair `.chip.this` already wears;
   brand-blue on brand-current is only 4.2:1, an edge pair, not a text one. */
.steps { display: grid; gap: .75rem; margin: 0 0 1.25rem; padding: 0; list-style: none; counter-reset: step; }
.steps > li {
  position: relative; padding: .95rem 1rem .95rem 3.5rem; counter-increment: step;
  background: var(--surface-card); border-radius: 16px;
}
.steps > li::before {
  content: counter(step); position: absolute; left: .9rem; top: .8rem; display: grid; place-items: center;
  width: 1.85rem; height: 1.85rem; color: var(--ink-bright); background: var(--brand-current);
  border-radius: 50%; font-weight: 700; line-height: 1;
}
.steps b { color: var(--ink-bright); }
/* An address a viewer types. A sans monospace (no web font: every face here is already on the device), and it
   breaks only where the page put a <wbr>, never inside a word or a port number. */
.card code { font-family: ui-monospace, "Cascadia Mono", Consolas, "SF Mono", Menlo, monospace; font-size: .9em; color: var(--ink-bright); }
/* The one collapsed section: a full-width row with a chevron, 56 px tall so a thumb finds it. */
.advanced { margin: 1.75rem 0 1.25rem; border-top: 1px solid var(--line-edge); border-bottom: 1px solid var(--line-edge); }
.advanced > summary {
  list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 56px; color: var(--ink-bright); font-weight: 700; cursor: pointer;
}
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > summary::after {
  content: ""; flex: 0 0 auto; width: .55rem; height: .55rem; margin-right: .4rem;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft); transform: rotate(45deg);
}
.advanced[open] > summary::after { transform: rotate(225deg); }
.advanced > summary:focus-visible { outline: 3px solid var(--surface-focus); outline-offset: 2px; }
.advanced h3 { margin: 1.5rem 0 .6rem; color: var(--ink-bright); font-size: 1.05rem; }

/* P372: a library passphrase is typed into a text field masked here, never type=password — Chrome's password manager
   offers a saved site password to any password field, whatever its autocomplete says. */
input.secret { -webkit-text-security: disc; }

/* ---------------------------------------------------------------- P367: the site on a desktop
   Joe, #tv 2026-09-29, on /terms and / at ~1,500 px: "its all mobile view styled", "this border is all over the
   homepage... shouldnt be for this splash", "animated bg of gradients like northern lights... and particles
   emitting from logo". Under 64rem nothing in this section changes a page, except the splash losing its box. */

/* The sky (app/assets/javascripts/sky.js): one small canvas stretched behind everything, at 12% — the most any
   frame of it may sit under a word. Body text is ink-body on surface-page at 16:1; the brightest curtain pixel at
   12% lifts the page to ~1.3x its luminance, which leaves it past 12:1, well over the 7:1 bar
   (tools/p367-web.mjs measures it on the lightest pixel under every word, three frames apart). */
.sky { position: fixed; inset: 0; z-index: -1; width: 100%; height: 100%; opacity: .12; pointer-events: none; }
/* The sparks' own canvas, laid over the logo and the gap above it, stopping at the lockup's right edge so a spark
   never crosses the header's words. */
.site .mark { position: relative; }
.sparks { position: absolute; left: -1rem; top: -1.25rem; width: calc(100% + 1rem); height: calc(100% + 1.75rem); pointer-events: none; }

/* The front page's splash: no border and no box, bleeding to both edges of the window. The words stay on the
   page's column; the living room is anchored right and fades into the sky on its left and at its foot, so the
   splash has no edge anywhere. (overflow-x: clip, not hidden: 100vw counts a desktop scrollbar, and clip cuts
   that sliver without making the body a scroll container.) */
body:has(.home) { overflow-x: clip; }
.hero-copy.pictured { border: 0; border-radius: 0; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw); }
@media (min-width: 34.01rem) {
  .hero-copy.pictured { margin-top: -1.25rem; padding: 3rem calc(50vw - 50%) 2.5rem; }
  .hero-copy.pictured > .backdrop {
    left: auto; right: 0; width: min(100%, 64rem); object-position: right 30%;
    -webkit-mask-image: linear-gradient(to right, transparent, black 45%), linear-gradient(to bottom, transparent, black 14%, black 65%, transparent);
    -webkit-mask-composite: source-in;
    mask-image: linear-gradient(to right, transparent, black 45%), linear-gradient(to bottom, transparent, black 14%, black 65%, transparent);
    mask-composite: intersect;
  }
  /* The veil stays, but only where the words are, and it fades out at its top, foot and right rather than
     ending in a line across the sky. */
  .hero-copy.pictured::before {
    background: linear-gradient(to right, color-mix(in srgb, var(--surface-page) 88%, transparent) 0%,
      color-mix(in srgb, var(--surface-page) 70%, transparent) 45%, transparent 75%);
    -webkit-mask-image: linear-gradient(to bottom, transparent, black 18%, black 72%, transparent);
    mask-image: linear-gradient(to bottom, transparent, black 18%, black 72%, transparent);
  }
}
/* A phone keeps P292's banner (the room over the top, the words under it), edge to edge: the veil now ends in the
   page's own surface instead of the night, so the block's foot meets the page with no seam. */
@media (max-width: 34rem) {
  .hero-copy.pictured { padding-left: calc(50vw - 50%); padding-right: calc(50vw - 50%); }
  .hero-copy.pictured::before {
    background: linear-gradient(to bottom, transparent 0, transparent calc(28vw), color-mix(in srgb, var(--surface-page) 85%, transparent) calc(46vw), var(--surface-page) calc(58vw));
  }
}

/* Persona sweep 2026-10-14 (impatient at 200% zoom = a 720 px page; a 768 tablet in portrait reads the same): between
   the phone and a laptop the words' 34rem column covered the room, and "every TV" sat on the TV's own bezel. There
   the room goes over the top and the words under it, as on a phone, at the column's full width. */
@media (min-width: 34.01rem) and (max-width: 60rem) {
  .hero-copy.pictured { padding-top: 46vw; }
  .hero-copy.pictured > .backdrop {
    width: 100%; height: 56vw; object-position: right top;
    -webkit-mask-image: linear-gradient(to bottom, transparent, black 12%, black 70%, transparent); mask-image: linear-gradient(to bottom, transparent, black 12%, black 70%, transparent);
  }
  .hero-copy.pictured::before {
    -webkit-mask-image: none; mask-image: none;
    background: linear-gradient(to bottom, transparent 0, transparent 30vw, color-mix(in srgb, var(--surface-page) 85%, transparent) 42vw, var(--surface-page) 52vw);
  }
  .hero-copy > h1, .hero-copy > .lede { max-width: none; }
}

@media (min-width: 64rem) {
  body { padding: 1.25rem 2rem 6vh; }
  .site, .foot, body:has(.home) .site, body:has(.home) .foot, .home, .doc { max-width: 68rem; }
  .hero-copy.pictured { margin-top: -1.25rem; padding-top: 4.5rem; padding-bottom: 3.5rem; }
  .hero-copy.pictured > .backdrop { width: min(100%, 72rem); }

  /* Reading pages: the list of sections on the left, sticky, and a ~44rem reading column beside it. */
  .doc { display: grid; grid-template-columns: 15rem minmax(0, 44rem); gap: 5rem; align-items: start; margin: 0 auto; }
  .doc > .card { max-width: none; width: 100%; margin: 0; }
  .toc { position: sticky; top: 1.5rem; padding-top: 1.75rem; }
  .toc-h { margin: 0 0 .6rem; }
  .toc ol { display: grid; gap: .1rem; margin: 0; padding: 0; list-style: none; border-left: 1px solid var(--line-divider); }
  .toc a {
    display: block; margin-left: -1px; padding: .35rem 0 .35rem .9rem; border-left: 2px solid transparent;
    color: var(--ink-soft); font-size: .95rem; line-height: 1.35; text-decoration: none;
  }
  .toc a:hover { color: var(--ink-bright); border-left-color: var(--brand-blue); }
  .toc a:focus-visible { outline: 3px solid var(--surface-focus); outline-offset: 2px; }
  /* A heading the list jumps to lands clear of the window's top edge. */
  .doc [id] { scroll-margin-top: 1.25rem; }
}
.toc { display: none; }
@media (min-width: 64rem) { .toc { display: block; } }

/* Every h1/h2 carries a glyph (LucideHelper, IconsHelper): in the heading's own ink, a touch of blue, on its baseline. */
.card h1 .icon, .pages-h2 .icon { color: var(--brand-blue); }
.pages-h2 { display: flex; align-items: center; gap: .5rem; }
.pages-h2 .icon { width: 1.2em; height: 1.2em; }

/* ---------------------------------------------------------------- /install (P389)
   "Which TV do you have?": a wrap of plain-name pills, 44 px tall for a thumb. Every pill is an anchor, so
   /install#fire-tv links one TV: with a TV section targeted, CSS hides the other TVs (updating and safety stay).
   A Roku/Samsung/LG/Apple TV link targets its line inside "Other TVs", which then stands alone.
   No fragment (or no CSS): every TV shows, in order. */
.tv-pick { display: flex; flex-wrap: wrap; gap: .5rem; margin: .25rem 0 .75rem; }
.tv-pick a {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 1rem;
  color: var(--ink-bright); background: var(--surface-section); border: 1px solid var(--line-edge);
  border-radius: 999px; font-weight: 600; text-decoration: none;
}
.tv-pick a:hover { border-color: var(--brand-blue); }
.tv-pick a:focus-visible, .tv-all:focus-visible { outline: 3px solid var(--surface-focus); outline-offset: 2px; }
.tv-hint { color: var(--ink-soft); font-size: .95rem; }
.tv { scroll-margin-top: 1rem; }
.tv h3 { margin: 1rem 0 .4rem; color: var(--ink-bright); font-size: 1.05rem; }
.tv-which, .tv-not-yet { display: grid; gap: .5rem; margin: 0 0 1.25rem; padding-left: 1.2rem; }
.tv-all { display: inline-block; margin-bottom: .5rem; font-size: .95rem; }
main:has(.tv:target) .tv:not(:target),
main:has(#not-yet :target) .tv:not(#not-yet) { display: none; }
.account-card .add-email { margin: .2rem 0 1rem; }
/* P632: the coin pay page. The QR sits on its own white plate so a phone camera reads it on the dark page. */
.pay-coins { display: flex; flex-wrap: wrap; gap: .75rem; margin: 1rem 0; }
.pay-coins .cta.ghost, .pay .cta.ghost { background: color-mix(in srgb, var(--ink-bright) 9%, transparent); color: var(--ink-bright); }
.pay .qr { display: block; margin: 1rem auto; max-width: 100%; height: auto; padding: 8px; background: var(--surface-white); border-radius: var(--radius-card); }
.pay .field { width: 100%; box-sizing: border-box; min-height: 48px; padding: .6rem .8rem; border-radius: var(--radius-card);
  border: 1px solid var(--line-divider); background: transparent; color: var(--ink-bright); font: inherit; font-size: 1rem; overflow-wrap: anywhere; }
.pay .amount { font-size: 1.35rem; font-weight: 700; color: var(--ink-bright); }
.pay .pay-state { min-height: 1.5em; font-weight: 700; color: var(--ink-bright); }
.pay label { display: block; margin: .9rem 0 .3rem; color: var(--ink-soft); font-size: .95rem; }
/* Persona sweep 2026-10-12: copy buttons beside each field; on a desktop the fields and the QR sit side by side. */
.pay .copy-row { display: flex; gap: .5rem; align-items: stretch; }
.pay .copy-row .field { flex: 1; min-width: 0; }
.pay .copy-row .copy { flex: none; min-width: 5.5rem; }
.pay .pay-network { margin: .5rem 0 1rem; }
.pay .pay-qr { text-align: center; }
.pay .pay-qr .note { max-width: 22rem; margin: 0 auto .75rem; }
.pay .pay-how { margin: 1.75rem 0 .75rem; font-size: 1.15rem; color: var(--ink-bright); }
.pay .pay-guides { display: grid; gap: .6rem; }
.pay .pay-guide { background: var(--surface-card); border-radius: 16px; padding: .7rem 1rem; }
.pay .pay-guide summary { cursor: pointer; font-weight: 700; color: var(--ink-bright); min-height: 32px; }
.pay .pay-guide ol { margin: .6rem 0 .3rem; padding-left: 1.3rem; display: grid; gap: .4rem; }
@media (min-width: 900px) {
  .pay .pay-grid { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2rem; align-items: start; }
  .pay .pay-qr .qr { margin-top: .9rem; }
}
.pay .pay-send .pay-state { margin-top: 1rem; }
.pay textarea.field { resize: none; field-sizing: content; min-height: 3.4em; }
@media (max-width: 600px) { .pay textarea.field { min-height: 5em; } }

/* ---------------------------------------------------------------- P882: borderless, like Google TV
   Joe, #tv 2026-10-12 (mockup B, design/mockups/web-borderless-*.png): "lots of borders all over.. mimic how the tv app
   is starting to look .. no borders on things". Hierarchy is tonal only: the page and the sidebar share surface-page, a
   card is one step up (surface-card, radius 24), a row a step further (surface-strip, radius 16), and a secondary button
   is a translucent grey pill that reads on any of them. Blue is the current nav pill and the one primary action. A form
   field keeps its edge (a borderless text box is unfindable) and the keyboard keeps its white ring.
   tools/smoke/417-p882.mjs measures all of it on computed styles. */
.side .sections { flex: 1 0 auto; align-content: start; }
.side-account {
  display: flex; align-items: center; gap: .75rem; margin-top: 1rem; padding: .75rem .9rem;
  background: var(--surface-card); border-radius: 16px;
}
.side-account .avatar { width: 2.4rem; height: 2.4rem; font-size: 1rem; }
.side-account .who-lines { display: grid; min-width: 0; }
.side-account .addr { color: var(--ink-bright); font-size: .92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-account form.out button { padding: 0; font-size: .88rem; color: var(--ink-soft); text-align: left; }

/* The page title sits on the page, not in a box; the list under it is the card. */
body.shell .card h1 { font-size: clamp(1.8rem, 3vw, 2.3rem); margin-bottom: .4rem; }
body.shell .card h1 .icon { display: none; }
body.shell .card > .lede { margin-bottom: 1.5rem; font-size: 1.05rem; }
body.shell .card > .devices, #sources:not(:empty) { padding: .75rem; background: var(--surface-card); border-radius: 24px; }
.devices li.browsers { padding: 0 1rem; }

/* /playlists: the two-secrets picture shrinks to one strip under the list, and the no-reset line is an amber note. */
.secrets-strip {
  display: flex; align-items: center; gap: 1rem; margin: 0 0 .75rem; padding: .85rem 1.2rem;
  background: var(--surface-card); border-radius: 999px;
}
.secrets-strip .disc {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem;
  border-radius: 50%; color: var(--brand-blue); background: var(--brand-current);
}
.secrets-strip .disc .icon { width: 1.2rem; height: 1.2rem; vertical-align: 0; }
.secrets-strip p { flex: 1; margin: 0; color: var(--ink-soft); font-size: .95rem; }
.secrets-strip a { flex: 0 0 auto; white-space: nowrap; font-size: .95rem; }
.warn.no-reset { padding: .85rem 1.2rem .85rem 3.6rem; border-radius: 999px; }
.warn.no-reset::before { top: 50%; transform: translateY(-50%); }
.warn.no-reset p { margin: 0; color: var(--ink-body); font-weight: 400; }
@media (max-width: 600px) {
  .secrets-strip, .warn.no-reset { border-radius: 16px; }
  .secrets-strip { flex-wrap: wrap; }
}

/* Persona sweep 2026-10-12 (old-phone-housemate, a 320 px iPhone SE): every layer kept its phone padding, so it
   stacked — body 16 + card 28 + panel 22 a side left the sign-in form 187 px, and a device row's name 98 px beside
   a 56 px TV drawing ("Living / Room / Android TV" on three lines). Under 360 px each layer gives some back, and the
   dashboard's band drops its room picture: the 62% copy column wrote "sync." over the TV in it. */
@media (max-width: 360px) {
  .card { padding: 1.1rem 1rem 1rem; }
  .panel { padding: .9rem; }
  .box { padding: .8rem; }
  .devices li { gap: .5rem; padding: .6rem .7rem; }
  .devices .body > .art { width: 40px; height: 29px; margin-right: .6rem; }
  .card .devices li > form { margin-left: 0; }
  .hero-band.pictured { min-height: 0; }
  .hero-band.pictured > .backdrop, .hero-band.pictured::before { display: none; }
  .hero-band .copy { max-width: none; }
  .box-head .more-link { white-space: nowrap; }
  .box-head .more-link .icon { display: none; }
  /* Run 17 (2026-10-16): one stat a row, each kept the three-across card's icon-on-top stack, so the three
     stood ~150 px tall each and pushed the TV list below an iPhone SE's first screen. One a row, the icon
     goes back beside the number. */
  .stat-card { grid-template-columns: auto minmax(0, 1fr); justify-items: start; text-align: left; padding: .6rem .8rem; }
  .stat-card .glyph { grid-row: span 3; width: 2.4rem; height: 2.4rem; margin-bottom: 0; }
  .stat-card .k, .stat-card .v, .stat-card .sub { grid-column: 2; }
}

/* Persona sweep 2026-10-13 (tivimate-switcher, 2560 ultrawide). Two things only a big screen showed:
   - A short page (Playlists, locked) ended its footer half-way down the screen with ~700 px of nothing under it;
     the signed-in column is now a flex column whose footer takes the leftover space above itself.
   - The dashboard band is a 9.5:1 strip at 2560, so "cover" cropped its room picture to a slice that cut the lit
     TV's wordmark in half. Past 1800 px the picture keeps a 9:2 box on the right and fades in from the left
     (the band's own gradient is night there anyway), so the whole TV stays in frame at every width. */
.main { display: flex; flex-direction: column; }
.main > main { margin-bottom: 2rem; }
.main > .foot { margin-top: auto; }
@media (min-width: 1800px) {
  .hero-band.pictured > .backdrop { left: auto; width: auto; max-width: 100%; aspect-ratio: 9 / 2;
    mask-image: linear-gradient(to right, transparent, var(--surface-night) 30%); }
}

/* Persona sweep 2026-10-13 (slow 3G): /p2's sign-in waits for the script that carries the code across it. */
#sign-in[aria-disabled="true"] { pointer-events: none; opacity: .55; }

/* Persona sweep 2026-10-14 (payer): an ended plan's tile links to the Account card's pay rows. */
.stat-card .sub .renew { color: var(--ink-bright); font-weight: 600; }

/* Persona sweep 2026-10-14 (keyboard-reader): "Skip to content" is the first Tab stop on every page and shows only
   while focused (a white pill, the focus fill, top-left); the wordmark link's ring stands clear of the lockup
   instead of cutting through the "v" of "tv". */
.skip { position: absolute; left: 1rem; top: -4rem; z-index: 100; padding: .6rem 1rem; border-radius: 999px;
  background: var(--surface-focus); color: var(--ink-on-focus); font-weight: 700; text-decoration: none; }
.skip:focus, .skip:focus-visible { top: 1rem; outline: none; }
.mark:focus-visible { outline: 3px solid var(--surface-focus); outline-offset: 6px; border-radius: 6px; }

/* P930: /admin/stats — Joe's launch numbers. A wide table that scrolls inside its card on a phone. */
.admin-stats { box-sizing: border-box; width: 100%; min-width: 0; max-width: 72rem; margin: 2rem auto; padding: 0 1rem; color: var(--ink-soft); } /* width 100%: auto side margins in .main's flex column shrink-wrap to the wide table otherwise */
.admin-stats h1, .admin-stats h2 { color: var(--ink-bright); }
.admin-stats-now { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: .5rem 1.5rem; padding: 0; list-style: none; }
.admin-stats-now strong { color: var(--ink-bright); }
.admin-stats-scroll { max-width: 100%; overflow-x: auto; background: var(--surface-card); border: 1px solid var(--line-edge); border-radius: var(--radius-card); }
.admin-stats-table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; font-size: .9rem; }
.admin-stats-table th, .admin-stats-table td { padding: .4rem .6rem; text-align: right; white-space: nowrap; border-bottom: 1px solid var(--line-edge); }
.admin-stats-table th:first-child, .admin-stats-table td:first-child { text-align: left; }
.admin-stats-table thead th, .admin-stats-table tfoot th { color: var(--ink-bright); }
.admin-stats-note { margin-top: .75rem; color: var(--ink-muted); font-size: .85rem; }

/* Persona sweep 2026-10-15 (desk-manager, 768 tablet, six TVs): the dashboard's "Rename" opens /devices#device-<id>,
   and two TVs called "Living Room Android TV" left the viewer guessing which row they had picked. The row they came
   for wears the "current" blue the sidebar marks its page with (never the white focus fill, never a shadow: P180). */
.devices li:target { background: var(--surface-current); scroll-margin-top: 1rem; }
