/* roomboard.app/info — the public widget guide. Standalone stylesheet: it uses
   the Momentum tokens from the app but NOT main.css, which locks html/body to
   the 1920x1080 signage viewport. Desktop-first, readable down to phone. */

:root {
  --bg: #000;
  --bg-card: #121212;
  --bg-card-2: rgba(255, 255, 255, 0.11);
  --ink: rgba(255, 255, 255, 0.95);
  --ink-mid: rgba(255, 255, 255, 0.84);
  --ink-dim: rgba(255, 255, 255, 0.72);
  --ink-faint: rgba(255, 255, 255, 0.56);
  --accent: #64b4fa;
  --good: #3cc29a;
  --warn: #f2990a;
  --radius: 24px;
  --gap: 20px;
  --pad: 28px;
  /* One reading measure for every long-form block on the page. 68ch keeps a
     line at the 45-75 character range where the eye finds the next line without
     hunting; the old 760px lede ran past 90 characters at desktop sizes. */
  --measure: 68ch;
  /* The page's ONLY monospace, and it is spent on device configuration lines
     and nothing else: an xConfiguration is a value the reader retypes exactly,
     so the alignment and the unambiguous glyphs are load-bearing. Mono as a
     costume for "technical" is how a guide ends up with three voices. */
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: 'CiscoSansTT', 'Cisco SansTT', -apple-system, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: #8ec7fb; }
img { max-width: 100%; }

/* One focus vocabulary for the whole page. The screenshot buttons had earned a
   deliberate 3px accent ring while every other focusable fell back to Chrome's
   1px default; the ring is the page's, so every focusable gets it. Radius is
   inherited from whatever the element already has, so pills stay pills. */
a:focus-visible,
button:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.wrap { max-width: 1240px; margin: 0 auto; padding-left: var(--pad); padding-right: var(--pad); }

/* ---------- sticky nav with scroll-spy ---------- */
.nav { position: sticky; top: 0; z-index: 50; background: #000; border-bottom: 1px solid var(--bg-card-2); }
.nav__inner {
  position: relative; /* the glide pill positions against the scrolling row */
  max-width: 1240px; margin: 0 auto; padding: 10px var(--pad);
  display: flex; align-items: center; gap: 4px; overflow-x: auto;
  scrollbar-width: none;
}
.nav__inner::-webkit-scrollbar { display: none; }
/* margin-right: auto separates brand from pills only while the rail has slack;
   on a phone the rail overflows and scrolls, the auto margin collapses to
   nothing, and the lockup sat one 4px gap from the first pill (Sean's catch,
   2026-08-18). The padding is the floor the auto margin cannot go below,
   sized to the pills' own 14px inset. */
.nav__brand { margin-right: auto; padding: 9px 18px 9px 0; display: flex; align-items: center; flex: none; }
/* 22px line + 11px top and bottom = a 44px target, the floor for a thumb. */
.nav__link {
  position: relative; z-index: 1; /* labels ride above the glide pill */
  font-size: 15px; line-height: 22px; padding: 11px 14px; border-radius: 999px; white-space: nowrap;
  color: var(--ink-dim); background: transparent; flex: none;
  transition: color 150ms cubic-bezier(0.22, 1, 0.36, 1), background 150ms cubic-bezier(0.22, 1, 0.36, 1);
}
.nav__link:hover { color: var(--ink); }
.nav__link.is-active { background: var(--bg-card-2); color: var(--ink); }
/* The active highlight, as one element that GLIDES between labels (info.js
   builds it and stamps nav--glide in the same breath). It scrolls with the
   rail because it is positioned inside the scrolling row, and the edge fade
   masks it exactly as it masks the labels. left/width are layout properties,
   but this is a single out-of-flow element the size of a word; nothing else
   reflows. JS owns top/height too, so the taller brand can never misalign it. */
.nav__pill {
  position: absolute; z-index: 0; width: 0;
  border-radius: 999px; background: var(--bg-card-2);
  transition: left 260ms cubic-bezier(0.22, 1, 0.36, 1), width 260ms cubic-bezier(0.22, 1, 0.36, 1);
}
/* With the pill carrying the highlight, the per-link background retires; the
   rule above it stays as the no-script fallback. */
.nav--glide .nav__link.is-active { background: transparent; }
/* Scrolled-off pills were invisible on a phone: the row just looked truncated.
   info.js sets these classes from the scroll position, so the fade appears only
   on the side that actually has more nav to reach. */
.nav--fade-right .nav__inner {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 48px), transparent);
}
.nav--fade-left .nav__inner {
  -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to left, #000 calc(100% - 48px), transparent);
}
.nav--fade-left.nav--fade-right .nav__inner {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
  mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
}

/* ---------- the wordmark ----------
   unsleep is set as live text, one family, two weights, and the word carries
   its own meaning in the weight: "un" is the semibold half that stays awake,
   "sleep" is the half held back at 50% ink, and the slash between them is the
   only blue thing in the word. Three plain spans, no overlay, no clip, no
   aria-hidden twin, so find, copy, and screen readers all get exactly
   "un/sleep". (The old mark needed a glyph-cover construction to paint one
   accent blue and took four iterations to stop clipping; nothing here can
   regress the same way, because nothing is drawn twice.)
   The brand rule: lowercase, always. Never title-case it in copy or CSS.
   These class names and values are shared verbatim with the app shell's
   lockup: change one, change both. */
.umark { line-height: 1; letter-spacing: -0.03em; }
.umark__un { font-weight: 600; color: var(--ink); }
/* Asymmetric on purpose (Sean's catch, 2026-08-18): the slash leans right,
   so its foot crowds the n while its head leaves an open pocket before the
   s. Equal margins read unequal; the left side carries the optical
   correction. Chosen against 0.05/0.09/0.12/0.15 at 64px: 0.12 balances the
   two pockets, 0.15 detaches the slash from the word. */
.umark__sl { color: var(--accent); font-weight: 300; margin: 0 0.05em 0 0.12em; }
.umark__sleep { font-weight: 300; color: rgba(255, 255, 255, 0.50); }

/* ---------- hero ---------- */
.hero { padding-top: 84px; padding-bottom: 12px; }
/* The masthead is the product's own subject at poster scale: one landscape
   screen, awake, with four cards on its ruled grid and the name read across
   the clear middle band. The accent marks what is awake: exactly one card is
   lit, the other three sit at descending light, which is the system rule the
   whole palette runs on. Nothing sits BEHIND the letters except the ruling
   itself, which is the precedent the previous masthead set: the faint grid
   may run under the word, placed cards never do.
   (The ruling came back at Sean's call 2026-08-18: under the old name it drew
   the name's meaning; under this one it draws the BOARD, the grid the ghost
   cards actually live on. Every geometric rule below is inherited verbatim
   from the retired field, learned defects and all.) */
.hero__screen {
  --cell: 30px;
  /* The gutter a placed module keeps from the rules around it — the same idea
     as the icon masters' constant gutter, and the edit view's card inset. */
  --g: 3px;
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* Landscape at every width, and tall enough that the word's own band stays
     clear of the card rows. The snap in info.js rounds the rendered box to
     whole cells, so these bounds are starting points, not the final size. */
  min-height: clamp(180px, 26vw, 260px);
  /* Two cells of vertical padding, the retired field's own number: the top
     and bottom card rows live inside it, so the centered title-and-sub stack
     can never enter their rows; when the sub wraps taller the panel grows
     and the snap rounds the growth to whole cells. */
  padding: calc(var(--cell) * 2) calc(var(--cell) * 1.5);
  background-color: #05070a;
  /* The corner radius is exactly ONE CELL: the arc's endpoints land on the
     first interior rules, so each corner cell is a quarter-round bounded by
     the arc and two rules, a corner OF the grid rather than a curve clipping
     through it. An arbitrary radius cuts cells mid-way. */
  border-radius: var(--cell);
  /* The keyline is the outermost rule: firmer than the interior ruling (.085)
     because anti-aliasing feathers the corner arcs and dilutes their apparent
     ink; equal alpha would read fainter on the curves than on the straights.
     background-position hides the gradient's own first line under the keyline
     so the left and top edges are never drawn twice. */
  border: 1px solid rgba(255, 255, 255, 0.13);
  background-image:
    linear-gradient(to right, rgba(255, 255, 255, 0.085) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.085) 1px, transparent 1px);
  background-size: var(--cell) var(--cell);
  background-position: -1px -1px;
}
/* Cell units, not percentages: cards on a visible ruling must LAND on it, the
   same discipline the dashboard itself and the icon masters keep. ONE rule
   for every card, lit and ghost alike: occupy a whole-cell span, inset by
   the gutter from the four rules that bound it. And every card is OPAQUE
   (the ghost tints are pre-blended against the panel's #05070a): a
   translucent card lets the ruling show through its body, which reads as a
   card that does not fit its cells (Sean's catch on beta review). A real
   card covers the cells it occupies; these do too. */
.hero__card { position: absolute; border-radius: 3px; }
.hero__card--lit { left: calc(var(--cell) + var(--g)); top: calc(var(--cell) + var(--g)); width: calc(var(--cell) * 3 - var(--g) * 2); height: calc(var(--cell) - var(--g) * 2); background: #64b4fa; }
.hero__card--b { right: calc(var(--cell) + var(--g)); top: calc(var(--cell) + var(--g)); width: calc(var(--cell) * 2 - var(--g) * 2); height: calc(var(--cell) - var(--g) * 2); background: #182a3a; }
.hero__card--c { right: calc(var(--cell) + var(--g)); bottom: calc(var(--cell) + var(--g)); width: calc(var(--cell) * 2 - var(--g) * 2); height: calc(var(--cell) - var(--g) * 2); background: #101c27; }
.hero__card--d { left: calc(var(--cell) + var(--g)); bottom: calc(var(--cell) + var(--g)); width: calc(var(--cell) * 2 - var(--g) * 2); height: calc(var(--cell) - var(--g) * 2); background: #182a3a; }
/* position:relative is paint-order clarity only; the panel is one stacking
   context and the word is the last thing in it either way. */
.hero__title { position: relative; margin: 0; font-size: clamp(38px, 6.4vw, 64px); line-height: 1; }
/* The sub lives INSIDE the panel under the wordmark (Sean's call on beta
   review, restoring the old masthead's arrangement); the note stays below.
   position: relative is the same paint-order clarity the title carries, and
   the measure is tighter than the page's because the panel's own padding is
   what frames it now. */
.hero__sub { position: relative; font-size: clamp(15px, 1.6vw, 17px); line-height: 1.45; color: var(--ink-mid); margin: 14px 0 0; max-width: 48ch; text-align: center; text-wrap: pretty; }
.hero__note { font-size: 16px; color: var(--ink-dim); margin: 18px 0 0; max-width: var(--measure); }
/* Nav brand is the mark and the wordmark at rail scale. min-height holds the
   same 44px thumb target the pills beside it keep, and it is a BORDER-box 44
   (the sheet sets box-sizing globally), so the 9px padding is inside it:
   .umark sets line-height:1, which collapses the flex row to the em box and
   would otherwise leave the brand a 39px target next to 44px neighbours. */
.nav__brand { font-size: 21px; color: var(--ink); gap: 10px; min-height: 44px; }
.nav__mark { flex: none; display: block; }

/* The hero shows the product: the board sits beside the lede once there is room
   for both to stay legible, and drops below it before either gets squeezed.
   The picture is deliberately compact here, which is why it is a shot__btn like
   the section screenshots — the full board is one tap away in the lightbox. */
.hero__shot { margin: 34px 0 0; }
.hero__shot img {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius); border: 1px solid var(--bg-card-2);
}
@media (min-width: 1100px) {
  .hero {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: center; column-gap: 48px;
  }
  .hero__shot { margin-top: 0; }
}

/* ---------- sections ---------- */
.section { padding-top: 72px; scroll-margin-top: 76px; }
.section:first-of-type { padding-top: 56px; }
.h2 { font-size: 30px; font-weight: 700; margin: 0 0 26px; letter-spacing: -0.01em; }
.h2--tight { margin-bottom: 6px; }
/* .h2--flush, .head and .count are gone with the per-group sections: a count
   chip beside every heading was a third uppercase register saying what the list
   under it already said, and the one on Screensavers had drifted (it claimed
   six sources; there are seven). */
.section__sub { font-size: 17px; color: var(--ink-dim); margin: 0 0 26px; max-width: var(--measure); }
.section__lede { font-size: 17px; line-height: 1.5; color: var(--ink-dim); margin: 0 0 26px; max-width: var(--measure); text-wrap: pretty; }
/* A section's trailing quiet line (the relocated footer sentence): the old
   footer__p's own size and ink, so the sentence reads exactly as it always
   did, just standing where its subject lives. The stamp beside it is the
   sentence's own demonstration: the amber "as of" treatment a stale card
   wears, in miniature. */
.section__aside { font-size: 15.5px; color: var(--ink-dim); margin: 22px 0 0; max-width: var(--measure); }
.aside__stamp { font-size: 14px; color: var(--warn); opacity: 0.75; margin-left: 6px; white-space: nowrap; }

/* ---------- tile grid (display modes, screensaver sources) ----------
   A grid only. Both grids hold .card, so the page has exactly one card
   surface: the tinted 12px .tile that display modes used to get is gone,
   because two different card treatments inside one section read as two
   unrelated things. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--gap); }
.tiles--cards { margin-bottom: 14px; }

/* ---------- cards ---------- */
.card {
  background: var(--bg-card); border-radius: var(--radius); padding: 24px 26px;
  display: flex; flex-direction: column; overflow: hidden;
}
.card--pad { padding: 18px 20px; }
.card--row { padding: 22px 24px; flex-direction: row; gap: 18px; align-items: flex-start; }
.card__stack { display: flex; flex-direction: column; gap: 8px; }
.card__h { font-size: 18px; font-weight: 600; margin: 0 0 8px; }
.card--row .card__h, .card--pad .card__h { margin: 0 0 8px; }
.card--row .card__h { margin: 0; }
.card__p { font-size: 16px; line-height: 1.5; color: var(--ink-mid); margin: 0; }
.card__note { font-size: 15px; color: var(--ink-dim); margin-top: auto; padding-top: 14px; }
/* .card__fine retired 2026-07-29 with its only user, the "Tap anywhere to
   close" fine print under Tap a card. Left as a note rather than silently
   dropped: this file has grown orphaned declarations before. */
.duo { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: var(--gap); }
/* "Making it yours" grew a third control — the cards themselves — so the pair
   becomes a trio. 300px is the floor at which a 56px FAB plus its copy still
   reads; below it the row-cards stack, as they already did at 560px. */
.duo--trio { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

/* ---------- getting it on a board ----------
   Three doors in the tile-card grammar Display Modes and Screensavers already
   use. Deliberately NO icon badge: the .fab circles below mean "this is one of
   the board's three buttons", and borrowing them for device names would spend
   a meaningful component on decoration. A door's own name is the whole label
   it needs. */
.doors { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
/* The one string that tells a door apart from the other two, pinned to the
   card's foot by the same margin-top:auto .card__note uses — so three cards of
   unequal prose still line their values up on one baseline. The full set of
   configurations lives once, in the .conf block below; this is an identifier,
   not a copy of it. */
.card__code {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--bg-card-2);
  font-size: 14px;
  line-height: 1.5;
}
.card__code code { background: none; padding: 0; color: var(--ink-dim); white-space: normal; word-break: break-word; }
/* The macro is the one door whose artifact is a FILE rather than a value, so
   its foot is a link and reads like one. */
.card__code a { color: var(--accent); }
.card__code a code { color: inherit; }

/* A configuration value, inline in prose. */
code {
  font-family: var(--mono);
  font-size: 0.9em;
  color: var(--ink);
  background: var(--bg-card-2);
  border-radius: 6px;
  padding: 1px 6px;
  white-space: nowrap;
}
/* The device's own lines, quoted verbatim and set as one well. */
.conf {
  font-family: var(--mono);
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-mid);
  background: var(--bg-card-2);
  border-radius: 12px;
  margin: 0;
  padding: 14px 18px;
}
/* A hanging indent rather than a horizontal scrollbar: a wrapped
   xConfiguration still reads as one setting on a phone, and nothing in a guide
   should have to be dragged sideways. It needs a block per line — text-indent
   on the <pre> only ever catches the first — which is why info.html writes
   each line as its own span. */
.conf__l { display: block; padding-left: 1.6em; text-indent: -1.6em; white-space: pre-wrap; }
/* An annotation on a line meant to be retyped, so it must not travel with a
   copy: user-select:none drops it from the selection, and a paste gets the
   configuration alone. --ink-dim rather than --ink-faint because this sits at
   14px, below the size where faint still clears AA. */
.conf__note { color: var(--ink-dim); user-select: none; -webkit-user-select: none; }
.conf__after { font-size: 15.5px; line-height: 1.5; color: var(--ink-dim); margin: 14px 0 0; max-width: var(--measure); }

/* the two round board buttons, rendered calm like they are on the device */
.fab {
  width: 56px; height: 56px; border-radius: 999px; background: var(--bg-card-2);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; color: var(--ink);
}

/* ---------- the folded index ----------
   One disclosure per product group. What folds is the PROSE; the inventory
   never folds, so all 33 widget names are on screen at rest and the reader
   opens only the group they came for. No script: <summary> is natively
   focusable and keyboard-operable, and the print block at the foot of this
   file forces every group open. */
.folds { display: flex; flex-direction: column; margin-top: 8px; }
/* scroll-margin because each fold is a link target in its own right: the ids
   are the ones the old per-group sections carried, so /info#commute still
   lands, and the sticky nav would otherwise cover it. */
.fold { border-top: 1px solid var(--bg-card-2); scroll-margin-top: 76px; position: relative; }
/* Which rows belong to the open group? The spine answers: a rule that grows
   down the left gutter, header through last row (Sean's pick, B of three
   mockups, 2026-08-13). It floats OUTSIDE the text column so nothing indents
   when a group opens, and it is deliberately brighter than the row hairlines:
   figure, not furniture. Its growth keeps time with the unfold in info.js. */
.fold::before {
  content: ""; position: absolute; left: -20px; top: 14px; bottom: 14px;
  width: 1px; background: rgba(255, 255, 255, 0.38);
  transform: scaleY(0); transform-origin: top;
  transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
}
.fold[open]::before { transform: scaleY(1); }
/* No closing rule: the "On a board" subhead below carries its own border-top,
   and two hairlines 40px apart read as a mistake. */
/* 150px + a 20px gutter puts the dek on the same optical left edge as the
   .row__desc inside the group, so opening one does not shift the reading
   column. Every child is placed explicitly: the caret spans both rows, and
   auto-placement around a row-spanning item is not worth guessing at. */
.fold__sum {
  list-style: none; cursor: pointer;
  display: grid; grid-template-columns: 150px minmax(0, 1fr) 1.1em;
  gap: 6px 20px; align-items: baseline;
  padding: 16px 2px;
}
.fold__sum::-webkit-details-marker { display: none; }
.fold__sum:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.fold__name { grid-column: 1; grid-row: 1; font-size: 15px; font-weight: 600; margin: 0; text-transform: uppercase; letter-spacing: 0.09em; color: var(--ink); }
.fold__dek { grid-column: 2; grid-row: 1; font-size: 16px; line-height: 1.45; color: var(--ink-mid); }
/* The inventory line: quiet, but present at rest — this is the part a scanner
   is actually looking for. It goes away once the group is open, because every
   name is then a row heading two lines below. */
.fold__ids { grid-column: 2; grid-row: 2; font-size: 15px; line-height: 1.5; color: var(--ink-dim); }
.fold[open] .fold__ids { display: none; }
/* Calm at rest, ignites only on press — the page's own control idiom. */
.fold__caret { grid-column: 3; grid-row: 1 / span 2; justify-self: end; align-self: center; color: var(--ink-dim); transition: color 150ms; }
.fold__sum:hover .fold__caret { color: var(--ink); }
.fold__sum:active .fold__caret { color: var(--accent); }
.fold__caret::after {
  content: ""; display: block; width: 0.55em; height: 0.55em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-0.12em) rotate(45deg);
  transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.fold[open] .fold__caret::after { transform: translateY(0.12em) rotate(-135deg); }
.fold > .list { padding-bottom: 14px; }

/* ---------- widget rows ---------- */
.list { display: grid; grid-template-columns: repeat(auto-fit, minmax(470px, 1fr)); column-gap: 56px; }
/* An odd number of widgets in a two-column list leaves the last cell empty, and
   with it a visibly missing hairline at the foot of the right column. A grid
   ::after lands in exactly that cell, and only draws its rule when the count is
   odd, so an even group gains a zero-height row that draws nothing. */
.list:has(.row:nth-child(odd):last-child)::after { content: ""; border-top: 1px solid var(--bg-card-2); }
.row { display: flex; flex-wrap: wrap; gap: 4px 20px; padding: 15px 2px; border-top: 1px solid var(--bg-card-2); }
.row__name { font-size: 17px; font-weight: 600; margin: 0; flex: 0 0 150px; }
.row__desc { font-size: 16px; line-height: 1.5; color: var(--ink-mid); margin: 0; flex: 1 1 260px; }
.row__note { color: var(--ink-dim); display: block; margin-top: 4px; }

/* Settings hint marker. The app's own cog (icons.js) drawn as a mask in
   currentColor — NOT the "⚙" character, which platforms render as a colour
   emoji that clashes with the type. */
.row__note::before,
.card__note::before,
.notes__p::before {
  content: "";
  display: inline-block;
  width: 1.05em; height: 1.05em;
  margin-right: 0.45em;
  vertical-align: -0.17em;
  background-color: currentColor;
  -webkit-mask: var(--cog) center / contain no-repeat;
  mask: var(--cog) center / contain no-repeat;
}
:root {
  --cog: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3Cpath d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z'/%3E%3C/svg%3E");
}

/* ---------- board screenshots ---------- */
.shot { margin: 34px 0 0; border-radius: var(--radius); border: 1px solid var(--bg-card-2); overflow: hidden; }
.shot img { display: block; width: 100%; height: auto; cursor: zoom-in; }
/* Each screenshot is a real button, so the lightbox opens from the keyboard
   (Enter or Space) as well as a tap. Stripped to nothing visually; the focus
   ring is the only thing it draws, and it sits inside the figure's clip so it
   follows the rounded corner. */
.shot__btn {
  display: block; width: 100%; padding: 0; margin: 0; border: 0;
  background: none; color: inherit; font: inherit; cursor: zoom-in;
}
.shot__btn:focus { outline: none; }
.shot__btn:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; border-radius: var(--radius); }
.shots { display: grid; grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); gap: var(--gap); margin-top: var(--gap); }
.shot--half { margin: 0; position: relative; }
.shot__cap {
  position: absolute; top: 14px; left: 14px; background: var(--bg-card-2);
  border-radius: 10px; padding: 5px 11px; font-size: 14px; font-weight: 600; color: var(--ink);
}

/* ---------- the contact sheet ----------
   The four board captures that used to interrupt four sections, gathered into
   one block a reader passes once: 1,332px instead of 2,664px, and on a phone
   one block to scroll past instead of four ambushes.
   A contact sheet labels its plates underneath. The .shots chip overlay works
   on the two screensaver captures (dark, empty top-left corner); on a dashboard
   capture it lands squarely on the greeting and reads louder than the board it
   is naming, which is the wrong way round. */
.gal { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 440px), 1fr)); gap: 22px var(--gap); margin-top: 26px; }
.gal .shot--half { margin: 0; position: static; border: 0; border-radius: 0; overflow: visible; }
.gal .shot--half img { border: 1px solid var(--bg-card-2); border-radius: 14px; }
.gal .shot__cap {
  position: static; display: block; background: none; padding: 9px 2px 0;
  font-size: 15px; font-weight: 600; color: var(--ink-dim);
}

/* ---------- subheads ---------- */
/* scroll-margin because a subhead can be a link target in its own right:
   /info#modes lands on one, and the sticky nav would otherwise cover it. */
.subhead { display: flex; align-items: baseline; gap: 14px; padding-top: 22px; border-top: 1px solid var(--bg-card-2); margin-bottom: 20px; flex-wrap: wrap; scroll-margin-top: 76px; }
/* A subhead that follows a card grid or a list has to bring its own breathing
   room; the hairline alone lands too close to what it is separating. */
.subhead--gap { margin-top: 40px; }
.subhead__title { font-size: 20px; font-weight: 600; margin: 0; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-dim); }
.subhead__note { font-size: 15px; color: var(--ink-dim); }
/* Two short notes stacked left in a full-width tinted well left the right half
   of the well empty. Capping the prose is right; stacking it was not. They sit
   side by side and each column lands inside the measure on its own. */
.notes {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 34ch), 1fr));
  gap: 10px 28px; margin-top: var(--gap);
  background: var(--bg-card-2); border-radius: 12px; padding: 16px 18px;
}
.notes__p { font-size: 15px; line-height: 1.45; color: var(--ink-dim); margin: 0; max-width: var(--measure); }

/* ---------- terms ----------
   The same row rhythm the widget list and the changelog keep: a quiet label
   column, prose on the right. A terms page that invents its own type and
   spacing reads as some other product's terms; this one is the guide in a
   plainer mood. Only the container is new, and .row does the rest. */
.section--terms { padding-bottom: 8px; }
/* The container stops where the prose does. A hairline that runs 600px past
   the last word of every clause reads as an unfinished table; the label
   column (150px) plus its gutter plus the measure is the real width of the
   thing being ruled. */
.terms { display: flex; flex-direction: column; margin-top: 26px; max-width: calc(170px + var(--measure)); }
.terms .row { padding: 18px 2px; }
/* The widget list gets its measure from being two columns; a single-column
   clause list has to bring its own, or the prose runs to ~100 characters and
   the eye starts hunting for the next line. */
.terms .row__desc { max-width: var(--measure); }
/* A clause's trailing caveat: quieter than the clause, still above the
   small-text contrast floor (--ink-faint fails AA below 18px; --ink-dim
   does not). No cog marker — .row__note carries one, and nothing here
   happens behind the gear. */
.row__fine { display: block; margin-top: 10px; font-size: 15px; line-height: 1.45; color: var(--ink-dim); }
/* When the page last changed, which a terms page has to say. */
.section__stamp { font-size: 15px; color: var(--ink-dim); margin: 0 0 4px; }
.footer__link { font-size: 15px; color: var(--ink-dim); }
.footer__link:hover { color: var(--ink); }

/* ---------- what's new (built by info.js) ----------
   Deliberately the same geometry as the widget rows above: hairline per group,
   a quiet label column, prose on the right. A changelog is a list, not a set of
   cards, and reusing the row rhythm keeps it from becoming its own little
   design. */
.log { display: flex; flex-direction: column; }
.log__group { display: flex; flex-wrap: wrap; gap: 4px 20px; padding: 15px 2px; border-top: 1px solid var(--bg-card-2); }
.log__date { font-size: 16px; font-weight: 600; color: var(--ink-dim); margin: 0; flex: 0 0 150px; }
/* The items flow into as many measure-sized columns as the row can hold rather
   than running a single 68ch strip down the left of a 1,184px row. CSS COLUMNS,
   not grid (Sean, 2026-08-02): the grid's shared row heights parked a short
   entry beside a long one and left its whole cell-mate's height as dead space.
   Columns give every entry its own height and fill down-then-over, which is
   also the newest-first reading order the page promises. One column is what a
   narrow row gets. */
.log__items {
  columns: 44ch; column-gap: 28px; flex: 1 1 260px; min-width: 0;
}
.log__item { font-size: 16px; line-height: 1.5; color: var(--ink-mid); margin: 0 0 10px; text-wrap: pretty; max-width: var(--measure); break-inside: avoid; }
.log__item:last-child { margin-bottom: 0; }
.log__lead { font-weight: 600; color: var(--ink); margin-right: 0.4em; }
/* The disclosure for older groups. Calm at rest — it reads as one more quiet
   row in the list, not a button competing with the copy — and only ignites on
   press. 20px line + 12px top and bottom = a 44px target. */
.log__toggle {
  font: inherit; font-size: 15px; line-height: 20px; text-align: left;
  padding: 12px 2px; margin: 0; width: 100%;
  background: none; border: 0; border-top: 1px solid var(--bg-card-2);
  color: var(--ink-dim); cursor: pointer;
  transition: color 150ms cubic-bezier(0.22, 1, 0.36, 1);
}
.log__toggle:hover { color: var(--ink); }
.log__toggle:active { color: var(--accent); }
/* A control has to look like one before it is touched, so the caret carries the
   affordance the calm colour deliberately does not. It points down while there
   is more below, and up once there is not. */
.log__toggle::after {
  content: ""; display: inline-block; width: 0.5em; height: 0.5em;
  margin-left: 0.6em; vertical-align: 0.18em;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
  transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.log__toggle[aria-expanded="true"]::after { transform: translateY(0.1em) rotate(-135deg); }
.log__more { display: flex; flex-direction: column; }
/* Shown when the notes cannot be fetched, and by <noscript> when there is no
   script at all. Quiet on purpose: it is an absence, not an error. */
.log__empty { font-size: 16px; line-height: 1.5; color: var(--ink-dim); margin: 0; padding: 15px 2px; border-top: 1px solid var(--bg-card-2); max-width: var(--measure); }

/* ---------- footer ----------
   The lockup alone signs the page (the brand sheet's own footer context).
   The behavior sentence AND its stale-stamp wink both moved up into Making
   it yours (Sean's calls, 2026-08-18): facts about the product belong with
   the section that explains it, and the wink only reads beside the sentence
   it demonstrates. */
.footer { margin-top: 96px; padding-top: 26px; padding-bottom: 72px; }
.footer__inner { border-top: 1px solid var(--bg-card-2); padding-top: 24px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.footer__brand { display: flex; align-items: center; gap: 10px; }
.footer__brand .umark { font-size: 17px; }

/* ---------- lightbox (built by info.js) ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 100; background: #000;
  display: flex; align-items: center; justify-content: center; padding: 32px; cursor: zoom-out;
}
.lightbox__img { max-width: 100%; max-height: 100%; width: auto; height: auto; border-radius: var(--radius); border: 1px solid var(--bg-card-2); }
.lightbox__cap {
  position: absolute; top: 24px; left: 28px; font-size: 14px; font-weight: 600;
  color: var(--ink); background: var(--bg-card-2); border-radius: 10px; padding: 5px 11px;
}
.lightbox__hint { position: absolute; bottom: 24px; font-size: 15px; color: var(--ink-dim); }

/* ---------- narrow screens ---------- */
@media (max-width: 900px) {
  :root { --pad: 20px; }
  .hero { padding-top: 56px; }
  /* One knob: the cell. Every card is sized and placed in cell units, so the
     grid, the cards, the corner radius and the padding all shrink together
     and nothing can fall off the ruling. Same step the retired field used. */
  .hero__screen { --cell: 24px; }
  .section { padding-top: 56px; }
  .h2 { font-size: 26px; }
  /* One column: the two-column ::after balancer has no empty cell to fill. */
  .list { grid-template-columns: 1fr; column-gap: 0; }
  .list::after { display: none; }
  /* The fold's three lines stack, and the caret spans all three. */
  .fold__sum { grid-template-columns: minmax(0, 1fr) 1.1em; }
  /* --pad is 20px here; at -20px the spine would kiss the viewport edge. */
  .fold::before { left: -12px; }
  .fold__name { grid-column: 1; grid-row: 1; }
  .fold__dek { grid-column: 1; grid-row: 2; }
  .fold__ids { grid-column: 1; grid-row: 3; }
  .fold__caret { grid-column: 2; grid-row: 1 / span 3; }
  /* A phone renders a 1920px board at 0.18 scale whatever column it sits in,
     so a full-width one buys nothing but scroll. Two up, the four read as one
     contact sheet for half the height, and the lightbox is still one tap. */
  .gal { grid-template-columns: 1fr 1fr; gap: 16px 14px; }
  .gal .shot--half img { border-radius: 12px; }
  .gal .shot__cap { padding-top: 7px; font-size: 13px; font-weight: 500; }
  .conf { font-size: 13px; }
  .terms .row__name { flex: 1 1 100%; }
  .row__name { flex: 1 1 100%; }
  .row__desc { flex: 1 1 100%; }
  .log__date { flex: 1 1 100%; }
  .log__items { flex: 1 1 100%; }
  .footer { margin-top: 64px; }
}
@media (max-width: 560px) {
  /* Horizontal padding gives up half a cell for width; the vertical TWO
     CELLS are load-bearing (the card rows live inside them) and do not
     shrink. A third cell size would make the ruling read as three different
     products in one scroll, so the grid itself stays at the 24px step. */
  .hero__screen { padding: calc(var(--cell) * 2) var(--cell); }
  .hero__card--c, .hero__card--d { height: 28px; }
  .duo { grid-template-columns: 1fr; }
  .card--row { flex-direction: column; gap: 14px; }
  .shots { grid-template-columns: 1fr; }
  .lightbox { padding: 56px 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .nav__link, .nav__pill, .log__toggle, .log__toggle::after, .fold__caret, .fold__caret::after, .fold::before { transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- print ----------
   A document that calls itself a guide has to survive being printed. Same page,
   ink on paper: the screen chrome (sticky nav, overlay, the disclosure the
   reader cannot press) goes away, the palette flips, and nothing that reads as
   one unit is allowed to break across a sheet. */
@media print {
  :root {
    --bg: #fff; --bg-card: #fff; --bg-card-2: #d8d8d8;
    --ink: #000; --ink-mid: #1a1a1a; --ink-dim: #3a3a3a; --ink-faint: #555;
    --accent: #00429d;
  }
  /* info.html carries an inline background on <html> so the page never flashes
     white before this stylesheet lands. The canvas background propagates from the
     ROOT, so without this the black would print as a frame around every sheet.
     !important is load-bearing: an author !important outranks an inline attribute. */
  html { background: #fff !important; }
  body { background: #fff; color: #000; }
  .nav, .lightbox, .log__toggle { display: none !important; }
  /* Paper has no "show more": print the history the toggle was hiding. */
  .log__more[hidden] { display: flex !important; }
  /* Nor a disclosure. A closed <details> hides its content on the
     ::details-content pseudo, NOT on the .list inside it, so overriding the
     child does nothing at all — that mistake prints eight headings and no
     guide. Open the pseudo itself.
     info.js also sets `open` on every fold from beforeprint, which is what
     actually fires on Cmd+P and covers browsers without ::details-content;
     this rule is what Chrome's print PREVIEW and headless print-to-PDF get,
     since neither dispatches that event. Belt and braces on purpose: a guide
     has to survive being printed.
     The inventory line is dropped in exactly the two cases where the rows
     below it replace it: @supports covers the CSS path (the fold has no `open`
     attribute, so the screen rule cannot see it), and the screen rule itself
     covers the script path. A browser with neither still prints every widget
     name rather than a blank page, which is the point of doing both. */
  @supports selector(::details-content) {
    .fold::details-content { content-visibility: visible !important; }
    .fold__ids { display: none !important; }
  }
  .fold__caret { display: none !important; }
  .wrap { max-width: none; padding-left: 0; padding-right: 0; }
  a { color: #000; text-decoration: underline; }
  /* The screen panel is a dark slab and would print as one. On paper the
     masthead is just the name: panel chrome off, cards gone, the word set
     left where a printed heading belongs. The slash keeps the accent, which
     the palette flip above prints as a readable navy; "sleep" needs its own
     ink, because its 50%-WHITE is invisible on paper, and a mid-gray holds
     the same two-tone reading the screen has. */
  .hero__screen {
    background: none; border: 1px solid #c8c8c8; padding: 18px 22px;
    display: block; min-height: 0;
  }
  .hero__card { display: none; }
  .hero__title, .nav__brand { color: #000; }
  .umark__sleep { color: #5a5a5a; }
  .hero { padding-top: 0; display: block; }
  .hero__shot { margin-top: 24px; }
  .section { padding-top: 28px; }
  .card, .notes, .fab, .conf { border: 1px solid #c8c8c8; }
  .card, .notes { background: #fff; }
  .shot, .hero__shot, .card, .notes, .conf, .row, .log__group, .subhead, .fold { break-inside: avoid; }
  .h2, .subhead__title, .fold__name { break-after: avoid; }
  .footer { margin-top: 36px; padding-bottom: 0; }
  .footer__stamp { color: var(--ink-dim); opacity: 1; }
}
